diff --git a/spine-unity/Assets/spine-unity/Ghost.meta b/spine-unity/Assets/spine-unity/Ghost.meta new file mode 100644 index 000000000..cc485896c --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ghost.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 13193c9d213765f4c85f4c1faa615711 +folderAsset: yes +DefaultImporter: + userData: diff --git a/spine-unity/Assets/spine-unity/Ghost/Shaders.meta b/spine-unity/Assets/spine-unity/Ghost/Shaders.meta new file mode 100644 index 000000000..0e0bd214c --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ghost/Shaders.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: a0cee0de78cef7440ae0b5aac39ae971 +folderAsset: yes +DefaultImporter: + userData: diff --git a/spine-unity/Assets/spine-unity/Ghost/Shaders/SkeletonGhost.shader b/spine-unity/Assets/spine-unity/Ghost/Shaders/SkeletonGhost.shader new file mode 100644 index 000000000..1494a89d6 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ghost/Shaders/SkeletonGhost.shader @@ -0,0 +1,92 @@ +//Shader written by Alex Dixon +Shader "Spine/SkeletonGhost" +{ + Properties + { + _Color ("Main Color", Color) = (1,1,1,1) + _MainTex ("Base (RGB) Alpha (A)", 2D) = "white" {} + _TextureFade ("Texture Fade Out", Range(0,1)) = 0 + } + SubShader + { + + Tags {"Queue"="Transparent" "IgnoreProjector"="False" "RenderType"="Transparent"} + Fog { Mode Off } + Blend One OneMinusSrcAlpha + ZWrite Off + Cull Off + + Pass + { + Tags {"LightMode" = "Always"} // This Pass tag is important or Unity may not give it the correct light information. + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + //#pragma multi_compile_fwdbase // This line tells Unity to compile this pass for forward base. + + #include "UnityCG.cginc" + //#include "AutoLight.cginc" + + struct vertex_input + { + float4 vertex : POSITION; + float2 texcoord : TEXCOORD0; + float4 color : COLOR; + + }; + + struct vertex_output + { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + + float4 color : COLOR; + }; + + sampler2D _MainTex; + fixed4 _Color; + fixed _TextureFade; + + vertex_output vert (vertex_input v) + { + vertex_output o; + o.pos = mul( UNITY_MATRIX_MVP, v.vertex); + o.uv = v.texcoord.xy; + o.color = v.color; + + + return o; + } + + fixed4 frag(vertex_output i) : COLOR + { + fixed4 tex = tex2D(_MainTex, i.uv); + + tex = fixed4(max(_TextureFade, tex.r), max(_TextureFade, tex.g), max(_TextureFade, tex.b), tex.a); + + return tex * ((i.color * _Color) * tex.a); + + + + //float finalAlpha = tex.a * i.color.a * _Color.a; + + /* + TODO: Add basic lighting stuff in later? + + fixed4 c; + c.rgb = (UNITY_LIGHTMODEL_AMBIENT.rgb * tex.rgb); // Ambient term. Only do this in Forward Base. It only needs calculating once. + c.rgb += tex.rgb; // Diffuse and specular. + //Unity 4: c.rgb = (UNITY_LIGHTMODEL_AMBIENT.rgb * tex.rgb * 2); // Ambient term. Only do this in Forward Base. It only needs calculating once. + //Unity 4: c.rgb += (tex.rgb * _LightColor0.rgb * diff) * (atten * 2); // Diffuse and specular. + c.a = tex.a; // + _LightColor0.a * atten; + + return c; + */ + } + ENDCG + } + + + } + //FallBack "Transparent/Cutout/VertexLit" // Use VertexLit's shadow caster/receiver passes. +} \ No newline at end of file diff --git a/spine-unity/Assets/spine-unity/Ghost/Shaders/SkeletonGhost.shader.meta b/spine-unity/Assets/spine-unity/Ghost/Shaders/SkeletonGhost.shader.meta new file mode 100644 index 000000000..d24d9a340 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ghost/Shaders/SkeletonGhost.shader.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3873d4699ee8a4b4da8fa6b8c229b94d +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Ghost/SkeletonGhost.cs b/spine-unity/Assets/spine-unity/Ghost/SkeletonGhost.cs new file mode 100644 index 000000000..fe827aebe --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ghost/SkeletonGhost.cs @@ -0,0 +1,147 @@ +/***************************************************************************** + * SkeletonGhost created by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ + +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +[RequireComponent(typeof(SkeletonRenderer))] +public class SkeletonGhost : MonoBehaviour { + public bool ghostingEnabled = true; + public float spawnRate = 0.05f; + public Color32 color = new Color32(0xFF, 0xFF, 0xFF, 0x00); + [Tooltip("Remember to set color alpha to 0 if Additive is true")] + public bool additive = true; + public int maximumGhosts = 10; + public float fadeSpeed = 10; + public Shader ghostShader; + [Tooltip("0 is Color and Alpha, 1 is Alpha only.")] + [Range(0, 1)] + public float textureFade = 1; + + float nextSpawnTime; + SkeletonGhostRenderer[] pool; + int poolIndex = 0; + SkeletonRenderer skeletonRenderer; + MeshRenderer meshRenderer; + MeshFilter meshFilter; + + + Dictionary materialTable = new Dictionary(); + + void Start () { + if (ghostShader == null) + ghostShader = Shader.Find("Spine/SkeletonGhost"); + + skeletonRenderer = GetComponent(); + meshFilter = GetComponent(); + meshRenderer = GetComponent(); + nextSpawnTime = Time.time + spawnRate; + pool = new SkeletonGhostRenderer[maximumGhosts]; + for (int i = 0; i < maximumGhosts; i++) { + GameObject go = new GameObject(gameObject.name + " Ghost", typeof(SkeletonGhostRenderer)); + pool[i] = go.GetComponent(); + go.SetActive(false); + go.hideFlags = HideFlags.HideInHierarchy; + } + + if (skeletonRenderer is SkeletonAnimation) + ((SkeletonAnimation)skeletonRenderer).state.Event += OnEvent; + + } + + //SkeletonAnimation + /* + * Int Value: 0 sets ghostingEnabled to false, 1 sets ghostingEnabled to true + * Float Value: Values greater than 0 set the spawnRate equal the float value + * String Value: Pass RGBA hex color values in to set the color property. IE: "A0FF8BFF" + */ + void OnEvent (Spine.AnimationState state, int trackIndex, Spine.Event e) { + if (e.Data.Name == "Ghosting") { + ghostingEnabled = e.Int > 0; + if (e.Float > 0) + spawnRate = e.Float; + if (e.String != null) { + this.color = HexToColor(e.String); + } + } + } + + //SkeletonAnimator + //SkeletonAnimator or Mecanim based animations only support toggling ghostingEnabled. Be sure not to set anything other than the Int param in Spine or String will take priority. + void Ghosting (float val) { + ghostingEnabled = val > 0; + } + + void Update () { + if (!ghostingEnabled) + return; + + if (Time.time >= nextSpawnTime) { + GameObject go = pool[poolIndex].gameObject; + + Material[] materials = meshRenderer.sharedMaterials; + for (int i = 0; i < materials.Length; i++) { + var originalMat = materials[i]; + Material ghostMat; + if (!materialTable.ContainsKey(originalMat)) { + ghostMat = new Material(originalMat); + ghostMat.shader = ghostShader; + ghostMat.color = Color.white; + if (ghostMat.HasProperty("_TextureFade")) + ghostMat.SetFloat("_TextureFade", textureFade); + materialTable.Add(originalMat, ghostMat); + } else { + ghostMat = materialTable[originalMat]; + } + + materials[i] = ghostMat; + } + + pool[poolIndex].Initialize(meshFilter.sharedMesh, materials, color, additive, fadeSpeed, meshRenderer.sortingOrder - 1); + go.transform.parent = transform; + + go.transform.localPosition = Vector3.zero; + go.transform.localRotation = Quaternion.identity; + go.transform.localScale = Vector3.one; + + go.transform.parent = null; + + poolIndex++; + + if (poolIndex == pool.Length) + poolIndex = 0; + + nextSpawnTime = Time.time + spawnRate; + } + } + + void OnDestroy () { + for (int i = 0; i < maximumGhosts; i++) { + if (pool[i] != null) + pool[i].Cleanup(); + } + + foreach (var mat in materialTable.Values) + Destroy(mat); + } + + + //based on UnifyWiki http://wiki.unity3d.com/index.php?title=HexConverter + static Color32 HexToColor (string hex) { + if (hex.Length < 6) + return Color.magenta; + + hex = hex.Replace("#", ""); + byte r = byte.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); + byte g = byte.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); + byte b = byte.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); + byte a = 0xFF; + if (hex.Length == 8) + a = byte.Parse(hex.Substring(6, 2), System.Globalization.NumberStyles.HexNumber); + + return new Color32(r, g, b, a); + } +} diff --git a/spine-unity/Assets/spine-unity/Ghost/SkeletonGhost.cs.meta b/spine-unity/Assets/spine-unity/Ghost/SkeletonGhost.cs.meta new file mode 100644 index 000000000..32cd44c59 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ghost/SkeletonGhost.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 02f2fa991881c6d419500ccc40ad443f +timeCreated: 1431858330 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: + - ghostShader: {fileID: 4800000, guid: 3873d4699ee8a4b4da8fa6b8c229b94d, type: 3} + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/spine-unity/Ghost/SkeletonGhostRenderer.cs b/spine-unity/Assets/spine-unity/Ghost/SkeletonGhostRenderer.cs new file mode 100644 index 000000000..ee86a3920 --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ghost/SkeletonGhostRenderer.cs @@ -0,0 +1,109 @@ +/***************************************************************************** + * SkeletonGhostRenderer created by Mitch Thompson + * Full irrevocable rights and permissions granted to Esoteric Software +*****************************************************************************/ + +using UnityEngine; +using System.Collections; + +public class SkeletonGhostRenderer : MonoBehaviour { + + public float fadeSpeed = 10; + + Color32[] colors; + Color32 black = new Color32(0, 0, 0, 0); + MeshFilter meshFilter; + MeshRenderer meshRenderer; + + void Awake () { + meshRenderer = gameObject.AddComponent(); + meshFilter = gameObject.AddComponent(); + } + + public void Initialize (Mesh mesh, Material[] materials, Color32 color, bool additive, float speed, int sortingOrder) { + StopAllCoroutines(); + + gameObject.SetActive(true); + + + meshRenderer.sharedMaterials = materials; + meshRenderer.sortingOrder = sortingOrder; + + meshFilter.sharedMesh = (Mesh)Instantiate(mesh); + + colors = meshFilter.sharedMesh.colors32; + + if ((color.a + color.r + color.g + color.b) > 0) { + for (int i = 0; i < colors.Length; i++) { + colors[i] = color; + } + } + + fadeSpeed = speed; + + if (additive) + StartCoroutine(FadeAdditive()); + else + StartCoroutine(Fade()); + } + + IEnumerator Fade () { + Color32 c; + for (int t = 0; t < 500; t++) { + + bool breakout = true; + for (int i = 0; i < colors.Length; i++) { + c = colors[i]; + if (c.a > 0) + breakout = false; + + colors[i] = Color32.Lerp(c, black, Time.deltaTime * fadeSpeed); + } + + meshFilter.sharedMesh.colors32 = colors; + + if (breakout) + break; + yield return null; + } + + Destroy(meshFilter.sharedMesh); + + gameObject.SetActive(false); + } + + IEnumerator FadeAdditive () { + Color32 c; + Color32 black = this.black; + + for (int t = 0; t < 500; t++) { + + bool breakout = true; + for (int i = 0; i < colors.Length; i++) { + c = colors[i]; + black.a = c.a; + if (c.r > 0 || c.g > 0 || c.b > 0) + breakout = false; + + colors[i] = Color32.Lerp(c, black, Time.deltaTime * fadeSpeed); + } + + meshFilter.sharedMesh.colors32 = colors; + + if (breakout) + break; + yield return null; + } + + Destroy(meshFilter.sharedMesh); + + gameObject.SetActive(false); + } + + public void Cleanup () { + if (meshFilter != null && meshFilter.sharedMesh != null) + Destroy(meshFilter.sharedMesh); + + Destroy(gameObject); + } +} diff --git a/spine-unity/Assets/spine-unity/Ghost/SkeletonGhostRenderer.cs.meta b/spine-unity/Assets/spine-unity/Ghost/SkeletonGhostRenderer.cs.meta new file mode 100644 index 000000000..2f9fb1a1a --- /dev/null +++ b/spine-unity/Assets/spine-unity/Ghost/SkeletonGhostRenderer.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 58e3a9b80754b7545a1dff4d8475b51f +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: