[unity] Added native support for slot blend modes Additive, Multiply and Screen with automatic assignment at newly imported skeleton assets. Added upgrade functionality. Closes #1822, closes #1559.

This commit is contained in:
Harald Csaszar 2021-01-22 11:03:24 +01:00
parent 110581abea
commit 752e72eb8f
10 changed files with 619 additions and 4 deletions

View File

@ -250,6 +250,7 @@
* `SkeletonRenderer` components now provide an additional update mode `Only Event Timelines` at the `Update When Invisible` property. This mode saves additional timeline updates compared to update mode `Everything Except Mesh`.
* Now all URP (Universal Render Pipeline) and LWRP (Lightweight Render Pipeline) shaders support SRP (Scriptable Render Pipeline) batching. See [Unity SRPBatcher documentation pages](https://docs.unity3d.com/Manual/SRPBatcher.html) for additional information.
* Sprite shaders now provide four `Diffuse Ramp` modes as an Inspector Material parameter: `Hard`, `Soft`, `Old Hard` and `Old Soft`. In spine-unity 3.8 it defaults to `Old Hard` to keep the behaviour of existing projects unchanged. Note that `Old Hard` and `Old Soft` ramp versions were using only the right half of the ramp texture, and additionally multiplying the light intensity by 2, both leading to brighter lighting than without a ramp texture active. The new ramp modes `Hard` and `Soft` use the full ramp texture and do not modify light intensity, being consistent with lighting without a ramp texture active.
* Added **native support for slot blend modes** `Additive`, `Multiply` and `Screen` with automatic assignment at newly imported skeleton assets. `BlendModeMaterialAssets` are now obsolete and replaced by the native properties at `SkeletonDataAsset`. The `SkeletonDataAsset` Inspector provides a new `Blend Modes - Upgrade` button to upgrade an obsolete `BlendModeMaterialAsset` to the native blend modes properties. This upgrade will be performed automatically on imported and re-imported assets in Unity 2020.1 and newer to prevent reported `BlendModeMaterialAsset` issues in these Unity versions. spine-unity 4.0 and newer will automatically perform this upgrade regardless of the Unity version.
* **Changes of default values**
* `SkeletonMecanim`'s `Layer Mix Mode` now defaults to `MixMode.MixNext` instead of `MixMode.MixAlways`.

View File

@ -56,6 +56,7 @@ namespace Spine.Unity.Editor {
SerializedProperty atlasAssets, skeletonJSON, scale, fromAnimation, toAnimation, duration, defaultMix;
SerializedProperty skeletonDataModifiers;
SerializedProperty blendModeMaterials;
#if SPINE_TK2D
SerializedProperty spriteCollection;
#endif
@ -114,6 +115,7 @@ namespace Spine.Unity.Editor {
defaultMix = serializedObject.FindProperty("defaultMix");
skeletonDataModifiers = serializedObject.FindProperty("skeletonDataModifiers");
blendModeMaterials = serializedObject.FindProperty("blendModeMaterials");
#if SPINE_SKELETON_MECANIM
controller = serializedObject.FindProperty("controller");
@ -125,7 +127,7 @@ namespace Spine.Unity.Editor {
#else
// Analysis disable once ConvertIfToOrExpression
if (newAtlasAssets) atlasAssets.isExpanded = true;
#endif
#endif
// This handles the case where the managed editor assembly is unloaded before recompilation when code changes.
AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload;
@ -286,6 +288,8 @@ namespace Spine.Unity.Editor {
EditorGUILayout.DelayedFloatField(scale); //EditorGUILayout.PropertyField(scale);
EditorGUILayout.Space();
EditorGUILayout.PropertyField(skeletonDataModifiers, true);
DrawBlendModeMaterialProperties();
}
// Texture source field.
@ -311,6 +315,27 @@ namespace Spine.Unity.Editor {
}
void DrawBlendModeMaterialProperties () {
if (skeletonDataModifiers.arraySize > 0) {
EditorGUILayout.BeginHorizontal(GUILayout.Height(EditorGUIUtility.singleLineHeight + 5));
EditorGUILayout.PrefixLabel("Blend Modes");
if (GUILayout.Button(new GUIContent("Upgrade", "Upgrade BlendModeMaterialAsset to built-in BlendModeMaterials."), EditorStyles.miniButton, GUILayout.Width(65f))) {
foreach (SkeletonDataAsset skeletonData in targets) {
BlendModeMaterialsUtility.UpgradeBlendModeMaterials(skeletonData);
}
}
EditorGUILayout.EndHorizontal();
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(blendModeMaterials, true);
if (EditorGUI.EndChangeCheck()) {
serializedObject.ApplyModifiedProperties();
foreach (SkeletonDataAsset skeletonData in targets) {
BlendModeMaterialsUtility.UpdateBlendModeMaterials(skeletonData);
}
}
}
void DrawSkeletonDataFields () {
using (new EditorGUILayout.HorizontalScope()) {
EditorGUILayout.LabelField("SkeletonData", EditorStyles.boldLabel);
@ -331,6 +356,8 @@ namespace Spine.Unity.Editor {
EditorGUILayout.DelayedFloatField(scale); //EditorGUILayout.PropertyField(scale);
EditorGUILayout.Space();
EditorGUILayout.PropertyField(skeletonDataModifiers, true);
DrawBlendModeMaterialProperties();
}
void DrawAtlasAssetsFields () {

View File

@ -427,6 +427,8 @@ namespace Spine.Unity.Editor {
}
SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(true);
BlendModeMaterialsUtility.UpdateBlendModeMaterials(skeletonDataAsset, ref skeletonData);
string currentHash = skeletonData != null ? skeletonData.Hash : null;
#if SPINE_SKELETONMECANIM
@ -887,6 +889,7 @@ namespace Spine.Unity.Editor {
skeletonDataAsset.skeletonJSON = spineJson;
skeletonDataAsset.defaultMix = SpineEditorUtilities.Preferences.defaultMix;
skeletonDataAsset.scale = SpineEditorUtilities.Preferences.defaultScale;
skeletonDataAsset.blendModeMaterials.applyAdditiveMaterial = !SpineEditorUtilities.Preferences.UsesPMAWorkflow;
}
AssetDatabase.CreateAsset(skeletonDataAsset, filePath);
@ -894,7 +897,8 @@ namespace Spine.Unity.Editor {
} else {
skeletonDataAsset.atlasAssets = atlasAssets;
skeletonDataAsset.Clear();
skeletonDataAsset.GetSkeletonData(true);
var skeletonData = skeletonDataAsset.GetSkeletonData(true);
BlendModeMaterialsUtility.UpdateBlendModeMaterials(skeletonDataAsset, ref skeletonData);
}
return skeletonDataAsset;

View File

@ -0,0 +1,282 @@
/******************************************************************************
* 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);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8094f8aedb33b7744b109c2c1294d37a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -124,6 +124,32 @@ namespace Spine.Unity.Editor {
const string TEXTURE_SETTINGS_REFERENCE_KEY = "SPINE_TEXTURE_SETTINGS_REFERENCE";
public static string textureSettingsReference = SpinePreferences.DEFAULT_TEXTURE_SETTINGS_REFERENCE;
public static bool UsesPMAWorkflow {
get {
return SpinePreferences.IsPMAWorkflow(textureSettingsReference);
}
}
const string BLEND_MODE_MATERIAL_MULTIPLY_KEY = "SPINE_BLENDMODE_MATERIAL_MULTIPLY";
const string BLEND_MODE_MATERIAL_SCREEN_KEY = "SPINE_BLENDMODE_MATERIAL_SCREEN";
const string BLEND_MODE_MATERIAL_ADDITIVE_KEY = "SPINE_BLENDMODE_MATERIAL_ADDITIVE";
public static string blendModeMaterialMultiply = "";
public static string blendModeMaterialScreen = "";
public static string blendModeMaterialAdditive = "";
public const string DEFAULT_BLEND_MODE_MULTIPLY_MATERIAL = SpinePreferences.DEFAULT_BLEND_MODE_MULTIPLY_MATERIAL;
public const string DEFAULT_BLEND_MODE_SCREEN_MATERIAL = SpinePreferences.DEFAULT_BLEND_MODE_SCREEN_MATERIAL;
public const string DEFAULT_BLEND_MODE_ADDITIVE_MATERIAL = SpinePreferences.DEFAULT_BLEND_MODE_ADDITIVE_MATERIAL;
public static Material BlendModeMaterialMultiply {
get { return AssetDatabase.LoadAssetAtPath<Material>(blendModeMaterialMultiply); }
}
public static Material BlendModeMaterialScreen {
get { return AssetDatabase.LoadAssetAtPath<Material>(blendModeMaterialScreen); }
}
public static Material BlendModeMaterialAdditive {
get { return AssetDatabase.LoadAssetAtPath<Material>(blendModeMaterialAdditive); }
}
const string ATLASTXT_WARNING_KEY = "SPINE_ATLASTXT_WARNING";
public static bool atlasTxtImportWarning = SpinePreferences.DEFAULT_ATLASTXT_WARNING;
@ -161,6 +187,9 @@ namespace Spine.Unity.Editor {
showHierarchyIcons = EditorPrefs.GetBool(SHOW_HIERARCHY_ICONS_KEY, SpinePreferences.DEFAULT_SHOW_HIERARCHY_ICONS);
setTextureImporterSettings = EditorPrefs.GetBool(SET_TEXTUREIMPORTER_SETTINGS_KEY, SpinePreferences.DEFAULT_SET_TEXTUREIMPORTER_SETTINGS);
textureSettingsReference = EditorPrefs.GetString(TEXTURE_SETTINGS_REFERENCE_KEY, SpinePreferences.DEFAULT_TEXTURE_SETTINGS_REFERENCE);
blendModeMaterialMultiply = EditorPrefs.GetString(BLEND_MODE_MATERIAL_MULTIPLY_KEY, "");
blendModeMaterialScreen = EditorPrefs.GetString(BLEND_MODE_MATERIAL_SCREEN_KEY, "");
blendModeMaterialAdditive = EditorPrefs.GetString(BLEND_MODE_MATERIAL_ADDITIVE_KEY, "");
autoReloadSceneSkeletons = EditorPrefs.GetBool(AUTO_RELOAD_SCENESKELETONS_KEY, SpinePreferences.DEFAULT_AUTO_RELOAD_SCENESKELETONS);
mecanimEventIncludeFolderName = EditorPrefs.GetBool(MECANIM_EVENT_INCLUDE_FOLDERNAME_KEY, SpinePreferences.DEFAULT_MECANIM_EVENT_INCLUDE_FOLDERNAME);
atlasTxtImportWarning = EditorPrefs.GetBool(ATLASTXT_WARNING_KEY, SpinePreferences.DEFAULT_ATLASTXT_WARNING);
@ -246,6 +275,28 @@ namespace Spine.Unity.Editor {
EditorPrefs.SetString(TEXTURE_SETTINGS_REFERENCE_KEY, textureSettingsReference);
}
}
SpineEditorUtilities.MaterialPrefsField(ref blendModeMaterialMultiply, BLEND_MODE_MATERIAL_MULTIPLY_KEY, new GUIContent("Multiply Material", "Multiply blend mode Material template."));
if (string.IsNullOrEmpty(blendModeMaterialMultiply)) {
var blendModeMaterialMultiplyGUIDS = AssetDatabase.FindAssets(DEFAULT_BLEND_MODE_MULTIPLY_MATERIAL);
if (blendModeMaterialMultiplyGUIDS.Length > 0) {
blendModeMaterialMultiply = AssetDatabase.GUIDToAssetPath(blendModeMaterialMultiplyGUIDS[0]);
}
}
SpineEditorUtilities.MaterialPrefsField(ref blendModeMaterialScreen, BLEND_MODE_MATERIAL_SCREEN_KEY, new GUIContent("Screen Material", "Screen blend mode Material template."));
if (string.IsNullOrEmpty(blendModeMaterialScreen)) {
var blendModeMaterialScreenGUIDS = AssetDatabase.FindAssets(DEFAULT_BLEND_MODE_SCREEN_MATERIAL);
if (blendModeMaterialScreenGUIDS.Length > 0) {
blendModeMaterialScreen = AssetDatabase.GUIDToAssetPath(blendModeMaterialScreenGUIDS[0]);
}
}
SpineEditorUtilities.MaterialPrefsField(ref blendModeMaterialAdditive, BLEND_MODE_MATERIAL_ADDITIVE_KEY, new GUIContent("Additive Material", "Additive blend mode Material template."));
if (string.IsNullOrEmpty(blendModeMaterialAdditive)) {
var blendModeMaterialAdditiveGUIDS = AssetDatabase.FindAssets(DEFAULT_BLEND_MODE_ADDITIVE_MATERIAL);
if (blendModeMaterialAdditiveGUIDS.Length > 0) {
blendModeMaterialAdditive = AssetDatabase.GUIDToAssetPath(blendModeMaterialAdditiveGUIDS[0]);
}
}
}
EditorGUILayout.Space();
@ -344,6 +395,16 @@ namespace Spine.Unity.Editor {
}
}
static void MaterialPrefsField (ref string currentValue, string editorPrefsKey, GUIContent label) {
EditorGUI.BeginChangeCheck();
EditorGUIUtility.wideMode = true;
var material = (EditorGUILayout.ObjectField(label, AssetDatabase.LoadAssetAtPath<Material>(currentValue), typeof(Object), false) as Material);
currentValue = material != null ? AssetDatabase.GetAssetPath(material) : "";
if (EditorGUI.EndChangeCheck()) {
EditorPrefs.SetString(editorPrefsKey, currentValue);
}
}
public static void FloatPropertyField (SerializedProperty property, GUIContent label, float min = float.NegativeInfinity, float max = float.PositiveInfinity) {
EditorGUI.BeginChangeCheck();
property.floatValue = EditorGUILayout.DelayedFloatField(label, property.floatValue);
@ -357,6 +418,10 @@ namespace Spine.Unity.Editor {
property.stringValue = shader != null ? shader.name : fallbackShaderName;
}
public static void MaterialPropertyField (SerializedProperty property, GUIContent label) {
var material = (EditorGUILayout.ObjectField(label, AssetDatabase.LoadAssetAtPath<Material>(property.stringValue), typeof(Material), false) as Material);
property.stringValue = material ? AssetDatabase.GetAssetPath(material) : "";
}
#if NEW_PREFERENCES_SETTINGS_PROVIDER
public static void PresetAssetPropertyField (SerializedProperty property, GUIContent label) {

View File

@ -78,6 +78,65 @@ namespace Spine.Unity.Editor {
internal const string DEFAULT_TEXTURE_SETTINGS_REFERENCE = "";
public string textureSettingsReference = DEFAULT_TEXTURE_SETTINGS_REFERENCE;
public bool UsesPMAWorkflow {
get {
return IsPMAWorkflow(textureSettingsReference);
}
}
public static bool IsPMAWorkflow(string textureSettingsReference) {
if (textureSettingsReference == null)
return true;
string settingsReference = textureSettingsReference.ToLower();
if (settingsReference.Contains("straight") || !settingsReference.Contains("pma"))
return false;
return true;
}
public const string DEFAULT_BLEND_MODE_MULTIPLY_MATERIAL = "SkeletonPMAMultiply";
public const string DEFAULT_BLEND_MODE_SCREEN_MATERIAL = "SkeletonPMAScreen";
public const string DEFAULT_BLEND_MODE_ADDITIVE_MATERIAL = "SkeletonPMAAdditive";
public Material blendModeMaterialMultiply = null;
public Material blendModeMaterialScreen = null;
public Material blendModeMaterialAdditive = null;
public string FindPathOfAsset (string assetName) {
string typeSearchString = assetName;
string[] guids = AssetDatabase.FindAssets(typeSearchString);
if (guids.Length > 0) {
return AssetDatabase.GUIDToAssetPath(guids[0]);
}
return null;
}
public Material BlendModeMaterialMultiply {
get {
if (blendModeMaterialMultiply == null) {
string path = FindPathOfAsset(DEFAULT_BLEND_MODE_MULTIPLY_MATERIAL);
blendModeMaterialMultiply = AssetDatabase.LoadAssetAtPath<Material>(path);
}
return blendModeMaterialMultiply;
}
}
public Material BlendModeMaterialScreen {
get {
if (blendModeMaterialScreen == null) {
string path = FindPathOfAsset(DEFAULT_BLEND_MODE_SCREEN_MATERIAL);
blendModeMaterialScreen = AssetDatabase.LoadAssetAtPath<Material>(path);
}
return blendModeMaterialScreen;
}
}
public Material BlendModeMaterialAdditive {
get {
if (blendModeMaterialAdditive == null) {
string path = FindPathOfAsset(DEFAULT_BLEND_MODE_ADDITIVE_MATERIAL);
blendModeMaterialAdditive = AssetDatabase.LoadAssetAtPath<Material>(path);
}
return blendModeMaterialAdditive;
}
}
internal const bool DEFAULT_ATLASTXT_WARNING = true;
public bool atlasTxtImportWarning = DEFAULT_ATLASTXT_WARNING;
@ -180,6 +239,10 @@ namespace Spine.Unity.Editor {
textureSettingsRef.stringValue = AssetDatabase.GUIDToAssetPath(pmaTextureSettingsReferenceGUIDS[0]);
}
}
EditorGUILayout.PropertyField(settings.FindProperty("blendModeMaterialMultiply"), new GUIContent("Multiply Material", "Multiply blend mode Material template."));
EditorGUILayout.PropertyField(settings.FindProperty("blendModeMaterialScreen"), new GUIContent("Screen Material", "Screen blend mode Material template."));
EditorGUILayout.PropertyField(settings.FindProperty("blendModeMaterialAdditive"), new GUIContent("Additive Material", "Additive blend mode Material template."));
}
EditorGUILayout.Space();

View File

@ -0,0 +1,144 @@
/******************************************************************************
* 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.
*****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using Spine;
namespace Spine.Unity {
[System.Serializable]
public class BlendModeMaterials {
[System.Serializable]
public class ReplacementMaterial {
public string pageName;
public Material material;
}
[SerializeField, HideInInspector] protected bool requiresBlendModeMaterials = false;
public bool applyAdditiveMaterial = false;
public List<ReplacementMaterial> additiveMaterials = new List<ReplacementMaterial>();
public List<ReplacementMaterial> multiplyMaterials = new List<ReplacementMaterial>();
public List<ReplacementMaterial> screenMaterials = new List<ReplacementMaterial>();
public bool RequiresBlendModeMaterials { get { return requiresBlendModeMaterials; } set { requiresBlendModeMaterials = value; } }
#if UNITY_EDITOR
public void TransferSettingsFrom (BlendModeMaterialsAsset modifierAsset) {
applyAdditiveMaterial = modifierAsset.applyAdditiveMaterial;
}
public bool UpdateBlendmodeMaterialsRequiredState (SkeletonData skeletonData) {
requiresBlendModeMaterials = false;
if (skeletonData == null) throw new ArgumentNullException("skeletonData");
var skinEntries = new List<Skin.SkinEntry>();
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;
skinEntries.Clear();
foreach (var skin in skeletonData.Skins)
skin.GetAttachments(slotIndex, skinEntries);
foreach (var entry in skinEntries) {
if (entry.Attachment is IHasRendererObject) {
requiresBlendModeMaterials = true;
return true;
}
}
}
return false;
}
#endif
public void ApplyMaterials (SkeletonData skeletonData) {
if (skeletonData == null) throw new ArgumentNullException("skeletonData");
if (!requiresBlendModeMaterials)
return;
var skinEntries = new List<Skin.SkinEntry>();
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<ReplacementMaterial> replacementMaterials = null;
switch (slot.blendMode) {
case BlendMode.Multiply:
replacementMaterials = multiplyMaterials;
break;
case BlendMode.Screen:
replacementMaterials = screenMaterials;
break;
case BlendMode.Additive:
replacementMaterials = additiveMaterials;
break;
}
if (replacementMaterials == null)
continue;
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) {
renderableAttachment.RendererObject = CloneAtlasRegionWithMaterial(
(AtlasRegion)renderableAttachment.RendererObject, replacementMaterials);
}
}
}
}
protected AtlasRegion CloneAtlasRegionWithMaterial (AtlasRegion originalRegion, List<ReplacementMaterial> replacementMaterials) {
var newRegion = originalRegion.Clone();
Material material = null;
foreach (var replacement in replacementMaterials) {
if (replacement.pageName == originalRegion.page.name) {
material = replacement.material;
break;
}
}
AtlasPage originalPage = originalRegion.page;
var newPage = originalPage.Clone();
newPage.rendererObject = material;
newRegion.page = newPage;
return newRegion;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d03ca55657e89b949a4c07bc9207beac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -49,6 +49,9 @@ namespace Spine.Unity {
#endif
public TextAsset skeletonJSON;
public bool isUpgradingBlendModeMaterials = false;
public BlendModeMaterials blendModeMaterials = new BlendModeMaterials();
[Tooltip("Use SkeletonDataModifierAssets to apply changes to the SkeletonData after being loaded, such as apply blend mode Materials to Attachments under slots with special blend modes.")]
public List<SkeletonDataModifierAsset> skeletonDataModifiers = new List<SkeletonDataModifierAsset>();
@ -187,10 +190,14 @@ namespace Spine.Unity {
return null;
if (skeletonDataModifiers != null) {
foreach (var m in skeletonDataModifiers) {
if (m != null) m.Apply(loadedSkeletonData);
foreach (var modifier in skeletonDataModifiers) {
if (modifier != null && !(isUpgradingBlendModeMaterials && modifier is BlendModeMaterialsAsset)) {
modifier.Apply(loadedSkeletonData);
}
}
}
if (!isUpgradingBlendModeMaterials)
blendModeMaterials.ApplyMaterials(loadedSkeletonData);
this.InitializeWithData(loadedSkeletonData);