[unity] Added DestroyGeneratedAssets method to RepackAttachmentsOutput struct for ease of use. Updated examples to use new idiom. See #1945.

This commit is contained in:
Harald Csaszar 2026-02-13 15:56:14 +01:00
parent 64b0031db0
commit f1b4d9b770
6 changed files with 120 additions and 47 deletions

View File

@ -370,7 +370,8 @@
- Spine UI Toolkit UPM package now supports PMA atlas textures. At the `SpineVisualElement` expand `Blend Mode Materials` and hit `Detect Materials` to automatically assign the proper PMA or straight alpha material at `Normal Material`. Unity minimum version increased to 6000.3 which added support for UI Toolkit materials. - Spine UI Toolkit UPM package now supports PMA atlas textures. At the `SpineVisualElement` expand `Blend Mode Materials` and hit `Detect Materials` to automatically assign the proper PMA or straight alpha material at `Normal Material`. Unity minimum version increased to 6000.3 which added support for UI Toolkit materials.
- Spine UI Toolkit UPM package now supports all Spine blend modes via blend mode materials and multiple materials per skeleton. Enable `Multiple Materials` (enabled by default), expand `Blend Mode Materials` and hit `Detect Materials` to automatically assign the correct PMA or straight alpha blend mode materials. - Spine UI Toolkit UPM package now supports all Spine blend modes via blend mode materials and multiple materials per skeleton. Enable `Multiple Materials` (enabled by default), expand `Blend Mode Materials` and hit `Detect Materials` to automatically assign the correct PMA or straight alpha blend mode materials.
- Every Spine URP shader now has an `Outline` option to switch to the respective Outline shader variant. Uses multi-pass support of newer URP versions. Requires spine-unity core package version 4.3.44 or newer due to required modifications in custom Sprite Shader GUI. - Every Spine URP shader now has an `Outline` option to switch to the respective Outline shader variant. Uses multi-pass support of newer URP versions. Requires spine-unity core package version 4.3.44 or newer due to required modifications in custom Sprite Shader GUI.
- Added new variants of `GetRepackedSkin` and `GetRepackedAttachments` supporting blend modes. These new variants take a packing configuration input struct `RepackAttachmentsSettings` which provides optional `additiveMaterialSource`, `multiplyMaterialSource` and `screenMaterialSource` properties, enabling blend mode repacking when any is non-null. Create your `RepackAttachmentsSettings` from default settings via `RepackAttachmentsSettings.Default` and then customize settings as needed. - Added new variants of `GetRepackedSkin` and `GetRepackedAttachments` supporting blend modes. These new variants take a packing configuration input struct `RepackAttachmentsSettings` which provides optional `additiveMaterialSource`, `multiplyMaterialSource` and `screenMaterialSource` properties, enabling blend mode repacking when any is non-null. Create your `RepackAttachmentsSettings` from default settings via `RepackAttachmentsSettings.Default` and then customize settings as needed. Uses new `RepackAttachmentsOutput` struct providing `DestroyGeneratedAssets` to easily destroy any previously generated assets.
- Updated example scenes to demonstrate new `GetRepackedSkin` variant usage.
- **Deprecated** - **Deprecated**

View File

