mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 06:29:02 +08:00
[unity] Added new GetRepackedSkin and GetRepackedAttachments variants which maintain blend modes. Closes #1945.
This commit is contained in:
parent
ab79c143c3
commit
5beb3c9352
@ -370,6 +370,7 @@
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
- **Deprecated**
|
||||
|
||||
|
||||
@ -236,9 +236,375 @@ namespace Spine.Unity.AttachmentTools {
|
||||
static List<Texture2D>[] texturesToPackAtParam = new List<Texture2D>[1];
|
||||
static List<Attachment> inoutAttachments = new List<Attachment>();
|
||||
|
||||
/// <summary>
|
||||
/// Repack output struct for <see cref="GetRepackedAttachments"/> and <see cref="GetRepackedSkin"/>.
|
||||
/// Returned <c>Material</c> and <c>Texture</c> behave like <c>new Texture2D()</c>, thus you need to
|
||||
/// call <c>Destroy()</c> to free resources.
|
||||
/// </summary>
|
||||
public struct RepackAttachmentsOutput {
|
||||
/// <summary>The newly generated Material for the normal blend mode, holding the repacked texture
|
||||
/// as main texture.
|
||||
/// Materials and textures returned behave like <c>new Texture2D()</c> and need to be destroyed.</summary>
|
||||
public Material outputMaterial;
|
||||
/// <summary>The newly generated main repacked texture, assigned at <c>outputMaterial.mainTexture</c>.
|
||||
/// Materials and textures returned behave like <c>new Texture2D()</c> and need to be destroyed.</summary>
|
||||
public Texture2D outputTexture;
|
||||
/// <summary>When non-null, this array will be filled with the resulting repacked texture for every property,
|
||||
/// just as the main repacked texture is assigned to <c>outputTexture</c>. This serves to avoid unnecessary
|
||||
/// allocations.
|
||||
/// Materials and textures returned behave like <c>new Texture2D()</c> and need to be destroyed.</summary>
|
||||
public Texture2D[] additionalOutputTextures;
|
||||
/// <summary>The optional generated Material for additive blend mode, holding the repacked texture
|
||||
/// as main texture.
|
||||
/// Materials and textures returned behave like <c>new Texture2D()</c> and need to be destroyed.</summary>
|
||||
public Material outputAdditiveMaterial;
|
||||
/// <summary>The optional generated Material for multiply blend mode, holding the repacked texture
|
||||
/// as main texture.
|
||||
/// Materials and textures returned behave like <c>new Texture2D()</c> and need to be destroyed.</summary>
|
||||
public Material outputMultiplyMaterial;
|
||||
/// <summary>The optional generated Material for screen blend mode, holding the repacked texture
|
||||
/// as main texture.
|
||||
/// Materials and textures returned behave like <c>new Texture2D()</c> and need to be destroyed.</summary>
|
||||
public Material outputScreenMaterial;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repack configuration settings for <see cref="GetRepackedAttachments"/> and <see cref="GetRepackedSkin"/>.
|
||||
/// </summary>
|
||||
public struct RepackAttachmentsSettings {
|
||||
public static readonly string DefaultTextureName = "Repacked Attachments";
|
||||
|
||||
/// <summary>
|
||||
/// Name used for newly created textures, materials and atlas pages.
|
||||
/// </summary>
|
||||
public string newAssetName;
|
||||
|
||||
/// <summary>
|
||||
/// Optional shader to be used, overrides the shader of <c>materialPropertySource.shader</c>.
|
||||
/// </summary>
|
||||
public Shader shader;
|
||||
|
||||
/// <summary>
|
||||
/// Normal blend mode Material reference. The generated normal blend mode Material copies these settings.
|
||||
/// </summary>
|
||||
public Material materialPropertySource;
|
||||
/// <summary>
|
||||
/// Additive blend mode Material reference. Optional, assign when repacking any additive blend mode
|
||||
/// attachments. The generated additive blend mode Material copies these settings.
|
||||
/// If all three blend mode materials <c>additiveMaterialSource</c>, <c>multiplyMaterialSource</c> and
|
||||
/// <c>screenMaterialSource</c> are null, blend mode attachments are treated as normal blend mode and
|
||||
/// repacked using a single material and atlas page.
|
||||
/// </summary>
|
||||
public Material additiveMaterialSource;
|
||||
/// <summary>
|
||||
/// Multiply blend mode Material reference. Optional, assign when repacking any multiply blend mode
|
||||
/// attachments. The generated multiply blend mode Material copies these settings.
|
||||
/// If all three blend mode materials <c>additiveMaterialSource</c>, <c>multiplyMaterialSource</c> and
|
||||
/// <c>screenMaterialSource</c> are null, blend mode attachments are treated as normal blend mode and
|
||||
/// repacked using a single material and atlas page.
|
||||
/// </summary>
|
||||
public Material multiplyMaterialSource;
|
||||
/// <summary>
|
||||
/// Screen blend mode Material reference. Optional, assign when repacking any screen blend mode attachments.
|
||||
/// The generated screen blend mode Material copies these settings.
|
||||
/// If all three blend mode materials <c>additiveMaterialSource</c>, <c>multiplyMaterialSource</c> and
|
||||
/// <c>screenMaterialSource</c> are null, blend mode attachments are treated as normal blend mode and
|
||||
/// repacked using a single material and atlas page.
|
||||
/// </summary>
|
||||
public Material screenMaterialSource;
|
||||
|
||||
/// <summary>Max atlas size of a repacked texture. Packed attachments are scaled down to fit into a single
|
||||
/// texture, not using multiple repacked texture pages.</summary>
|
||||
public int maxAtlasSize;
|
||||
/// <summary>Padding between packed texture regions in pixels.</summary>
|
||||
public int padding;
|
||||
/// <summary>Texture format of the main repacked texture.</summary>
|
||||
public TextureFormat textureFormat;
|
||||
/// <summary>
|
||||
/// Whether mip-maps shall be generated for newly created repacked textures.
|
||||
/// </summary>
|
||||
public bool mipmaps;
|
||||
/// <summary>When set to <c>true</c>, <see cref="AtlasUtilities.ClearCache()"/> is called after
|
||||
/// repacking to clear the texture cache. See remarks in <see cref="GetRepackedAttachments"/> and
|
||||
/// <see cref="GetRepackedSkin"/> for additional info.</summary>
|
||||
public bool clearCache;
|
||||
/// <summary>
|
||||
/// When <c>true</c>, original non-texture-region attachments (e.g. bounding box or point attachments) are
|
||||
/// attached to the new skin as-is.
|
||||
/// </summary>
|
||||
public bool useOriginalNonrenderables;
|
||||
|
||||
/// <summary>Optional additional textures (such as normal maps) to copy while repacking.
|
||||
/// To copy e.g. the main texture and normal maps, set it to
|
||||
/// <c>new int[] { Shader.PropertyToID("_BumpMap") }</c>.
|
||||
/// </summary>
|
||||
public int[] additionalTexturePropertyIDsToCopy;
|
||||
/// <summary>When non-null, this array will be used as <c>TextureFormat</c> at the Texture at the respective property.
|
||||
/// When set to <c>null</c> or when its array size is smaller, <c>textureFormat</c> is used where there
|
||||
/// exists no corresponding array item.</summary>
|
||||
public TextureFormat[] additionalTextureFormats;
|
||||
/// <summary>When non-null, this array will be used to determine whether <c>linear</c> or <c>sRGB</c> color space is
|
||||
/// used at the Texture at the respective property.
|
||||
/// When set to <c>null</c>, <c>linear</c> color space is assumed at every additional Texture element.
|
||||
/// When e.g. packing the main texture and normal maps, assign <c>new bool[] { true }</c> at this parameters,
|
||||
/// because normal maps use linear color space.</summary>
|
||||
public bool[] additionalTextureIsLinear;
|
||||
|
||||
/// <summary>Default settings providing reasonable parameters, modify according to your needs.</summary>
|
||||
public static RepackAttachmentsSettings Default = new RepackAttachmentsSettings {
|
||||
materialPropertySource = null,
|
||||
additiveMaterialSource = null,
|
||||
multiplyMaterialSource = null,
|
||||
screenMaterialSource = null,
|
||||
|
||||
newAssetName = "Repacked Attachments",
|
||||
|
||||
maxAtlasSize = 1024,
|
||||
padding = 2,
|
||||
textureFormat = SpineTextureFormat,
|
||||
mipmaps = UseMipMaps,
|
||||
clearCache = false,
|
||||
|
||||
useOriginalNonrenderables = true,
|
||||
additionalTexturePropertyIDsToCopy = null,
|
||||
additionalTextureFormats = null,
|
||||
additionalTextureIsLinear = null
|
||||
};
|
||||
}
|
||||
|
||||
protected struct BlendModeAtlasPages {
|
||||
public AtlasPage normalPage;
|
||||
public AtlasPage additivePage;
|
||||
public AtlasPage multiplyPage;
|
||||
public AtlasPage screenPage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills the outputAttachments list with new attachment objects based on the attachments in sourceAttachments,
|
||||
/// but mapped to a new single texture using the same material.</summary>
|
||||
/// <remarks>
|
||||
/// This variant of <c>GetRepackedAttachments</c> supports repacking blend mode attachments.
|
||||
/// To enable blend mode repacking, assign a reference material at either
|
||||
/// <see cref="RepackAttachmentsSettings.additiveMaterialSource"/>,
|
||||
/// <see cref="RepackAttachmentsSettings.multiplyMaterialSource"/> or
|
||||
/// <see cref="RepackAttachmentsSettings.screenMaterialSource"/> of parameter <paramref name="settings"/>.
|
||||
/// Otherwise any blend mode attachments are treated as normal blend mode and repacked using a single material
|
||||
/// and atlas page.
|
||||
/// </remarks>
|
||||
/// <remarks>Returned <c>Material</c> and <c>Texture</c> behave like <c>new Texture2D()</c>, thus you need to
|
||||
/// call <c>Destroy()</c> to free resources.
|
||||
/// This method caches necessary Texture copies for later re-use, which might steadily increase the texture
|
||||
/// memory footprint when used excessively.
|
||||
/// Set <see cref="RepackAttachmentsSettings.clearCache"/> to <c>true</c> at the argument
|
||||
/// <paramref name="settings"/> or call <see cref="AtlasUtilities.ClearCache()"/> to clear this texture cache.
|
||||
/// You may want to call <c>Resources.UnloadUnusedAssets()</c> after that.
|
||||
/// </remarks>
|
||||
/// <param name="sourceAttachments">The list of attachments to be repacked.</param>
|
||||
/// <param name = "outputAttachments">The List(Attachment) to populate with the newly created Attachment objects.
|
||||
/// May be equal to <c>sourceAttachments</c> for in-place operation.</param>
|
||||
/// <param name="settings">Repack configuration settings, see <see cref="RepackAttachmentsSettings"/>.</param>
|
||||
/// <param name="output">Repack output struct holding generated material and texture references for
|
||||
/// potential later cleanup.</param>
|
||||
public static void GetRepackedAttachments (List<Attachment> sourceAttachments, List<Attachment> outputAttachments,
|
||||
RepackAttachmentsSettings settings, ref RepackAttachmentsOutput output) {
|
||||
|
||||
if (sourceAttachments == null) throw new System.ArgumentNullException("sourceAttachments");
|
||||
if (outputAttachments == null) throw new System.ArgumentNullException("outputAttachments");
|
||||
|
||||
if (settings.shader == null)
|
||||
settings.shader = settings.materialPropertySource == null ?
|
||||
Shader.Find("Spine/Skeleton") : settings.materialPropertySource.shader;
|
||||
|
||||
output.outputTexture = null;
|
||||
if (settings.additionalTexturePropertyIDsToCopy != null && settings.additionalTextureIsLinear == null) {
|
||||
settings.additionalTextureIsLinear = new bool[settings.additionalTexturePropertyIDsToCopy.Length];
|
||||
for (int i = 0; i < settings.additionalTextureIsLinear.Length; ++i) {
|
||||
settings.additionalTextureIsLinear[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Use these to detect and use shared regions.
|
||||
existingRegions.Clear();
|
||||
regionIndices.Clear();
|
||||
|
||||
// Collect all textures from original attachments.
|
||||
int numTextureParamsToRepack = 1 + (settings.additionalTexturePropertyIDsToCopy == null ?
|
||||
0 : settings.additionalTexturePropertyIDsToCopy.Length);
|
||||
|
||||
if (texturesToPackAtParam.Length < numTextureParamsToRepack)
|
||||
Array.Resize(ref texturesToPackAtParam, numTextureParamsToRepack);
|
||||
for (int i = 0; i < numTextureParamsToRepack; ++i) {
|
||||
if (texturesToPackAtParam[i] != null)
|
||||
texturesToPackAtParam[i].Clear();
|
||||
else
|
||||
texturesToPackAtParam[i] = new List<Texture2D>();
|
||||
}
|
||||
originalRegions.Clear();
|
||||
|
||||
if (!object.ReferenceEquals(sourceAttachments, outputAttachments)) {
|
||||
outputAttachments.Clear();
|
||||
outputAttachments.AddRange(sourceAttachments);
|
||||
}
|
||||
|
||||
int newRegionIndex = 0;
|
||||
for (int attachmentIndex = 0, n = sourceAttachments.Count; attachmentIndex < n; attachmentIndex++) {
|
||||
Attachment originalAttachment = sourceAttachments[attachmentIndex];
|
||||
|
||||
if (originalAttachment is IHasTextureRegion) {
|
||||
MeshAttachment originalMeshAttachment = originalAttachment as MeshAttachment;
|
||||
IHasTextureRegion originalTextureAttachment = (IHasTextureRegion)originalAttachment;
|
||||
|
||||
Attachment newAttachment = (originalTextureAttachment.Sequence != null) ? originalAttachment :
|
||||
(originalMeshAttachment != null) ? originalMeshAttachment.NewLinkedMesh() :
|
||||
originalAttachment.Copy();
|
||||
IHasTextureRegion newTextureAttachment = (IHasTextureRegion)newAttachment;
|
||||
AtlasRegion region = newTextureAttachment.Region as AtlasRegion;
|
||||
if (region == null && originalTextureAttachment.Sequence != null)
|
||||
region = (AtlasRegion)originalTextureAttachment.Sequence.Regions[0];
|
||||
|
||||
int existingIndex;
|
||||
if (existingRegions.TryGetValue(region, out existingIndex)) {
|
||||
regionIndices.Add(existingIndex);
|
||||
} else {
|
||||
existingRegions.Add(region, newRegionIndex);
|
||||
Sequence originalSequence = originalTextureAttachment.Sequence;
|
||||
if (originalSequence != null) {
|
||||
newTextureAttachment.Sequence = new Sequence(originalSequence);
|
||||
for (int i = 0, regionCount = originalSequence.Regions.Length; i < regionCount; ++i) {
|
||||
AtlasRegion sequenceRegion = (AtlasRegion)originalSequence.Regions[i];
|
||||
AddRegionTexturesToPack(numTextureParamsToRepack, sequenceRegion,
|
||||
settings.textureFormat, settings.mipmaps, settings.additionalTextureFormats,
|
||||
settings.additionalTexturePropertyIDsToCopy, settings.additionalTextureIsLinear);
|
||||
originalRegions.Add(sequenceRegion);
|
||||
regionIndices.Add(newRegionIndex);
|
||||
newRegionIndex++;
|
||||
}
|
||||
} else {
|
||||
AddRegionTexturesToPack(numTextureParamsToRepack, region,
|
||||
settings.textureFormat, settings.mipmaps, settings.additionalTextureFormats,
|
||||
settings.additionalTexturePropertyIDsToCopy, settings.additionalTextureIsLinear);
|
||||
originalRegions.Add(region);
|
||||
regionIndices.Add(newRegionIndex);
|
||||
newRegionIndex++;
|
||||
}
|
||||
}
|
||||
outputAttachments[attachmentIndex] = newAttachment;
|
||||
} else {
|
||||
outputAttachments[attachmentIndex] = settings.useOriginalNonrenderables ?
|
||||
originalAttachment : originalAttachment.Copy();
|
||||
regionIndices.Add(NonrenderingRegion); // Output attachments pairs with regionIndices list 1:1. Pad with a sentinel if the attachment doesn't have a region.
|
||||
}
|
||||
}
|
||||
|
||||
// Rehydrate the repacked textures as a Material, Spine atlas and Spine.AtlasAttachments
|
||||
Material newMaterial = new Material(settings.shader);
|
||||
if (settings.materialPropertySource != null) {
|
||||
newMaterial.CopyPropertiesFromMaterial(settings.materialPropertySource);
|
||||
newMaterial.shaderKeywords = settings.materialPropertySource.shaderKeywords;
|
||||
}
|
||||
newMaterial.name = settings.newAssetName;
|
||||
|
||||
Rect[] rects = null;
|
||||
for (int i = 0; i < numTextureParamsToRepack; ++i) {
|
||||
// Fill a new texture with the collected attachment textures.
|
||||
TextureFormat format = (i > 0 &&
|
||||
settings.additionalTextureFormats != null &&
|
||||
i - 1 < settings.additionalTextureFormats.Length) ?
|
||||
settings.additionalTextureFormats[i - 1] : settings.textureFormat;
|
||||
bool linear = (i > 0) ? settings.additionalTextureIsLinear[i - 1] : false;
|
||||
Texture2D newTexture = new Texture2D(settings.maxAtlasSize, settings.maxAtlasSize,
|
||||
format, settings.mipmaps, linear);
|
||||
newTexture.mipMapBias = AtlasUtilities.DefaultMipmapBias;
|
||||
|
||||
List<Texture2D> texturesToPack = texturesToPackAtParam[i];
|
||||
if (texturesToPack.Count > 0) {
|
||||
Texture2D sourceTexture = texturesToPack[0];
|
||||
newTexture.CopyTextureAttributesFrom(sourceTexture);
|
||||
}
|
||||
newTexture.name = settings.newAssetName;
|
||||
Rect[] rectsForTexParam = newTexture.PackTextures(texturesToPack.ToArray(),
|
||||
settings.padding, settings.maxAtlasSize);
|
||||
if (i == 0) {
|
||||
rects = rectsForTexParam;
|
||||
newMaterial.mainTexture = newTexture;
|
||||
output.outputTexture = newTexture;
|
||||
} else {
|
||||
newMaterial.SetTexture(settings.additionalTexturePropertyIDsToCopy[i - 1], newTexture);
|
||||
if (output.additionalOutputTextures != null && output.additionalOutputTextures.Length > i - 1)
|
||||
output.additionalOutputTextures[i - 1] = newTexture;
|
||||
}
|
||||
}
|
||||
|
||||
AtlasPage page = newMaterial.ToSpineAtlasPage();
|
||||
page.name = settings.newAssetName;
|
||||
|
||||
BlendModeAtlasPages blendModePages = new BlendModeAtlasPages();
|
||||
blendModePages.normalPage = page;
|
||||
|
||||
repackedRegions.Clear();
|
||||
for (int i = 0, n = originalRegions.Count; i < n; i++) {
|
||||
AtlasRegion oldRegion = originalRegions[i];
|
||||
AtlasRegion newRegion = UVRectToAtlasRegion(rects[i], oldRegion, page);
|
||||
repackedRegions.Add(newRegion);
|
||||
}
|
||||
|
||||
Material additiveMaterialSource = settings.additiveMaterialSource;
|
||||
Material multiplyMaterialSource = settings.multiplyMaterialSource;
|
||||
Material screenMaterialSource = settings.screenMaterialSource;
|
||||
bool enableBlendModes =
|
||||
(additiveMaterialSource != null) ||
|
||||
(multiplyMaterialSource != null) ||
|
||||
(screenMaterialSource != null);
|
||||
Shader normalShader = settings.shader;
|
||||
|
||||
// Map the cloned attachments to the repacked atlas.
|
||||
for (int attachmentIndex = 0, repackedIndex = 0, n = outputAttachments.Count;
|
||||
attachmentIndex < n;
|
||||
++attachmentIndex, ++repackedIndex) {
|
||||
|
||||
Attachment attachment = outputAttachments[attachmentIndex];
|
||||
IHasTextureRegion textureAttachment = attachment as IHasTextureRegion;
|
||||
if (textureAttachment != null) {
|
||||
if (textureAttachment.Sequence != null) {
|
||||
TextureRegion[] regions = textureAttachment.Sequence.Regions;
|
||||
for (int r = 0, regionCount = regions.Length; r < regionCount; ++r) {
|
||||
TextureRegion originalRegion = regions[r];
|
||||
TextureRegion repackedRegion = repackedRegions[regionIndices[repackedIndex++]];
|
||||
AssignBlendMode(ref repackedRegion, originalRegion, normalShader, ref blendModePages,
|
||||
additiveMaterialSource, multiplyMaterialSource, screenMaterialSource);
|
||||
regions[r] = repackedRegion;
|
||||
}
|
||||
textureAttachment.Region = regions[0];
|
||||
--repackedIndex;
|
||||
} else {
|
||||
TextureRegion originalRegion = textureAttachment.Region;
|
||||
TextureRegion repackedRegion = repackedRegions[regionIndices[repackedIndex]];
|
||||
if (enableBlendModes)
|
||||
AssignBlendMode(ref repackedRegion, originalRegion, normalShader, ref blendModePages,
|
||||
additiveMaterialSource, multiplyMaterialSource, screenMaterialSource);
|
||||
textureAttachment.Region = repackedRegion;
|
||||
}
|
||||
textureAttachment.UpdateRegion();
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up.
|
||||
if (settings.clearCache)
|
||||
AtlasUtilities.ClearCache();
|
||||
|
||||
output.outputMaterial = newMaterial;
|
||||
|
||||
output.outputAdditiveMaterial = blendModePages.additivePage != null ? (Material)blendModePages.additivePage.rendererObject : null;
|
||||
output.outputMultiplyMaterial = blendModePages.multiplyPage != null ? (Material)blendModePages.multiplyPage.rendererObject : null;
|
||||
output.outputScreenMaterial = blendModePages.screenPage != null ? (Material)blendModePages.screenPage.rendererObject : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills the outputAttachments list with new attachment objects based on the attachments in sourceAttachments,
|
||||
/// but mapped to a new single texture using the same material. All blend modes are treated as normal blend mode.
|
||||
/// Use <see cref="GetRepackedAttachments(List{Attachment}, List{Attachment}, RepackAttachmentsSettings,
|
||||
/// ref RepackAttachmentsOutput)"/> if blend modes shall be retained.
|
||||
/// </summary>
|
||||
/// <remarks>Returned <c>Material</c> and <c>Texture</c> behave like <c>new Texture2D()</c>, thus you need to call <c>Destroy()</c>
|
||||
/// to free resources.
|
||||
/// This method caches necessary Texture copies for later re-use, which might steadily increase the texture memory
|
||||
@ -285,7 +651,10 @@ namespace Spine.Unity.AttachmentTools {
|
||||
|
||||
/// <summary>
|
||||
/// Fills the outputAttachments list with new attachment objects based on the attachments in sourceAttachments,
|
||||
/// but mapped to a new single texture using the same material.</summary>
|
||||
/// but mapped to a new single texture using the same material. All blend modes are treated as normal blend mode.
|
||||
/// Use <see cref="GetRepackedAttachments(List{Attachment}, List{Attachment}, RepackAttachmentsSettings,
|
||||
/// ref RepackAttachmentsOutput)"/> if blend modes shall be retained.
|
||||
/// </summary>
|
||||
/// <remarks>Returned <c>Material</c> and <c>Texture</c> behave like <c>new Texture2D()</c>, thus you need to call <c>Destroy()</c>
|
||||
/// to free resources.</remarks>
|
||||
/// <param name="sourceAttachments">The list of attachments to be repacked.</param>
|
||||
@ -316,157 +685,60 @@ namespace Spine.Unity.AttachmentTools {
|
||||
int[] additionalTexturePropertyIDsToCopy = null, Texture2D[] additionalOutputTextures = null,
|
||||
TextureFormat[] additionalTextureFormats = null, bool[] additionalTextureIsLinear = null) {
|
||||
|
||||
if (sourceAttachments == null) throw new System.ArgumentNullException("sourceAttachments");
|
||||
if (outputAttachments == null) throw new System.ArgumentNullException("outputAttachments");
|
||||
outputTexture = null;
|
||||
if (additionalTexturePropertyIDsToCopy != null && additionalTextureIsLinear == null) {
|
||||
additionalTextureIsLinear = new bool[additionalTexturePropertyIDsToCopy.Length];
|
||||
for (int i = 0; i < additionalTextureIsLinear.Length; ++i) {
|
||||
additionalTextureIsLinear[i] = true;
|
||||
RepackAttachmentsSettings settings = new RepackAttachmentsSettings {
|
||||
shader = shader,
|
||||
maxAtlasSize = maxAtlasSize,
|
||||
padding = padding,
|
||||
textureFormat = textureFormat,
|
||||
mipmaps = mipmaps,
|
||||
newAssetName = newAssetName,
|
||||
materialPropertySource = materialPropertySource,
|
||||
clearCache = clearCache,
|
||||
useOriginalNonrenderables = useOriginalNonrenderables,
|
||||
additionalTexturePropertyIDsToCopy = additionalTexturePropertyIDsToCopy,
|
||||
additionalTextureFormats = additionalTextureFormats,
|
||||
additionalTextureIsLinear = additionalTextureIsLinear
|
||||
};
|
||||
RepackAttachmentsOutput output = new RepackAttachmentsOutput();
|
||||
output.additionalOutputTextures = additionalOutputTextures;
|
||||
GetRepackedAttachments(sourceAttachments, outputAttachments, settings, ref output);
|
||||
|
||||
outputMaterial = output.outputMaterial;
|
||||
outputTexture = output.outputTexture;
|
||||
}
|
||||
|
||||
private static void AssignBlendMode (ref TextureRegion repackedRegion, TextureRegion originalRegion,
|
||||
Shader normalShader, ref BlendModeAtlasPages blendModePages,
|
||||
Material additiveMaterialSource, Material multiplyMaterialSource, Material screenMaterialSource) {
|
||||
|
||||
Material material = ((AtlasRegion)originalRegion).page.rendererObject as Material;
|
||||
if (material == null) return;
|
||||
|
||||
if (material.shader != normalShader) {
|
||||
if (material.shader.name.Contains("Multiply")) {
|
||||
((AtlasRegion)repackedRegion).page = GetBlendModePage(ref blendModePages.multiplyPage,
|
||||
blendModePages.normalPage, multiplyMaterialSource, "-Multiply");
|
||||
} else if (material.shader.name.Contains("Screen")) {
|
||||
((AtlasRegion)repackedRegion).page = GetBlendModePage(ref blendModePages.screenPage,
|
||||
blendModePages.normalPage, screenMaterialSource, "-Screen");
|
||||
} else if (material.shader.name.Contains("Additive")) {
|
||||
((AtlasRegion)repackedRegion).page = GetBlendModePage(ref blendModePages.additivePage,
|
||||
blendModePages.normalPage, additiveMaterialSource, "-Additive");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use these to detect and use shared regions.
|
||||
existingRegions.Clear();
|
||||
regionIndices.Clear();
|
||||
private static AtlasPage GetBlendModePage (ref AtlasPage targetPage, AtlasPage normalReferencePage,
|
||||
Material materialSource, string nameSuffix) {
|
||||
|
||||
// Collect all textures from original attachments.
|
||||
int numTextureParamsToRepack = 1 + (additionalTexturePropertyIDsToCopy == null ? 0 : additionalTexturePropertyIDsToCopy.Length);
|
||||
additionalOutputTextures = (additionalTexturePropertyIDsToCopy == null ? null : new Texture2D[additionalTexturePropertyIDsToCopy.Length]);
|
||||
if (texturesToPackAtParam.Length < numTextureParamsToRepack)
|
||||
Array.Resize(ref texturesToPackAtParam, numTextureParamsToRepack);
|
||||
for (int i = 0; i < numTextureParamsToRepack; ++i) {
|
||||
if (texturesToPackAtParam[i] != null)
|
||||
texturesToPackAtParam[i].Clear();
|
||||
else
|
||||
texturesToPackAtParam[i] = new List<Texture2D>();
|
||||
if (targetPage == null) {
|
||||
targetPage = normalReferencePage.Clone();
|
||||
Material material = new Material(materialSource);
|
||||
material.mainTexture = ((Material)normalReferencePage.rendererObject).mainTexture;
|
||||
material.name = material.mainTexture.name + nameSuffix;
|
||||
targetPage.rendererObject = material;
|
||||
}
|
||||
originalRegions.Clear();
|
||||
|
||||
if (!object.ReferenceEquals(sourceAttachments, outputAttachments)) {
|
||||
outputAttachments.Clear();
|
||||
outputAttachments.AddRange(sourceAttachments);
|
||||
}
|
||||
|
||||
int newRegionIndex = 0;
|
||||
for (int attachmentIndex = 0, n = sourceAttachments.Count; attachmentIndex < n; attachmentIndex++) {
|
||||
Attachment originalAttachment = sourceAttachments[attachmentIndex];
|
||||
|
||||
if (originalAttachment is IHasTextureRegion) {
|
||||
MeshAttachment originalMeshAttachment = originalAttachment as MeshAttachment;
|
||||
IHasTextureRegion originalTextureAttachment = (IHasTextureRegion)originalAttachment;
|
||||
|
||||
Attachment newAttachment = (originalTextureAttachment.Sequence != null) ? originalAttachment :
|
||||
(originalMeshAttachment != null) ? originalMeshAttachment.NewLinkedMesh() :
|
||||
originalAttachment.Copy();
|
||||
IHasTextureRegion newTextureAttachment = (IHasTextureRegion)newAttachment;
|
||||
AtlasRegion region = newTextureAttachment.Region as AtlasRegion;
|
||||
if (region == null && originalTextureAttachment.Sequence != null)
|
||||
region = (AtlasRegion)originalTextureAttachment.Sequence.Regions[0];
|
||||
|
||||
int existingIndex;
|
||||
if (existingRegions.TryGetValue(region, out existingIndex)) {
|
||||
regionIndices.Add(existingIndex);
|
||||
} else {
|
||||
existingRegions.Add(region, newRegionIndex);
|
||||
Sequence originalSequence = originalTextureAttachment.Sequence;
|
||||
if (originalSequence != null) {
|
||||
newTextureAttachment.Sequence = new Sequence(originalSequence);
|
||||
for (int i = 0, regionCount = originalSequence.Regions.Length; i < regionCount; ++i) {
|
||||
AtlasRegion sequenceRegion = (AtlasRegion)originalSequence.Regions[i];
|
||||
AddRegionTexturesToPack(numTextureParamsToRepack, sequenceRegion, textureFormat, mipmaps,
|
||||
additionalTextureFormats, additionalTexturePropertyIDsToCopy, additionalTextureIsLinear);
|
||||
originalRegions.Add(sequenceRegion);
|
||||
regionIndices.Add(newRegionIndex);
|
||||
newRegionIndex++;
|
||||
}
|
||||
} else {
|
||||
AddRegionTexturesToPack(numTextureParamsToRepack, region, textureFormat, mipmaps,
|
||||
additionalTextureFormats, additionalTexturePropertyIDsToCopy, additionalTextureIsLinear);
|
||||
originalRegions.Add(region);
|
||||
regionIndices.Add(newRegionIndex);
|
||||
newRegionIndex++;
|
||||
}
|
||||
}
|
||||
outputAttachments[attachmentIndex] = newAttachment;
|
||||
} else {
|
||||
outputAttachments[attachmentIndex] = useOriginalNonrenderables ? originalAttachment : originalAttachment.Copy();
|
||||
regionIndices.Add(NonrenderingRegion); // Output attachments pairs with regionIndices list 1:1. Pad with a sentinel if the attachment doesn't have a region.
|
||||
}
|
||||
}
|
||||
|
||||
// Rehydrate the repacked textures as a Material, Spine atlas and Spine.AtlasAttachments
|
||||
Material newMaterial = new Material(shader);
|
||||
if (materialPropertySource != null) {
|
||||
newMaterial.CopyPropertiesFromMaterial(materialPropertySource);
|
||||
newMaterial.shaderKeywords = materialPropertySource.shaderKeywords;
|
||||
}
|
||||
newMaterial.name = newAssetName;
|
||||
|
||||
Rect[] rects = null;
|
||||
for (int i = 0; i < numTextureParamsToRepack; ++i) {
|
||||
// Fill a new texture with the collected attachment textures.
|
||||
Texture2D newTexture = new Texture2D(maxAtlasSize, maxAtlasSize,
|
||||
(i > 0 && additionalTextureFormats != null && i - 1 < additionalTextureFormats.Length) ?
|
||||
additionalTextureFormats[i - 1] : textureFormat,
|
||||
mipmaps,
|
||||
(i > 0) ? additionalTextureIsLinear[i - 1] : false);
|
||||
newTexture.mipMapBias = AtlasUtilities.DefaultMipmapBias;
|
||||
|
||||
List<Texture2D> texturesToPack = texturesToPackAtParam[i];
|
||||
if (texturesToPack.Count > 0) {
|
||||
Texture2D sourceTexture = texturesToPack[0];
|
||||
newTexture.CopyTextureAttributesFrom(sourceTexture);
|
||||
}
|
||||
newTexture.name = newAssetName;
|
||||
Rect[] rectsForTexParam = newTexture.PackTextures(texturesToPack.ToArray(), padding, maxAtlasSize);
|
||||
if (i == 0) {
|
||||
rects = rectsForTexParam;
|
||||
newMaterial.mainTexture = newTexture;
|
||||
outputTexture = newTexture;
|
||||
} else {
|
||||
newMaterial.SetTexture(additionalTexturePropertyIDsToCopy[i - 1], newTexture);
|
||||
additionalOutputTextures[i - 1] = newTexture;
|
||||
}
|
||||
}
|
||||
|
||||
AtlasPage page = newMaterial.ToSpineAtlasPage();
|
||||
page.name = newAssetName;
|
||||
|
||||
repackedRegions.Clear();
|
||||
for (int i = 0, n = originalRegions.Count; i < n; i++) {
|
||||
AtlasRegion oldRegion = originalRegions[i];
|
||||
AtlasRegion newRegion = UVRectToAtlasRegion(rects[i], oldRegion, page);
|
||||
repackedRegions.Add(newRegion);
|
||||
}
|
||||
|
||||
// Map the cloned attachments to the repacked atlas.
|
||||
for (int attachmentIndex = 0, repackedIndex = 0, n = outputAttachments.Count;
|
||||
attachmentIndex < n;
|
||||
++attachmentIndex, ++repackedIndex) {
|
||||
|
||||
Attachment attachment = outputAttachments[attachmentIndex];
|
||||
IHasTextureRegion textureAttachment = attachment as IHasTextureRegion;
|
||||
if (textureAttachment != null) {
|
||||
if (textureAttachment.Sequence != null) {
|
||||
TextureRegion[] regions = textureAttachment.Sequence.Regions;
|
||||
textureAttachment.Region = repackedRegions[regionIndices[repackedIndex]];
|
||||
for (int r = 0, regionCount = regions.Length; r < regionCount; ++r) {
|
||||
regions[r] = repackedRegions[regionIndices[repackedIndex++]];
|
||||
}
|
||||
--repackedIndex;
|
||||
} else {
|
||||
textureAttachment.Region = repackedRegions[regionIndices[repackedIndex]];
|
||||
}
|
||||
textureAttachment.UpdateRegion();
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up.
|
||||
if (clearCache)
|
||||
AtlasUtilities.ClearCache();
|
||||
|
||||
outputMaterial = newMaterial;
|
||||
return targetPage;
|
||||
}
|
||||
|
||||
private static void AddRegionTexturesToPack (int numTextureParamsToRepack, AtlasRegion region,
|
||||
@ -485,7 +757,59 @@ namespace Spine.Unity.AttachmentTools {
|
||||
|
||||
/// <summary>
|
||||
/// Creates and populates a duplicate skin with cloned attachments that are backed by a new packed texture atlas
|
||||
/// comprised of all the regions from the original skin.</summary>
|
||||
/// comprised of all the regions from the original skin. Supports blend modes if configured accordingly.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>GetRepackedSkin is an expensive operation, preferably call it at level load time.
|
||||
/// No Spine.Atlas object is created so there is no way to find AtlasRegions except through the Attachments
|
||||
/// using them. Returned <c>Material</c> and <c>Texture</c> behave like <c>new Texture2D()</c>, thus you
|
||||
/// need to call <c>Destroy()</c> to free resources.
|
||||
/// This method caches necessary Texture copies for later re-use, which might steadily increase the texture memory
|
||||
/// footprint when used excessively.
|
||||
/// Set <see cref="RepackAttachmentsSettings.clearCache"/> to <c>true</c> at the argument
|
||||
/// <paramref name="settings"/> or call <see cref="AtlasUtilities.ClearCache()"/> to clear this texture cache.
|
||||
/// You may want to call <c>Resources.UnloadUnusedAssets()</c> after that.
|
||||
/// </para><para>
|
||||
/// This variant of <c>GetRepackedSkin</c> supports repacking blend mode attachments.
|
||||
/// To enable blend mode repacking, assign a reference material at either
|
||||
/// <see cref="RepackAttachmentsSettings.additiveMaterialSource"/>,
|
||||
/// <see cref="RepackAttachmentsSettings.multiplyMaterialSource"/> or
|
||||
/// <see cref="RepackAttachmentsSettings.screenMaterialSource"/> of parameter <paramref name="settings"/>.
|
||||
/// Otherwise any blend mode attachments are treated as normal blend mode and repacked using a single material
|
||||
/// and atlas page.</para>
|
||||
/// </remarks>
|
||||
/// <param name="settings">Repack configuration settings, see <see cref="RepackAttachmentsSettings"/>.</param>
|
||||
/// <param name="output">Repack output struct holding generated material and texture references for
|
||||
/// potential later cleanup.</param>
|
||||
public static Skin GetRepackedSkin (this Skin o, string newName,
|
||||
RepackAttachmentsSettings settings, ref RepackAttachmentsOutput output) {
|
||||
|
||||
if (o == null) throw new System.NullReferenceException("Skin was null");
|
||||
ICollection<Skin.SkinEntry> skinAttachments = o.Attachments;
|
||||
Skin newSkin = new Skin(newName);
|
||||
|
||||
newSkin.Bones.AddRange(o.Bones);
|
||||
newSkin.Constraints.AddRange(o.Constraints);
|
||||
|
||||
inoutAttachments.Clear();
|
||||
foreach (Skin.SkinEntry entry in skinAttachments) {
|
||||
inoutAttachments.Add(entry.Attachment);
|
||||
}
|
||||
GetRepackedAttachments(inoutAttachments, inoutAttachments, settings, ref output);
|
||||
int i = 0;
|
||||
foreach (Skin.SkinEntry originalSkinEntry in skinAttachments) {
|
||||
Attachment newAttachment = inoutAttachments[i++];
|
||||
newSkin.SetAttachment(originalSkinEntry.SlotIndex, originalSkinEntry.Name, newAttachment);
|
||||
}
|
||||
return newSkin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and populates a duplicate skin with cloned attachments that are backed by a new packed texture atlas
|
||||
/// comprised of all the regions from the original skin. All blend modes are treated as normal blend mode.
|
||||
/// Use <see cref="GetRepackedSkin(Skin, string, RepackAttachmentsSettings, ref RepackAttachmentsOutput)"/> if
|
||||
/// blend modes shall be retained.
|
||||
/// </summary>
|
||||
/// <remarks>GetRepackedSkin is an expensive operation, preferably call it at level load time.
|
||||
/// No Spine.Atlas object is created so there is no way to find AtlasRegions except through the Attachments using them.
|
||||
/// Returned <c>Material</c> and <c>Texture</c> behave like <c>new Texture2D()</c>, thus you need to call <c>Destroy()</c>
|
||||
@ -526,36 +850,36 @@ namespace Spine.Unity.AttachmentTools {
|
||||
|
||||
/// <summary>
|
||||
/// Creates and populates a duplicate skin with cloned attachments that are backed by a new packed texture atlas
|
||||
/// comprised of all the regions from the original skin.</summary>
|
||||
/// See documentation of <see cref="GetRepackedSkin"/> for details.
|
||||
/// comprised of all the regions from the original skin. All blend modes are treated as normal blend mode.
|
||||
/// </summary>
|
||||
/// <remarks>See documentation of <see cref="GetRepackedSkin"/> for details.</remarks>
|
||||
public static Skin GetRepackedSkin (this Skin o, string newName, Shader shader, out Material outputMaterial, out Texture2D outputTexture,
|
||||
int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps,
|
||||
Material materialPropertySource = null, bool clearCache = false, bool useOriginalNonrenderables = true,
|
||||
int[] additionalTexturePropertyIDsToCopy = null, Texture2D[] additionalOutputTextures = null,
|
||||
TextureFormat[] additionalTextureFormats = null, bool[] additionalTextureIsLinear = null) {
|
||||
|
||||
outputTexture = null;
|
||||
RepackAttachmentsSettings settings = new RepackAttachmentsSettings {
|
||||
shader = shader,
|
||||
maxAtlasSize = maxAtlasSize,
|
||||
padding = padding,
|
||||
textureFormat = textureFormat,
|
||||
mipmaps = mipmaps,
|
||||
newAssetName = newName,
|
||||
materialPropertySource = materialPropertySource,
|
||||
clearCache = clearCache,
|
||||
useOriginalNonrenderables = useOriginalNonrenderables,
|
||||
additionalTexturePropertyIDsToCopy = additionalTexturePropertyIDsToCopy,
|
||||
additionalTextureFormats = additionalTextureFormats,
|
||||
additionalTextureIsLinear = additionalTextureIsLinear
|
||||
};
|
||||
RepackAttachmentsOutput output = new RepackAttachmentsOutput();
|
||||
output.additionalOutputTextures = additionalOutputTextures;
|
||||
|
||||
if (o == null) throw new System.NullReferenceException("Skin was null");
|
||||
ICollection<Skin.SkinEntry> skinAttachments = o.Attachments;
|
||||
Skin newSkin = new Skin(newName);
|
||||
|
||||
newSkin.Bones.AddRange(o.Bones);
|
||||
newSkin.Constraints.AddRange(o.Constraints);
|
||||
|
||||
inoutAttachments.Clear();
|
||||
foreach (Skin.SkinEntry entry in skinAttachments) {
|
||||
inoutAttachments.Add(entry.Attachment);
|
||||
}
|
||||
GetRepackedAttachments(inoutAttachments, inoutAttachments, materialPropertySource, out outputMaterial, out outputTexture,
|
||||
maxAtlasSize, padding, textureFormat, mipmaps, newName, clearCache, useOriginalNonrenderables,
|
||||
additionalTexturePropertyIDsToCopy, additionalOutputTextures, additionalTextureFormats, additionalTextureIsLinear);
|
||||
int i = 0;
|
||||
foreach (Skin.SkinEntry originalSkinEntry in skinAttachments) {
|
||||
Attachment newAttachment = inoutAttachments[i++];
|
||||
newSkin.SetAttachment(originalSkinEntry.SlotIndex, originalSkinEntry.Name, newAttachment);
|
||||
}
|
||||
return newSkin;
|
||||
Skin repackedSkin = o.GetRepackedSkin(newName, settings, ref output);
|
||||
outputMaterial = output.outputMaterial;
|
||||
outputTexture = output.outputTexture;
|
||||
return repackedSkin;
|
||||
}
|
||||
|
||||
public static Sprite ToSprite (this AtlasRegion ar, float pixelsPerUnit = 100) {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"name": "com.esotericsoftware.spine.spine-unity",
|
||||
"displayName": "spine-unity Runtime",
|
||||
"description": "This plugin provides the spine-unity runtime core and examples. Spine Examples can be installed via the Samples tab.",
|
||||
"version": "4.3.44",
|
||||
"version": "4.3.45",
|
||||
"unity": "2018.3",
|
||||
"author": {
|
||||
"name": "Esoteric Software",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user