From 12bc8ae24d0dcded940ecb1c49ab2b3b9833b001 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Thu, 14 Nov 2024 14:58:58 +0100 Subject: [PATCH] [unity] Fixed on-demand loading of blend mode materials. See #1890. --- .../Asset Types/OnDemandTextureLoader.cs | 5 + spine-unity/Assets/Spine/package.json | 2 +- .../GenericOnDemandTextureLoaderInspector.cs | 36 +++++ .../Runtime/GenericOnDemandTextureLoader.cs | 135 ++++++++++++++---- .../package.json | 4 +- 5 files changed, 155 insertions(+), 27 deletions(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/OnDemandTextureLoader.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/OnDemandTextureLoader.cs index 8f18a0937..20d590ae1 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/OnDemandTextureLoader.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/OnDemandTextureLoader.cs @@ -36,6 +36,11 @@ using UnityEngine; namespace Spine.Unity { public abstract class OnDemandTextureLoader : ScriptableObject { public AtlasAssetBase atlasAsset; + /// + /// Additional reference, currently only used to cover blend mode materials + /// which are not stored at atlasAsset. + /// + public SkeletonDataAsset skeletonDataAsset; /// Original texture name without extension. /// The placeholder texture's name for a given original target texture name. diff --git a/spine-unity/Assets/Spine/package.json b/spine-unity/Assets/Spine/package.json index babde3265..3662eab62 100644 --- a/spine-unity/Assets/Spine/package.json +++ b/spine-unity/Assets/Spine/package.json @@ -2,7 +2,7 @@ "name": "com.esotericsoftware.spine.spine-unity", "displayName": "spine-unity Runtime", "description": "This plugin provides the spine-unity runtime core.", - "version": "4.2.88", + "version": "4.2.89", "unity": "2018.3", "author": { "name": "Esoteric Software", diff --git a/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Editor/GenericOnDemandTextureLoaderInspector.cs b/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Editor/GenericOnDemandTextureLoaderInspector.cs index 49bc1fe47..76aa4d8f8 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Editor/GenericOnDemandTextureLoaderInspector.cs +++ b/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Editor/GenericOnDemandTextureLoaderInspector.cs @@ -59,6 +59,7 @@ namespace Spine.Unity.Editor { where TextureRequest : Spine.Unity.IOnDemandRequest { protected SerializedProperty atlasAsset; + protected SerializedProperty skeletonDataAsset; protected SerializedProperty maxPlaceholderSize; protected SerializedProperty placeholderMap; protected SerializedProperty unloadAfterSecondsUnused; @@ -172,6 +173,39 @@ namespace Spine.Unity.Editor { // assign late since CreatePlaceholderTextureFor(texture) method above might save assets and clear these values. loader.placeholderMap = materialMap; loader.atlasAsset = atlasAsset; + if (loader.skeletonDataAsset == null) + AssignSkeletonDataAsset(loader, atlasAsset); + } + + protected void AssignSkeletonDataAsset(GenericOnDemandTextureLoader loader, AtlasAssetBase atlasAsset) { + string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); + string parentFolder = System.IO.Path.GetDirectoryName(atlasAssetPath); + + SkeletonDataAsset skeletonDataAsset = FindSkeletonDataAsset(parentFolder, atlasAsset); + if (skeletonDataAsset) { + loader.skeletonDataAsset = skeletonDataAsset; + return; + } + string nextParentFolder = System.IO.Path.GetDirectoryName(parentFolder); + skeletonDataAsset = FindSkeletonDataAsset(nextParentFolder, atlasAsset); + if (skeletonDataAsset) { + loader.skeletonDataAsset = skeletonDataAsset; + return; + } + } + + protected SkeletonDataAsset FindSkeletonDataAsset (string searchFolder, AtlasAssetBase atlasAsset) { + string[] guids = AssetDatabase.FindAssets("t:SkeletonDataAsset", new[] { searchFolder }); + foreach (string guid in guids) { + string assetPath = AssetDatabase.GUIDToAssetPath(guid); + SkeletonDataAsset skeletonDataAsset = AssetDatabase.LoadAssetAtPath(assetPath); + if (skeletonDataAsset != null) { + if (skeletonDataAsset.atlasAssets.Contains(atlasAsset)) { + return skeletonDataAsset; + } + } + } + return null; } public virtual Texture CreatePlaceholderTextureFor (Texture originalTexture, int maxPlaceholderSize, @@ -244,6 +278,7 @@ namespace Spine.Unity.Editor { void OnEnable () { atlasAsset = serializedObject.FindProperty("atlasAsset"); + skeletonDataAsset = serializedObject.FindProperty("skeletonDataAsset"); maxPlaceholderSize = serializedObject.FindProperty("maxPlaceholderSize"); placeholderMap = serializedObject.FindProperty("placeholderMap"); unloadAfterSecondsUnused = serializedObject.FindProperty("unloadAfterSecondsUnused"); @@ -352,6 +387,7 @@ namespace Spine.Unity.Editor { serializedObject.Update(); EditorGUILayout.PropertyField(atlasAsset); + EditorGUILayout.PropertyField(skeletonDataAsset); EditorGUILayout.PropertyField(maxPlaceholderSize); EditorGUILayout.PropertyField(unloadAfterSecondsUnused); diff --git a/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Runtime/GenericOnDemandTextureLoader.cs b/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Runtime/GenericOnDemandTextureLoader.cs index c929b7dfd..9df02438c 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Runtime/GenericOnDemandTextureLoader.cs +++ b/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/Runtime/GenericOnDemandTextureLoader.cs @@ -33,10 +33,12 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using UnityEngine; namespace Spine.Unity { + using ReplacementMaterial = BlendModeMaterials.ReplacementMaterial; /// /// Interface to derive a concrete target reference struct from which holds @@ -114,9 +116,11 @@ namespace Spine.Unity { modifiedMaterials = null; if (!atlasAsset) return false; + int normalMaterialCount = atlasAsset.Materials.Count(); + IEnumerable inputMaterials = GetInputMaterials(); int materialIndex = 0; - foreach (Material targetMaterial in atlasAsset.Materials) { - if (materialIndex >= placeholderMap.Length) { + foreach (Material targetMaterial in inputMaterials) { + if ((materialIndex < normalMaterialCount) && (materialIndex >= placeholderMap.Length)) { Debug.LogError(string.Format("Failed to assign placeholder textures at {0}, material #{1} {2}. " + "It seems like the GenericOnDemandTextureLoader asset was not setup accordingly for the AtlasAsset.", atlasAsset, materialIndex + 1, targetMaterial), this); @@ -134,17 +138,19 @@ namespace Spine.Unity { mapIndex = foundMapIndex; } #endif - Texture placeholderTexture = placeholderMap[mapIndex].textures[textureIndex].placeholderTexture; - if (placeholderTexture == null) { - Debug.LogWarning(string.Format("Placeholder texture set to null at {0}, for material #{1} {2}. " + - "It seems like the GenericOnDemandTextureLoader asset was not setup accordingly for the AtlasAsset.", - atlasAsset, materialIndex + 1, targetMaterial), this); - } else { - targetMaterial.mainTexture = placeholderTexture; + if (mapIndex < normalMaterialCount) { + Texture placeholderTexture = placeholderMap[mapIndex].textures[textureIndex].placeholderTexture; + if (placeholderTexture == null) { + Debug.LogWarning(string.Format("Placeholder texture set to null at {0}, for material #{1} {2}. " + + "It seems like the GenericOnDemandTextureLoader asset was not setup accordingly for the AtlasAsset.", + atlasAsset, materialIndex + 1, targetMaterial), this); + } else { + targetMaterial.mainTexture = placeholderTexture; + } } ++materialIndex; } - modifiedMaterials = atlasAsset.Materials; + modifiedMaterials = inputMaterials; return true; } @@ -153,18 +159,14 @@ namespace Spine.Unity { if (!atlasAsset) return false; bool anyPlaceholderAssigned = false; - - int materialIndex = 0; - foreach (Material material in atlasAsset.Materials) { - if (materialIndex >= placeholderMap.Length) - return false; + IEnumerable inputMaterials = GetInputMaterials(); + foreach (Material material in inputMaterials) { bool hasPlaceholderAssigned = HasPlaceholderAssigned(material); if (hasPlaceholderAssigned) { anyPlaceholderAssigned = true; if (placeholderMaterials == null) placeholderMaterials = new List(); placeholderMaterials.Add(material); } - materialIndex++; } return anyPlaceholderAssigned; } @@ -173,22 +175,64 @@ namespace Spine.Unity { modifiedMaterials = null; if (!atlasAsset) return false; BeginCustomTextureLoading(); - int i = 0; - foreach (Material targetMaterial in atlasAsset.Materials) { - if (i >= placeholderMap.Length) { + + int normalMaterialCount = atlasAsset.Materials.Count(); + IEnumerable inputMaterials = GetInputMaterials(); + + // process normal materials + int materialIndex = 0; + foreach (Material targetMaterial in inputMaterials) { + if (materialIndex > normalMaterialCount - 1) break; + + if (materialIndex >= placeholderMap.Length) { Debug.LogError(string.Format("Failed to assign target textures at {0}, material #{1} {2}. " + "It seems like the OnDemandTextureLoader asset was not setup accordingly for the AtlasAsset.", - atlasAsset, i + 1, targetMaterial), this); + atlasAsset, materialIndex + 1, targetMaterial), this); return false; } - AssignTargetTextures(targetMaterial, i); - ++i; + + AssignTargetTextures(targetMaterial, materialIndex); + ++materialIndex; } - modifiedMaterials = atlasAsset.Materials; + // process blend mode materials + if (skeletonDataAsset != null) { + foreach (ReplacementMaterial replacement in skeletonDataAsset.blendModeMaterials.additiveMaterials) { + AssignBlendModeTargetTextures(replacement.material, replacement); + } + foreach (ReplacementMaterial replacement in skeletonDataAsset.blendModeMaterials.multiplyMaterials) { + AssignBlendModeTargetTextures(replacement.material, replacement); + } + foreach (ReplacementMaterial replacement in skeletonDataAsset.blendModeMaterials.screenMaterials) { + AssignBlendModeTargetTextures(replacement.material, replacement); + } + } + + modifiedMaterials = inputMaterials; EndCustomTextureLoading(); return true; } + protected IEnumerable GetInputMaterials () { + int normalMaterialCount = atlasAsset.Materials.Count(); + IEnumerable inputMaterials = atlasAsset.Materials; + if (skeletonDataAsset != null) { + BlendModeMaterials blendModeMaterials = skeletonDataAsset.blendModeMaterials; + int additiveCount = blendModeMaterials.additiveMaterials.Count; + int multiplyCount = blendModeMaterials.multiplyMaterials.Count; + int screenCount = blendModeMaterials.screenMaterials.Count; + int totalBlendModeMaterialCount = additiveCount + multiplyCount + screenCount; + if (totalBlendModeMaterialCount > 0) { + List materialsList = new List(normalMaterialCount + totalBlendModeMaterialCount); + materialsList.AddRange(atlasAsset.Materials); + materialsList.AddRange(blendModeMaterials.additiveMaterials.Where(r => r.material != null).Select(r => r.material)); + materialsList.AddRange(blendModeMaterials.multiplyMaterials.Where(r => r.material != null).Select(r => r.material)); + materialsList.AddRange(blendModeMaterials.screenMaterials.Where(r => r.material != null).Select(r => r.material)); + inputMaterials = materialsList; + } + } + return inputMaterials; + } + public override void BeginCustomTextureLoading () { if (loadedDataAtMaterial == null || (loadedDataAtMaterial.Length == 0 && placeholderMap.Length > 0)) { loadedDataAtMaterial = new MaterialOnDemandData[placeholderMap.Length]; @@ -262,9 +306,29 @@ namespace Spine.Unity { protected void AssignTargetTextures (Material material, int materialIndex) { int textureIndex = 0; // Todo: currently only main texture is supported. + if (materialIndex > placeholderMap.Length - 1) return; RequestLoadTexture(material, materialIndex, textureIndex, null); } + protected void AssignBlendModeTargetTextures (Material blendModeMaterial, ReplacementMaterial replacementMaterial) { + int textureIndex = 0; // Todo: currently only main texture is supported. + int mainMaterialIndex = 0; + if (blendModeMaterial.mainTexture != null) { + mainMaterialIndex = Array.FindIndex(placeholderMap, + entry => entry.textures[textureIndex].placeholderTexture == blendModeMaterial.mainTexture); + if (mainMaterialIndex < 0) + return; + } else { + string textureNameFull = Path.GetFileNameWithoutExtension(replacementMaterial.pageName); + string placeholderTextureName = GetPlaceholderTextureName(textureNameFull); + mainMaterialIndex = Array.FindIndex(placeholderMap, + entry => entry.textures[textureIndex].placeholderTexture.name == placeholderTextureName); + if (mainMaterialIndex < 0) + return; + } + RequestLoadTexture(blendModeMaterial, mainMaterialIndex, textureIndex, null); + } + protected virtual Texture RequestLoadTexture (Material material, int materialIndex, int textureIndex, System.Action onTextureLoaded) { @@ -336,11 +400,34 @@ namespace Spine.Unity { // reset material textures to placeholder textures. Material targetMaterial = atlasAsset.Materials.ElementAt(materialIndex); + Texture targetTexture = null; + Texture placeholderTexture = null; if (targetMaterial) { - targetMaterial.mainTexture = placeholderTextures[textureIndex].placeholderTexture; + targetTexture = targetMaterial.mainTexture; + placeholderTexture = placeholderTextures[textureIndex].placeholderTexture; + targetMaterial.mainTexture = placeholderTexture; if (wasReleased) OnTextureUnloaded(targetMaterial, textureIndex); } + // also reset material textures of blend mode materials + if (targetTexture != null && skeletonDataAsset != null) { + BlendModeMaterials blendModeMaterials = skeletonDataAsset.blendModeMaterials; + foreach (ReplacementMaterial replacementMaterial in blendModeMaterials.additiveMaterials) { + replacementMaterial.material.mainTexture = placeholderTexture; + if (wasReleased && replacementMaterial.material.mainTexture == targetTexture) + OnTextureUnloaded(targetMaterial, textureIndex); + } + foreach (ReplacementMaterial replacementMaterial in blendModeMaterials.multiplyMaterials) { + replacementMaterial.material.mainTexture = placeholderTexture; + if (wasReleased && replacementMaterial.material.mainTexture == targetTexture) + OnTextureUnloaded(targetMaterial, textureIndex); + } + foreach (ReplacementMaterial replacementMaterial in blendModeMaterials.screenMaterials) { + replacementMaterial.material.mainTexture = placeholderTexture; + if (wasReleased && replacementMaterial.material.mainTexture == targetTexture) + OnTextureUnloaded(targetMaterial, textureIndex); + } + } } public int maxPlaceholderSize = 128; diff --git a/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/package.json b/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/package.json index 7f0261ae4..857660b72 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/package.json +++ b/spine-unity/Modules/com.esotericsoftware.spine.on-demand-loading/package.json @@ -2,7 +2,7 @@ "name": "com.esotericsoftware.spine.on-demand-loading", "displayName": "Spine On-Demand Loading Extensions [Experimental]", "description": "This experimental plugin provides a generic basic implementation of on-demand texture loading for the spine-unity runtime. You might want to use the available com.esotericsoftware.spine.addressables package which depends on this package.\nPlease be sure to test this package first and create backups of your project before using.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime (via the spine-unity unitypackage), version 4.2.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)", - "version": "4.2.0-preview.4", + "version": "4.2.0-preview.5", "unity": "2018.3", "author": { "name": "Esoteric Software", @@ -10,7 +10,7 @@ "url": "http://esotericsoftware.com/" }, "dependencies": { - "com.esotericsoftware.spine.spine-unity": "4.2.21" + "com.esotericsoftware.spine.spine-unity": "4.2.89" }, "keywords": [ "spine",