283 lines
12 KiB
Plaintext
283 lines
12 KiB
Plaintext
/******************************************************************************
|
|
* Spine Runtimes License Agreement
|
|
* Last updated January 1, 2020. Replaces all prior versions.
|
|
*
|
|
* Copyright (c) 2013-2020, Esoteric Software LLC
|
|
*
|
|
* Integration of the Spine Runtimes into software or otherwise creating
|
|
* derivative works of the Spine Runtimes is permitted under the terms and
|
|
* conditions of Section 2 of the Spine Editor License Agreement:
|
|
* http://esotericsoftware.com/spine-editor-license
|
|
*
|
|
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
|
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
|
* "Products"), provided that each user of the Products must obtain their own
|
|
* Spine Editor license and redistribution of the Products in any form must
|
|
* include this license and copyright notice.
|
|
*
|
|
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
|
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*****************************************************************************/
|
|
|
|
#if UNITY_2020_1_OR_NEWER
|
|
#define UPGRADE_ALL_BLEND_MODE_MATERIALS
|
|
#endif
|
|
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System;
|
|
|
|
namespace Spine.Unity.Editor {
|
|
|
|
public class BlendModeMaterialsUtility {
|
|
|
|
public const string MATERIAL_SUFFIX_MULTIPLY = "-Multiply";
|
|
public const string MATERIAL_SUFFIX_SCREEN = "-Screen";
|
|
public const string MATERIAL_SUFFIX_ADDITIVE = "-Additive";
|
|
|
|
#if UPGRADE_ALL_BLEND_MODE_MATERIALS
|
|
public const bool ShallUpgradeBlendModeMaterials = true;
|
|
#else
|
|
public const bool ShallUpgradeBlendModeMaterials = false;
|
|
#endif
|
|
|
|
protected class TemplateMaterials {
|
|
public Material multiplyTemplate;
|
|
public Material screenTemplate;
|
|
public Material additiveTemplate;
|
|
};
|
|
|
|
public static void UpgradeBlendModeMaterials (SkeletonDataAsset skeletonDataAsset) {
|
|
var skeletonData = skeletonDataAsset.GetSkeletonData(true);
|
|
if (skeletonData == null)
|
|
return;
|
|
UpdateBlendModeMaterials(skeletonDataAsset, ref skeletonData, true);
|
|
}
|
|
|
|
public static void UpdateBlendModeMaterials (SkeletonDataAsset skeletonDataAsset) {
|
|
var skeletonData = skeletonDataAsset.GetSkeletonData(true);
|
|
if (skeletonData == null)
|
|
return;
|
|
UpdateBlendModeMaterials(skeletonDataAsset, ref skeletonData, false);
|
|
}
|
|
|
|
public static void UpdateBlendModeMaterials (SkeletonDataAsset skeletonDataAsset, ref SkeletonData skeletonData,
|
|
bool upgradeFromModifierAssets = ShallUpgradeBlendModeMaterials) {
|
|
|
|
TemplateMaterials templateMaterials = new TemplateMaterials();
|
|
bool anyMaterialsChanged = ClearUndesiredMaterialEntries(skeletonDataAsset);
|
|
|
|
var blendModesModifierAsset = FindBlendModeMaterialsModifierAsset(skeletonDataAsset);
|
|
if (blendModesModifierAsset) {
|
|
if (upgradeFromModifierAssets) {
|
|
TransferSettingsFromModifierAsset(blendModesModifierAsset,
|
|
skeletonDataAsset, templateMaterials);
|
|
UpdateBlendmodeMaterialsRequiredState(skeletonDataAsset, skeletonData);
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
else {
|
|
if (!UpdateBlendmodeMaterialsRequiredState(skeletonDataAsset, skeletonData))
|
|
return;
|
|
AssignPreferencesTemplateMaterials(templateMaterials);
|
|
}
|
|
bool success = CreateAndAssignMaterials(skeletonDataAsset, templateMaterials, ref anyMaterialsChanged);
|
|
if (success) {
|
|
if (blendModesModifierAsset != null) {
|
|
RemoveObsoleteModifierAsset(blendModesModifierAsset, skeletonDataAsset);
|
|
}
|
|
}
|
|
|
|
skeletonDataAsset.Clear();
|
|
skeletonData = skeletonDataAsset.GetSkeletonData(true);
|
|
if (anyMaterialsChanged)
|
|
ReloadSceneSkeletons(skeletonDataAsset);
|
|
AssetDatabase.SaveAssets();
|
|
}
|
|
|
|
protected static bool ClearUndesiredMaterialEntries (SkeletonDataAsset skeletonDataAsset) {
|
|
Predicate<BlendModeMaterials.ReplacementMaterial> ifMaterialMissing = r => r.material == null;
|
|
|
|
bool anyMaterialsChanged = false;
|
|
if (!skeletonDataAsset.blendModeMaterials.applyAdditiveMaterial) {
|
|
anyMaterialsChanged |= skeletonDataAsset.blendModeMaterials.additiveMaterials.Count > 0;
|
|
skeletonDataAsset.blendModeMaterials.additiveMaterials.Clear();
|
|
}
|
|
else
|
|
anyMaterialsChanged |= skeletonDataAsset.blendModeMaterials.additiveMaterials.RemoveAll(ifMaterialMissing) != 0;
|
|
anyMaterialsChanged |= skeletonDataAsset.blendModeMaterials.multiplyMaterials.RemoveAll(ifMaterialMissing) != 0;
|
|
anyMaterialsChanged |= skeletonDataAsset.blendModeMaterials.screenMaterials.RemoveAll(ifMaterialMissing) != 0;
|
|
return anyMaterialsChanged;
|
|
}
|
|
|
|
protected static BlendModeMaterialsAsset FindBlendModeMaterialsModifierAsset (SkeletonDataAsset skeletonDataAsset) {
|
|
foreach (var modifierAsset in skeletonDataAsset.skeletonDataModifiers) {
|
|
if (modifierAsset is BlendModeMaterialsAsset)
|
|
return (BlendModeMaterialsAsset)modifierAsset;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected static bool UpdateBlendmodeMaterialsRequiredState (SkeletonDataAsset skeletonDataAsset, SkeletonData skeletonData) {
|
|
return skeletonDataAsset.blendModeMaterials.UpdateBlendmodeMaterialsRequiredState(skeletonData);
|
|
}
|
|
|
|
protected static void TransferSettingsFromModifierAsset (BlendModeMaterialsAsset modifierAsset,
|
|
SkeletonDataAsset skeletonDataAsset, TemplateMaterials templateMaterials) {
|
|
|
|
skeletonDataAsset.blendModeMaterials.TransferSettingsFrom(modifierAsset);
|
|
|
|
templateMaterials.multiplyTemplate = modifierAsset.multiplyMaterialTemplate;
|
|
templateMaterials.screenTemplate = modifierAsset.screenMaterialTemplate;
|
|
templateMaterials.additiveTemplate = modifierAsset.additiveMaterialTemplate;
|
|
}
|
|
|
|
protected static void RemoveObsoleteModifierAsset (BlendModeMaterialsAsset modifierAsset,
|
|
SkeletonDataAsset skeletonDataAsset) {
|
|
|
|
skeletonDataAsset.skeletonDataModifiers.Remove(modifierAsset);
|
|
Debug.Log(string.Format("BlendModeMaterialsAsset upgraded to built-in BlendModeMaterials at SkeletonDataAsset '{0}'.",
|
|
skeletonDataAsset.name), skeletonDataAsset);
|
|
EditorUtility.SetDirty(skeletonDataAsset);
|
|
}
|
|
|
|
protected static void AssignPreferencesTemplateMaterials (TemplateMaterials templateMaterials) {
|
|
|
|
templateMaterials.multiplyTemplate = SpineEditorUtilities.Preferences.BlendModeMaterialMultiply;
|
|
templateMaterials.screenTemplate = SpineEditorUtilities.Preferences.BlendModeMaterialScreen;
|
|
templateMaterials.additiveTemplate = SpineEditorUtilities.Preferences.BlendModeMaterialAdditive;
|
|
}
|
|
|
|
protected static bool CreateAndAssignMaterials (SkeletonDataAsset skeletonDataAsset,
|
|
TemplateMaterials templateMaterials, ref bool anyReplacementMaterialsChanged) {
|
|
|
|
bool anyCreationFailed = false;
|
|
var blendModeMaterials = skeletonDataAsset.blendModeMaterials;
|
|
bool applyAdditiveMaterial = blendModeMaterials.applyAdditiveMaterial;
|
|
|
|
var skinEntries = new List<Skin.SkinEntry>();
|
|
|
|
skeletonDataAsset.Clear();
|
|
skeletonDataAsset.isUpgradingBlendModeMaterials = true;
|
|
SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(true);
|
|
|
|
var slotsItems = skeletonData.Slots.Items;
|
|
for (int slotIndex = 0, slotCount = skeletonData.Slots.Count; slotIndex < slotCount; slotIndex++) {
|
|
var slot = slotsItems[slotIndex];
|
|
if (slot.BlendMode == BlendMode.Normal) continue;
|
|
if (!applyAdditiveMaterial && slot.BlendMode == BlendMode.Additive) continue;
|
|
|
|
List<BlendModeMaterials.ReplacementMaterial> replacementMaterials = null;
|
|
Material materialTemplate = null;
|
|
string materialSuffix = null;
|
|
switch (slot.BlendMode) {
|
|
case BlendMode.Multiply:
|
|
replacementMaterials = blendModeMaterials.multiplyMaterials;
|
|
materialTemplate = templateMaterials.multiplyTemplate;
|
|
materialSuffix = MATERIAL_SUFFIX_MULTIPLY;
|
|
break;
|
|
case BlendMode.Screen:
|
|
replacementMaterials = blendModeMaterials.screenMaterials;
|
|
materialTemplate = templateMaterials.screenTemplate;
|
|
materialSuffix = MATERIAL_SUFFIX_SCREEN;
|
|
break;
|
|
case BlendMode.Additive:
|
|
replacementMaterials = blendModeMaterials.additiveMaterials;
|
|
materialTemplate = templateMaterials.additiveTemplate;
|
|
materialSuffix = MATERIAL_SUFFIX_ADDITIVE;
|
|
break;
|
|
}
|
|
|
|
skinEntries.Clear();
|
|
foreach (var skin in skeletonData.Skins)
|
|
skin.GetAttachments(slotIndex, skinEntries);
|
|
|
|
foreach (var entry in skinEntries) {
|
|
var renderableAttachment = entry.Attachment as IHasRendererObject;
|
|
if (renderableAttachment != null) {
|
|
var originalRegion = (AtlasRegion)renderableAttachment.RendererObject;
|
|
bool replacementExists = replacementMaterials.Exists(
|
|
replacement => replacement.pageName == originalRegion.page.name);
|
|
if (!replacementExists) {
|
|
bool createdNewMaterial;
|
|
var replacement = CreateOrLoadReplacementMaterial(originalRegion, materialTemplate, materialSuffix, out createdNewMaterial);
|
|
if (replacement != null) {
|
|
replacementMaterials.Add(replacement);
|
|
anyReplacementMaterialsChanged = true;
|
|
if (createdNewMaterial) {
|
|
Debug.Log(string.Format("Created blend mode Material '{0}' for SkeletonDataAsset '{1}'.",
|
|
replacement.material.name, skeletonDataAsset), replacement.material);
|
|
}
|
|
}
|
|
else {
|
|
Debug.LogError(string.Format("Failed creating blend mode Material for SkeletonDataAsset '{0}',"+
|
|
" atlas page '{1}', template '{2}'.",
|
|
skeletonDataAsset.name, originalRegion.page.name, materialTemplate.name),
|
|
skeletonDataAsset);
|
|
anyCreationFailed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
skeletonDataAsset.isUpgradingBlendModeMaterials = false;
|
|
EditorUtility.SetDirty(skeletonDataAsset);
|
|
return !anyCreationFailed;
|
|
}
|
|
|
|
protected static string GetBlendModeMaterialPath(AtlasPage originalPage, string materialSuffix) {
|
|
var originalMaterial = originalPage.rendererObject as Material;
|
|
var originalPath = AssetDatabase.GetAssetPath(originalMaterial);
|
|
return originalPath.Replace(".mat", materialSuffix + ".mat");
|
|
}
|
|
|
|
protected static BlendModeMaterials.ReplacementMaterial CreateOrLoadReplacementMaterial (
|
|
AtlasRegion originalRegion, Material materialTemplate, string materialSuffix, out bool createdNewMaterial) {
|
|
|
|
createdNewMaterial = false;
|
|
var newReplacement = new BlendModeMaterials.ReplacementMaterial();
|
|
var originalPage = originalRegion.page;
|
|
var originalMaterial = originalPage.rendererObject as Material;
|
|
var blendMaterialPath = GetBlendModeMaterialPath(originalPage, materialSuffix);
|
|
|
|
newReplacement.pageName = originalPage.name;
|
|
if (File.Exists(blendMaterialPath)) {
|
|
newReplacement.material = AssetDatabase.LoadAssetAtPath<Material>(blendMaterialPath);
|
|
}
|
|
else {
|
|
var blendModeMaterial = new Material(materialTemplate) {
|
|
name = originalMaterial.name + " " + materialTemplate.name,
|
|
mainTexture = originalMaterial.mainTexture
|
|
};
|
|
newReplacement.material = blendModeMaterial;
|
|
|
|
AssetDatabase.CreateAsset(blendModeMaterial, blendMaterialPath);
|
|
EditorUtility.SetDirty(blendModeMaterial);
|
|
createdNewMaterial = true;
|
|
}
|
|
|
|
if (newReplacement.material)
|
|
return newReplacement;
|
|
else
|
|
return null;
|
|
}
|
|
|
|
protected static void ReloadSceneSkeletons (SkeletonDataAsset skeletonDataAsset) {
|
|
if (SpineEditorUtilities.Preferences.autoReloadSceneSkeletons)
|
|
SpineEditorUtilities.DataReloadHandler.ReloadSceneSkeletonComponents(skeletonDataAsset);
|
|
}
|
|
}
|
|
}
|