@ -31,12 +31,10 @@
#define CONFIGURABLE_ENTER_PLAY_MODE #define CONFIGURABLE_ENTER_PLAY_MODE
#endif #endif
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
namespace Spine.Unity.AttachmentTools { namespace Spine.Unity.AttachmentTools {
public static class AtlasUtilities { public static class AtlasUtilities {
@ -266,6 +264,27 @@ namespace Spine.Unity.AttachmentTools {
/// as main texture. /// as main texture.
/// Materials and textures returned behave like <c>new Texture2D()</c> and need to be destroyed.</summary> /// Materials and textures returned behave like <c>new Texture2D()</c> and need to be destroyed.</summary>
public Material outputScreenMaterial; public Material outputScreenMaterial;
/// <summary>
/// Destroys any assigned previously generated assets. If you decide to store
/// <see cref="RepackAttachmentsOutput"/> in a MonoBehaviour for re-use, call this method before each
/// <see cref="GetRepackedSkin"/> or <see cref="GetRepackedAttachments"/> call, and also once in OnDestroy.
/// </summary>
public void DestroyGeneratedAssets () {
if (outputMaterial) { UnityEngine.Object.Destroy(outputMaterial); outputMaterial = null; }
if (outputTexture) { UnityEngine.Object.Destroy(outputTexture); outputTexture = null; }
if (additionalOutputTextures != null) {
for (int i = 0; i < additionalOutputTextures.Length; ++i) {
if (additionalOutputTextures[i]) {
UnityEngine.Object.Destroy(additionalOutputTextures[i]);
additionalOutputTextures[i] = null;
}
}
}
if (outputAdditiveMaterial) { UnityEngine.Object.Destroy(outputAdditiveMaterial); outputAdditiveMaterial = null; }
if (outputMultiplyMaterial) { UnityEngine.Object.Destroy(outputMultiplyMaterial); outputMultiplyMaterial = null; }
if (outputScreenMaterial) { UnityEngine.Object.Destroy(outputScreenMaterial); outputScreenMaterial = null; }
}
} }
/// <summary> /// <summary>
@ -351,25 +370,63 @@ namespace Spine.Unity.AttachmentTools {
public bool[] additionalTextureIsLinear; public bool[] additionalTextureIsLinear;
/// <summary>Default settings providing reasonable parameters, modify according to your needs.</summary> /// <summary>Default settings providing reasonable parameters, modify according to your needs.</summary>
public static RepackAttachmentsSettings Default = new RepackAttachmentsSettings { public static RepackAttachmentsSettings Default = new RepackAttachmentsSettings(true);
materialPropertySource = null,
additiveMaterialSource = null,
multiplyMaterialSource = null,
screenMaterialSource = null,
newAssetName = "Repacked Attachments", /// <summary>Hidden pseudo-default ctor, use <see cref="Default"/> instead.</summary>
private RepackAttachmentsSettings (bool _) {
newAssetName = DefaultTextureName;
maxAtlasSize = 1024, maxAtlasSize = 1024;
padding = 2, padding = 2;
textureFormat = SpineTextureFormat, textureFormat = SpineTextureFormat;
mipmaps = UseMipMaps, mipmaps = UseMipMaps;
clearCache = false, clearCache = false;
useOriginalNonrenderables = true;
useOriginalNonrenderables = true, shader = null;
additionalTexturePropertyIDsToCopy = null, materialPropertySource = null;
additionalTextureFormats = null, additiveMaterialSource = null;
additionalTextureIsLinear = null multiplyMaterialSource = null;
}; screenMaterialSource = null;
additionalTexturePropertyIDsToCopy = null;
additionalTextureFormats = null;
additionalTextureIsLinear = null;
}
/// <summary>
/// Default settings providing reasonable parameters, with source materials assigned according to the
/// provided <paramref name="skeletonDataAsset"/>. Modify according to your needs.
/// </summary>
/// <param name="skeletonDataAsset">Reference <see cref="SkeletonDataAsset"/> used to provide source
/// materials for all blend modes.</param>
public RepackAttachmentsSettings (SkeletonDataAsset skeletonDataAsset)
: this(true) {
UseSourceMaterialsFrom(skeletonDataAsset);
}
/// <summary>
/// Assigns source materials from the provided <paramref name="skeletonDataAsset"/> for all blend modes
/// including normal blend mode.
/// </summary>
public void UseSourceMaterialsFrom (SkeletonDataAsset skeletonDataAsset) {
materialPropertySource = skeletonDataAsset.atlasAssets[0].PrimaryMaterial;
UseBlendModeMaterialsFrom(skeletonDataAsset);
}
/// <summary>
/// Assigns source materials from the provided <paramref name="skeletonDataAsset"/> for
/// additive, multiply and screen blend modes.
/// </summary>
public void UseBlendModeMaterialsFrom (SkeletonDataAsset skeletonDataAsset) {
BlendModeMaterials materials = skeletonDataAsset.blendModeMaterials;
if (materials.additiveMaterials.Count > 0)
additiveMaterialSource = materials.additiveMaterials[0].material;
if (materials.multiplyMaterials.Count > 0)
multiplyMaterialSource = materials.multiplyMaterials[0].material;
if (materials.screenMaterials.Count > 0)
screenMaterialSource = materials.screenMaterials[0].material;
}
} }
private struct BlendModeAtlasPages { private struct BlendModeAtlasPages {

View File

@ -43,8 +43,7 @@ namespace Spine.Unity.Examples {
Spine.Skin equipsSkin; Spine.Skin equipsSkin;
Spine.Skin collectedSkin; Spine.Skin collectedSkin;
public Material runtimeMaterial; AtlasUtilities.RepackAttachmentsOutput repackingOutput;
public Texture2D runtimeAtlas;
void Start () { void Start () {
equipsSkin = new Skin("Equips"); equipsSkin = new Skin("Equips");
@ -58,6 +57,11 @@ namespace Spine.Unity.Examples {
RefreshSkeletonAttachments(); RefreshSkeletonAttachments();
} }
void OnDestroy () {
// Note: materials and textures returned by GetRepackedSkin() behave like 'new Texture2D()' and need to be destroyed
repackingOutput.DestroyGeneratedAssets();
}
public void Equip (int slotIndex, string attachmentName, Attachment attachment) { public void Equip (int slotIndex, string attachmentName, Attachment attachment) {
equipsSkin.SetAttachment(slotIndex, attachmentName, attachment); equipsSkin.SetAttachment(slotIndex, attachmentName, attachment);
skeletonAnimation.Skeleton.SetSkin(equipsSkin); skeletonAnimation.Skeleton.SetSkin(equipsSkin);
@ -72,13 +76,13 @@ namespace Spine.Unity.Examples {
collectedSkin.AddSkin(equipsSkin); collectedSkin.AddSkin(equipsSkin);
// 2. Create a repacked skin. // 2. Create a repacked skin.
// Note: materials and textures returned by GetRepackedSkin() behave like 'new Texture2D()' and need to be destroyed // Note: materials and textures returned by previous GetRepackedSkin() calls behave like 'new Texture2D()'
if (runtimeMaterial) // and need to be destroyed.
Destroy(runtimeMaterial); repackingOutput.DestroyGeneratedAssets();
if (runtimeAtlas) AtlasUtilities.RepackAttachmentsSettings settings = AtlasUtilities.RepackAttachmentsSettings.Default;
Destroy(runtimeAtlas); settings.UseSourceMaterialsFrom(skeletonAnimation.SkeletonDataAsset);
Skin repackedSkin = collectedSkin.GetRepackedSkin("Repacked skin", skeletonAnimation.SkeletonDataAsset.atlasAssets[0].PrimaryMaterial, settings.maxAtlasSize = 1024;
out runtimeMaterial, out runtimeAtlas, maxAtlasSize: 1024, clearCache: false); Skin repackedSkin = collectedSkin.GetRepackedSkin("repacked skin", settings, ref repackingOutput);
collectedSkin.Clear(); collectedSkin.Clear();
// You can optionally clear the textures cache after each ore multiple repack operations are done. // You can optionally clear the textures cache after each ore multiple repack operations are done.

View File

@ -65,9 +65,8 @@ namespace Spine.Unity.Examples {
Skin characterSkin; Skin characterSkin;
// for repacking the skin to a new atlas texture // for repacking the skin to a new atlas texture
public Material runtimeMaterial; AtlasUtilities.RepackAttachmentsOutput repackingOutput;
public Texture2D runtimeAtlas;
void Awake () { void Awake () {
skeletonAnimation = this.GetComponent<SkeletonAnimation>(); skeletonAnimation = this.GetComponent<SkeletonAnimation>();
} }
@ -77,6 +76,11 @@ namespace Spine.Unity.Examples {
UpdateCombinedSkin(); UpdateCombinedSkin();
} }
void OnDestroy () {
// Note: materials and textures returned by GetRepackedSkin() behave like 'new Texture2D()' and need to be destroyed
repackingOutput.DestroyGeneratedAssets();
}
public void NextHairSkin () { public void NextHairSkin () {
activeHairIndex = (activeHairIndex + 1) % hairSkins.Length; activeHairIndex = (activeHairIndex + 1) % hairSkins.Length;
UpdateCharacterSkin(); UpdateCharacterSkin();
@ -136,12 +140,14 @@ namespace Spine.Unity.Examples {
public void OptimizeSkin () { public void OptimizeSkin () {
// Create a repacked skin. // Create a repacked skin.
Skin previousSkin = skeletonAnimation.Skeleton.Skin; Skin previousSkin = skeletonAnimation.Skeleton.Skin;
// Note: materials and textures returned by GetRepackedSkin() behave like 'new Texture2D()' and need to be destroyed
if (runtimeMaterial) // Note: materials and textures returned by previous GetRepackedSkin() calls behave like 'new Texture2D()'
Destroy(runtimeMaterial); // and need to be destroyed.
if (runtimeAtlas) repackingOutput.DestroyGeneratedAssets();
Destroy(runtimeAtlas); AtlasUtilities.RepackAttachmentsSettings settings = AtlasUtilities.RepackAttachmentsSettings.Default;
Skin repackedSkin = previousSkin.GetRepackedSkin("Repacked skin", skeletonAnimation.SkeletonDataAsset.atlasAssets[0].PrimaryMaterial, out runtimeMaterial, out runtimeAtlas); settings.UseSourceMaterialsFrom(skeletonAnimation.SkeletonDataAsset);
settings.maxAtlasSize = 1024;
Skin repackedSkin = previousSkin.GetRepackedSkin("repacked skin", settings, ref repackingOutput);
previousSkin.Clear(); previousSkin.Clear();
// Use the repacked skin. // Use the repacked skin.

View File

@ -55,9 +55,7 @@ namespace Spine.Unity.Examples {
public bool repack = true; public bool repack = true;
public BoundingBoxFollower bbFollower; public BoundingBoxFollower bbFollower;
[Header("Do not assign")] AtlasUtilities.RepackAttachmentsOutput repackingOutput;
public Texture2D runtimeAtlas;
public Material runtimeMaterial;
#endregion #endregion
Skin customSkin; Skin customSkin;
@ -75,6 +73,11 @@ namespace Spine.Unity.Examples {
Apply(); Apply();
} }
void OnDestroy () {
// Note: materials and textures returned by GetRepackedSkin() behave like 'new Texture2D()' and need to be destroyed
repackingOutput.DestroyGeneratedAssets();
}
void Apply () { void Apply () {
SkeletonAnimation skeletonAnimation = GetComponent<SkeletonAnimation>(); SkeletonAnimation skeletonAnimation = GetComponent<SkeletonAnimation>();
Skeleton skeleton = skeletonAnimation.Skeleton; Skeleton skeleton = skeletonAnimation.Skeleton;
@ -124,12 +127,14 @@ namespace Spine.Unity.Examples {
repackedSkin.AddSkin(skeleton.Data.DefaultSkin); // Include the "default" skin. (everything outside of skin placeholders) repackedSkin.AddSkin(skeleton.Data.DefaultSkin); // Include the "default" skin. (everything outside of skin placeholders)
repackedSkin.AddSkin(customSkin); // Include your new custom skin. repackedSkin.AddSkin(customSkin); // Include your new custom skin.
// Note: materials and textures returned by GetRepackedSkin() behave like 'new Texture2D()' and need to be destroyed // Note: materials and textures returned by previous GetRepackedSkin() calls behave like 'new Texture2D()'
if (runtimeMaterial) // and need to be destroyed.
Destroy(runtimeMaterial); repackingOutput.DestroyGeneratedAssets();
if (runtimeAtlas) AtlasUtilities.RepackAttachmentsSettings settings = AtlasUtilities.RepackAttachmentsSettings.Default;
Destroy(runtimeAtlas); settings.UseSourceMaterialsFrom(skeletonAnimation.SkeletonDataAsset);
repackedSkin = repackedSkin.GetRepackedSkin("repacked skin", sourceMaterial, out runtimeMaterial, out runtimeAtlas); // Pack all the items in the skin. settings.maxAtlasSize = 1024;
repackedSkin = repackedSkin.GetRepackedSkin("repacked skin", settings, ref repackingOutput); // Pack all the items in the skin.
skeleton.SetSkin(repackedSkin); // Assign the repacked skin to your Skeleton. skeleton.SetSkin(repackedSkin); // Assign the repacked skin to your Skeleton.
if (bbFollower != null) bbFollower.Initialize(true); if (bbFollower != null) bbFollower.Initialize(true);
} else { } else {

View File

@ -2,7 +2,7 @@
"name": "com.esotericsoftware.spine.spine-unity", "name": "com.esotericsoftware.spine.spine-unity",
"displayName": "spine-unity Runtime", "displayName": "spine-unity Runtime",
"description": "This plugin provides the spine-unity runtime core and examples. Spine Examples can be installed via the Samples tab.", "description": "This plugin provides the spine-unity runtime core and examples. Spine Examples can be installed via the Samples tab.",
"version": "4.3.45", "version": "4.3.46",
"unity": "2018.3", "unity": "2018.3",
"author": { "author": {
"name": "Esoteric Software", "name": "Esoteric Software",