[unity] BlendModeMaterials Asset.

This commit is contained in:
pharan 2018-09-08 21:13:57 +08:00
parent 514450f3a5
commit 7b631b2961
21 changed files with 359 additions and 24 deletions

View File

@ -51,6 +51,7 @@ namespace Spine.Unity.Editor {
internal static bool showAttachments = false;
SerializedProperty atlasAssets, skeletonJSON, scale, fromAnimation, toAnimation, duration, defaultMix;
SerializedProperty blendModeMaterials;
#if SPINE_TK2D
SerializedProperty spriteCollection;
#endif
@ -100,6 +101,8 @@ namespace Spine.Unity.Editor {
duration = serializedObject.FindProperty("duration");
defaultMix = serializedObject.FindProperty("defaultMix");
blendModeMaterials = serializedObject.FindProperty("blendModeMaterials");
#if SPINE_SKELETON_MECANIM
controller = serializedObject.FindProperty("controller");
#endif
@ -315,6 +318,8 @@ namespace Spine.Unity.Editor {
if (atlasAssets.arraySize == 0)
EditorGUILayout.HelpBox("AtlasAssets array is empty. Skeleton's attachments will load without being mapped to images.", MessageType.Info);
EditorGUILayout.PropertyField(blendModeMaterials);
}
void HandleAtlasAssetsNulls () {

View File

@ -179,7 +179,7 @@ namespace Spine.Unity.Editor {
static void Initialize () {
Preferences.Load();
DirectoryInfo rootDir = new DirectoryInfo(Application.dataPath);
var rootDir = new DirectoryInfo(Application.dataPath);
FileInfo[] files = rootDir.GetFiles("SpineEditorUtilities.cs", SearchOption.AllDirectories);
editorPath = Path.GetDirectoryName(files[0].FullName.Replace("\\", "/").Replace(Application.dataPath, "Assets"));
editorGUIPath = editorPath + "/GUI";
@ -194,15 +194,27 @@ namespace Spine.Unity.Editor {
EditorApplication.hierarchyWindowItemOnGUI += HierarchyHandler.HandleDragAndDrop;
// Hierarchy Icons
#if UNITY_2017_2_OR_NEWER
#if UNITY_2017_2_OR_NEWER
EditorApplication.playModeStateChanged -= HierarchyHandler.IconsOnPlaymodeStateChanged;
EditorApplication.playModeStateChanged += HierarchyHandler.IconsOnPlaymodeStateChanged;
HierarchyHandler.IconsOnPlaymodeStateChanged(PlayModeStateChange.EnteredEditMode);
#else
#else
EditorApplication.playmodeStateChanged -= HierarchyHandler.IconsOnPlaymodeStateChanged;
EditorApplication.playmodeStateChanged += HierarchyHandler.IconsOnPlaymodeStateChanged;
HierarchyHandler.IconsOnPlaymodeStateChanged();
#endif
#endif
// Data Refresh Edit Mode.
// This prevents deserialized SkeletonData from persisting from play mode to edit mode.
#if UNITY_2017_2_OR_NEWER
EditorApplication.playModeStateChanged -= DataReloadHandler.OnPlaymodeStateChanged;
EditorApplication.playModeStateChanged += DataReloadHandler.OnPlaymodeStateChanged;
DataReloadHandler.OnPlaymodeStateChanged(PlayModeStateChange.EnteredEditMode);
#else
EditorApplication.playmodeStateChanged -= DataReloadHandler.OnPlaymodeStateChanged;
EditorApplication.playmodeStateChanged += DataReloadHandler.OnPlaymodeStateChanged;
DataReloadHandler.OnPlaymodeStateChanged();
#endif
initialized = true;
}
@ -214,11 +226,11 @@ namespace Spine.Unity.Editor {
#endregion
public static class Preferences {
#if SPINE_TK2D
#if SPINE_TK2D
const float DEFAULT_DEFAULT_SCALE = 1f;
#else
#else
const float DEFAULT_DEFAULT_SCALE = 0.01f;
#endif
#endif
const string DEFAULT_SCALE_KEY = "SPINE_DEFAULT_SCALE";
public static float defaultScale = DEFAULT_DEFAULT_SCALE;
@ -268,11 +280,11 @@ namespace Spine.Unity.Editor {
showHierarchyIcons = EditorGUILayout.Toggle(new GUIContent("Show Hierarchy Icons", "Show relevant icons on GameObjects with Spine Components on them. Disable this if you have large, complex scenes."), showHierarchyIcons);
if (EditorGUI.EndChangeCheck()) {
EditorPrefs.SetBool(SHOW_HIERARCHY_ICONS_KEY, showHierarchyIcons);
#if UNITY_2017_2_OR_NEWER
#if UNITY_2017_2_OR_NEWER
HierarchyHandler.IconsOnPlaymodeStateChanged(PlayModeStateChange.EnteredEditMode);
#else
#else
HierarchyHandler.IconsOnPlaymodeStateChanged();
#endif
#endif
}
EditorGUILayout.Separator();
@ -333,6 +345,38 @@ namespace Spine.Unity.Editor {
}
}
public static class DataReloadHandler {
#if UNITY_2017_2_OR_NEWER
internal static void OnPlaymodeStateChanged (PlayModeStateChange stateChange) {
#else
internal static void OnPlaymodeStateChanged () {
#endif
ReloadAllActiveSkeletons();
}
static void ReloadAllActiveSkeletons () {
var skeletonDataAssetsToReload = new HashSet<SkeletonDataAsset>();
var activeSkeletonRenderers = GameObject.FindObjectsOfType<SkeletonRenderer>();
foreach (var sr in activeSkeletonRenderers) {
var skeletonDataAsset = sr.skeletonDataAsset;
if (skeletonDataAsset != null) skeletonDataAssetsToReload.Add(skeletonDataAsset);
}
var activeSkeletonGraphics = GameObject.FindObjectsOfType<SkeletonGraphic>();
foreach (var sg in activeSkeletonGraphics) {
var skeletonDataAsset = sg.skeletonDataAsset;
if (skeletonDataAsset != null) skeletonDataAssetsToReload.Add(skeletonDataAsset);
}
foreach (var sda in skeletonDataAssetsToReload)
sda.Clear();
foreach (var sr in activeSkeletonRenderers) sr.Initialize(true);
foreach (var sg in activeSkeletonGraphics) sg.Initialize(true);
}
}
public static class AssetUtility {
public const string SkeletonDataSuffix = "_SkeletonData";
public const string AtlasSuffix = "_Atlas";

View File

@ -0,0 +1,120 @@
/******************************************************************************
* Spine Runtimes Software License v2.5
*
* Copyright (c) 2013-2016, Esoteric Software
* All rights reserved.
*
* You are granted a perpetual, non-exclusive, non-sublicensable, and
* non-transferable license to use, install, execute, and perform the Spine
* Runtimes software and derivative works solely for personal or internal
* use. Without the written permission of Esoteric Software (see Section 2 of
* the Spine Software License Agreement), you may not (a) modify, translate,
* adapt, or develop new applications using the Spine Runtimes or otherwise
* create derivative works or improvements of the Spine Runtimes or (b) remove,
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
* or other intellectual property or proprietary rights notices on or in the
* Software, including any copy thereof. Redistributions in binary or source
* form must include this license and terms.
*
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 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 THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Spine;
using Spine.Unity;
namespace Spine.Unity {
[CreateAssetMenu(menuName = "Spine/Blend Mode Materials Asset", order = 200)]
public class BlendModeMaterialsAsset : ScriptableObject {
public Material multiplyMaterialTemplate;
public Material screenMaterialTemplate;
public Material additiveMaterialTemplate;
public bool applyAdditiveMaterial;
public void Apply (SkeletonData skeletonData) {
ApplyMaterials(skeletonData, multiplyMaterialTemplate, screenMaterialTemplate, additiveMaterialTemplate, applyAdditiveMaterial);
}
public static void ApplyMaterials (SkeletonData skeletonData, Material multiplyTemplate, Material screenTemplate, Material additiveTemplate, bool includeAdditiveSlots) {
if (skeletonData == null) throw new ArgumentNullException("skeletonData");
var atlasPageMaterialCache = new Dictionary<KeyValuePair<AtlasPage, Material>, AtlasPage>();
var attachmentBuffer = new List<Attachment>();
var slotsItems = skeletonData.Slots.Items;
for (int i = 0, slotCount = skeletonData.Slots.Count; i < slotCount; i++) {
var slot = slotsItems[i];
if (slot.blendMode == BlendMode.Normal) continue;
if (!includeAdditiveSlots && slot.blendMode == BlendMode.Additive) continue;
attachmentBuffer.Clear();
foreach (var skin in skeletonData.Skins)
skin.FindAttachmentsForSlot(i, attachmentBuffer);
Material templateMaterial = null;
switch (slot.blendMode) {
case BlendMode.Multiply:
templateMaterial = multiplyTemplate;
break;
case BlendMode.Screen:
templateMaterial = screenTemplate;
break;
case BlendMode.Additive:
templateMaterial = additiveTemplate;
break;
}
if (templateMaterial == null) continue;
foreach (var attachment in attachmentBuffer) {
var renderableAttachment = attachment as IHasRendererObject;
if (renderableAttachment != null) {
renderableAttachment.RendererObject = AtlasRegionCloneWithMaterial((AtlasRegion)renderableAttachment.RendererObject, templateMaterial, atlasPageMaterialCache);
}
}
}
//atlasPageMaterialCache.Clear();
//attachmentBuffer.Clear();
}
static AtlasRegion AtlasRegionCloneWithMaterial (AtlasRegion originalRegion, Material materialTemplate, Dictionary<KeyValuePair<AtlasPage, Material>, AtlasPage> cache) {
var newRegion = originalRegion.Clone();
newRegion.page = GetAtlasPageWithMaterial(originalRegion.page, materialTemplate, cache);
return newRegion;
}
static AtlasPage GetAtlasPageWithMaterial (AtlasPage originalPage, Material materialTemplate, Dictionary<KeyValuePair<AtlasPage, Material>, AtlasPage> cache) {
if (originalPage == null) throw new ArgumentNullException("originalPage");
AtlasPage newPage = null;
var key = new KeyValuePair<AtlasPage, Material>(originalPage, materialTemplate);
cache.TryGetValue(key, out newPage);
if (newPage == null) {
newPage = originalPage.Clone();
var originalMaterial = originalPage.rendererObject as Material;
newPage.rendererObject = new Material(materialTemplate) {
name = originalMaterial.name + " " + materialTemplate.name,
mainTexture = originalMaterial.mainTexture
};
cache.Add(key, newPage);
}
return newPage;
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 12b0b98acbcda44468a7ae4e35000abe
timeCreated: 1536404384
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences:
- multiplyMaterialTemplate: {fileID: 2100000, guid: 53bf0ab317d032d418cf1252d68f51df,
type: 2}
- screenMaterialTemplate: {fileID: 2100000, guid: 73f0f46d3177c614baf0fa48d646a9be,
type: 2}
- additiveMaterialTemplate: {fileID: 2100000, guid: 4deba332d47209e4780b3c5fcf0e3745,
type: 2}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -31,6 +31,7 @@
using System;
using System.IO;
using UnityEngine;
using Spine;
namespace Spine.Unity {
@ -39,6 +40,8 @@ namespace Spine.Unity {
public class SkeletonDataAsset : ScriptableObject {
#region Inspector
public AtlasAssetBase[] atlasAssets = new AtlasAssetBase[0];
public BlendModeMaterialsAsset blendModeMaterials;
#if SPINE_TK2D
public tk2dSpriteCollectionData spriteCollection;
public float scale = 1f;
@ -87,6 +90,7 @@ namespace Spine.Unity {
}
#endregion
/// <summary>Clears the loaded SkeletonData and AnimationStateData. Use this to force a reload for the next time GetSkeletonData is called.</summary>
public void Clear () {
skeletonData = null;
stateData = null;
@ -161,6 +165,9 @@ namespace Spine.Unity {
}
if (blendModeMaterials != null)
blendModeMaterials.Apply(loadedSkeletonData);
this.InitializeWithData(loadedSkeletonData);
return skeletonData;
@ -218,6 +225,7 @@ namespace Spine.Unity {
GetSkeletonData(false);
return stateData;
}
}
}

View File

@ -69,7 +69,7 @@ namespace Spine.Unity.Modules.AttachmentTools {
/// <summary>Sets the region (image) of a RegionAttachment</summary>
public static void SetRegion (this RegionAttachment attachment, AtlasRegion region, bool updateOffset = true) {
if (region == null) throw new System.ArgumentNullException("region");
if (region == null) throw new System.ArgumentNullException("region");
// (AtlasAttachmentLoader.cs)
attachment.RendererObject = region;
@ -86,7 +86,7 @@ namespace Spine.Unity.Modules.AttachmentTools {
/// <summary>Sets the region (image) of a MeshAttachment</summary>
public static void SetRegion (this MeshAttachment attachment, AtlasRegion region, bool updateUVs = true) {
if (region == null) throw new System.ArgumentNullException("region");
if (region == null) throw new System.ArgumentNullException("region");
// (AtlasAttachmentLoader.cs)
attachment.RendererObject = region;
@ -395,7 +395,7 @@ namespace Spine.Unity.Modules.AttachmentTools {
/// 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>
/// <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.</param>
///
///
/// <param name="materialPropertySource">May be null. If no Material property source is provided, no special </param>
public static void GetRepackedAttachments (List<Attachment> sourceAttachments, List<Attachment> outputAttachments, Material materialPropertySource, out Material outputMaterial, out Texture2D outputTexture, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, string newAssetName = "Repacked Attachments", bool clearCache = false, bool useOriginalNonrenderables = true) {
if (sourceAttachments == null) throw new System.ArgumentNullException("sourceAttachments");
@ -413,7 +413,7 @@ namespace Spine.Unity.Modules.AttachmentTools {
int newRegionIndex = 0;
for (int i = 0, n = sourceAttachments.Count; i < n; i++) {
var originalAttachment = sourceAttachments[i];
if (IsRenderable(originalAttachment)) {
var newAttachment = originalAttachment.GetClone(true);
var region = newAttachment.GetRegion();
@ -524,7 +524,7 @@ namespace Spine.Unity.Modules.AttachmentTools {
newSkin.AddAttachment(originalKey.slotIndex, originalKey.name, newAttachment);
} else {
newSkin.AddAttachment(originalKey.slotIndex, originalKey.name, useOriginalNonrenderables ? originalAttachment : originalAttachment.GetClone(true));
}
}
}
// Fill a new texture with the collected attachment textures.
@ -657,7 +657,7 @@ namespace Spine.Unity.Modules.AttachmentTools {
/// Returns a Rect of the AtlasRegion according to Spine texture coordinates. (x-right, y-down)</summary>
static Rect GetSpineAtlasRect (this AtlasRegion region, bool includeRotate = true) {
if (includeRotate && region.rotate)
return new Rect(region.x, region.y, region.height, region.width);
return new Rect(region.x, region.y, region.height, region.width);
else
return new Rect(region.x, region.y, region.width, region.height);
}
@ -684,7 +684,7 @@ namespace Spine.Unity.Modules.AttachmentTools {
/// <summary>
/// Creates a new Spine AtlasRegion according to a Unity UV Rect (x-right, y-up, uv-normalized).</summary>
static AtlasRegion UVRectToAtlasRegion (Rect uvRect, string name, AtlasPage page, float offsetX, float offsetY, bool rotate) {
static AtlasRegion UVRectToAtlasRegion (Rect uvRect, string name, AtlasPage page, float offsetX, float offsetY, bool rotate) {
var tr = UVRectToTextureRect(uvRect, page.width, page.height);
var rr = tr.SpineUnityFlipRect(page.height);
@ -875,7 +875,7 @@ namespace Spine.Unity.Modules.AttachmentTools {
public static Attachment GetClone (this Attachment o, bool cloneMeshesAsLinked) {
var regionAttachment = o as RegionAttachment;
if (regionAttachment != null)
return regionAttachment.GetClone();
return regionAttachment.GetClone();
var meshAttachment = o as MeshAttachment;
if (meshAttachment != null)

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: baf1d09e18b500d41a714f6207ddda2d
folderAsset: yes
timeCreated: 1536402197
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 22c0225612a65ee4fb15bad49f644762
timeCreated: 1536404361
licenseType: Pro
NativeFormatImporter:
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4deba332d47209e4780b3c5fcf0e3745
timeCreated: 1496447909
licenseType: Free
NativeFormatImporter:
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
// Spine/Skeleton PMA Screen
// - single color multiply tint
// - unlit
// - Premultiplied alpha Multiply blending
// - No depth, no backface culling, no fog.
// - ShadowCaster pass
Shader "Spine/Blend Modes/Skeleton PMA Additive" {
Properties {
_Color ("Tint Color", Color) = (1,1,1,1)
[NoScaleOffset] _MainTex ("MainTex", 2D) = "black" {}
[Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0
_Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1
}
SubShader {
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
LOD 100
Fog { Mode Off }
Cull Off
ZWrite Off
Blend One One
Lighting Off
Pass {
CGPROGRAM
#pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
uniform float4 _Color;
struct VertexInput {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 vertexColor : COLOR;
};
struct VertexOutput {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 vertexColor : COLOR;
};
VertexOutput vert (VertexInput v) {
VertexOutput o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.vertexColor = v.vertexColor * float4(_Color.rgb * _Color.a, _Color.a); // Combine a PMA version of _Color with vertexColor.
return o;
}
float4 frag (VertexOutput i) : COLOR {
float4 texColor = tex2D(_MainTex, i.uv);
#if defined(_STRAIGHT_ALPHA_INPUT)
texColor.rgb *= texColor.a;
#endif
return (texColor * i.vertexColor);
}
ENDCG
}
Pass {
Name "Caster"
Tags { "LightMode"="ShadowCaster" }
Offset 1, 1
ZWrite On
ZTest LEqual
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
struct v2f {
V2F_SHADOW_CASTER;
float2 uv : TEXCOORD1;
};
uniform float4 _MainTex_ST;
v2f vert (appdata_base v) {
v2f o;
TRANSFER_SHADOW_CASTER(o)
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
float4 frag (v2f i) : COLOR {
fixed4 texcol = tex2D(_MainTex, i.uv);
clip(texcol.a - _Cutoff);
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 53efa1d97f5d9f74285d4330cda14e36
timeCreated: 1496446742
licenseType: Free
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,13 +1,11 @@
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
// Spine/Skeleton PMA Multiply
// Spine/Skeleton PMA Multiply
// - single color multiply tint
// - unlit
// - Premultiplied alpha Multiply blending
// - No depth, no backface culling, no fog.
// - ShadowCaster pass
Shader "Spine/Skeleton PMA Multiply" {
Shader "Spine/Blend Modes/Skeleton PMA Multiply" {
Properties {
_Color ("Tint Color", Color) = (1,1,1,1)
[NoScaleOffset] _MainTex ("MainTex", 2D) = "black" {}

View File

@ -1,11 +1,11 @@
// Spine/Skeleton PMA Multiply
// Spine/Skeleton PMA Screen
// - single color multiply tint
// - unlit
// - Premultiplied alpha Multiply blending
// - No depth, no backface culling, no fog.
// - ShadowCaster pass
Shader "Spine/Skeleton PMA Screen" {
Shader "Spine/Blend Modes/Skeleton PMA Screen" {
Properties {
_Color ("Tint Color", Color) = (1,1,1,1)
[NoScaleOffset] _MainTex ("MainTex", 2D) = "black" {}