提交Spine插件

This commit is contained in:
YiHan0621
2025-08-28 18:32:49 +08:00
parent 254a40d87d
commit 316427ebef
970 changed files with 139525 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 1dc4b7c23385e8c43ad19d01cbed78ce
folderAsset: yes
timeCreated: 1455489521
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#define AUTOINIT_SPINEREFERENCE
using UnityEngine;
namespace Spine.Unity {
[CreateAssetMenu(menuName = "Spine/Animation Reference Asset", order = 100)]
public class AnimationReferenceAsset : ScriptableObject, IHasSkeletonDataAsset {
const bool QuietSkeletonData = true;
[SerializeField] protected SkeletonDataAsset skeletonDataAsset;
[SerializeField, SpineAnimation] protected string animationName;
private Animation animation;
public SkeletonDataAsset SkeletonDataAsset { get { return skeletonDataAsset; } }
public Animation Animation {
get {
#if AUTOINIT_SPINEREFERENCE
if (animation == null)
Initialize();
#endif
return animation;
}
}
public void Initialize () {
if (skeletonDataAsset == null) return;
this.animation = skeletonDataAsset.GetSkeletonData(AnimationReferenceAsset.QuietSkeletonData).FindAnimation(animationName);
if (this.animation == null) Debug.LogWarningFormat("Animation '{0}' not found in SkeletonData : {1}.", animationName, skeletonDataAsset.name);
}
public static implicit operator Animation (AnimationReferenceAsset asset) {
return asset.Animation;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6e3e95a05e4c9774397eeeb7bdee8ccb
timeCreated: 1523328498
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 52b12ec801461494185a4d3dc66f3d1d, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,44 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
public abstract class AtlasAssetBase : ScriptableObject {
public abstract Material PrimaryMaterial { get; }
public abstract IEnumerable<Material> Materials { get; }
public abstract int MaterialCount { get; }
public abstract bool IsLoaded { get; }
public abstract void Clear ();
public abstract Atlas GetAtlas ();
}
}

View File

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

View File

@@ -0,0 +1,129 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 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/SkeletonData Modifiers/Blend Mode Materials", order = 200)]
public class BlendModeMaterialsAsset : SkeletonDataModifierAsset {
public Material multiplyMaterialTemplate;
public Material screenMaterialTemplate;
public Material additiveMaterialTemplate;
public bool applyAdditiveMaterial;
public override 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");
using (var materialCache = new AtlasMaterialCache()) {
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 = materialCache.CloneAtlasRegionWithMaterial((AtlasRegion)renderableAttachment.RendererObject, templateMaterial);
}
}
}
}
//attachmentBuffer.Clear();
}
class AtlasMaterialCache : IDisposable {
readonly Dictionary<KeyValuePair<AtlasPage, Material>, AtlasPage> cache = new Dictionary<KeyValuePair<AtlasPage, Material>, AtlasPage>();
/// <summary>Creates a clone of an AtlasRegion that uses different Material settings, while retaining the original texture.</summary>
public AtlasRegion CloneAtlasRegionWithMaterial (AtlasRegion originalRegion, Material materialTemplate) {
var newRegion = originalRegion.Clone();
newRegion.page = GetAtlasPageWithMaterial(originalRegion.page, materialTemplate);
return newRegion;
}
AtlasPage GetAtlasPageWithMaterial (AtlasPage originalPage, Material materialTemplate) {
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;
}
public void Dispose () {
cache.Clear();
}
}
}
}

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

@@ -0,0 +1,65 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#define AUTOINIT_SPINEREFERENCE
using UnityEngine;
namespace Spine.Unity {
[CreateAssetMenu(menuName = "Spine/EventData Reference Asset", order = 100)]
public class EventDataReferenceAsset : ScriptableObject {
const bool QuietSkeletonData = true;
[SerializeField] protected SkeletonDataAsset skeletonDataAsset;
[SerializeField, SpineEvent(dataField: "skeletonDataAsset")] protected string eventName;
EventData eventData;
public EventData EventData {
get {
#if AUTOINIT_SPINEREFERENCE
if (eventData == null)
Initialize();
#endif
return eventData;
}
}
public void Initialize () {
if (skeletonDataAsset == null)
return;
this.eventData = skeletonDataAsset.GetSkeletonData(EventDataReferenceAsset.QuietSkeletonData).FindEvent(eventName);
if (this.eventData == null)
Debug.LogWarningFormat("Event Data '{0}' not found in SkeletonData : {1}.", eventName, skeletonDataAsset.name);
}
public static implicit operator EventData (EventDataReferenceAsset asset) {
return asset.EventData;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8e60be42c1473144db0fd3337c25b500
timeCreated: 1523330891
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: d226a80acc775714aa78b85e16a00e9b, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,83 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
using UnityEngine;
namespace Spine.Unity {
public class RegionlessAttachmentLoader : AttachmentLoader {
static AtlasRegion emptyRegion;
static AtlasRegion EmptyRegion {
get {
if (emptyRegion == null) {
emptyRegion = new AtlasRegion {
name = "Empty AtlasRegion",
page = new AtlasPage {
name = "Empty AtlasPage",
rendererObject = new Material(Shader.Find("Spine/Special/HiddenPass")) { name = "NoRender Material" }
}
};
}
return emptyRegion;
}
}
public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) {
RegionAttachment attachment = new RegionAttachment(name) {
RendererObject = EmptyRegion
};
return attachment;
}
public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) {
MeshAttachment attachment = new MeshAttachment(name) {
RendererObject = EmptyRegion
};
return attachment;
}
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) {
return new BoundingBoxAttachment(name);
}
public PathAttachment NewPathAttachment (Skin skin, string name) {
return new PathAttachment(name);
}
public PointAttachment NewPointAttachment (Skin skin, string name) {
return new PointAttachment(name);
}
public ClippingAttachment NewClippingAttachment (Skin skin, string name) {
return new ClippingAttachment(name);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 15f0f78b87720c047a320c5e0e3f91b7
timeCreated: 1520505662
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,239 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* 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 {
[CreateAssetMenu(fileName = "New SkeletonDataAsset", menuName = "Spine/SkeletonData Asset")]
public class SkeletonDataAsset : ScriptableObject {
#region Inspector
public AtlasAssetBase[] atlasAssets = new AtlasAssetBase[0];
#if SPINE_TK2D
public tk2dSpriteCollectionData spriteCollection;
public float scale = 1f;
#else
public float scale = 0.01f;
#endif
public TextAsset skeletonJSON;
[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>();
[SpineAnimation(includeNone: false)]
public string[] fromAnimation = new string[0];
[SpineAnimation(includeNone: false)]
public string[] toAnimation = new string[0];
public float[] duration = new float[0];
public float defaultMix;
public RuntimeAnimatorController controller;
public bool IsLoaded { get { return this.skeletonData != null; } }
void Reset () {
Clear();
}
#endregion
SkeletonData skeletonData;
AnimationStateData stateData;
#region Runtime Instantiation
/// <summary>
/// Creates a runtime SkeletonDataAsset.</summary>
public static SkeletonDataAsset CreateRuntimeInstance (TextAsset skeletonDataFile, AtlasAssetBase atlasAsset, bool initialize, float scale = 0.01f) {
return CreateRuntimeInstance(skeletonDataFile, new [] {atlasAsset}, initialize, scale);
}
/// <summary>
/// Creates a runtime SkeletonDataAsset.</summary>
public static SkeletonDataAsset CreateRuntimeInstance (TextAsset skeletonDataFile, AtlasAssetBase[] atlasAssets, bool initialize, float scale = 0.01f) {
SkeletonDataAsset skeletonDataAsset = ScriptableObject.CreateInstance<SkeletonDataAsset>();
skeletonDataAsset.Clear();
skeletonDataAsset.skeletonJSON = skeletonDataFile;
skeletonDataAsset.atlasAssets = atlasAssets;
skeletonDataAsset.scale = scale;
if (initialize)
skeletonDataAsset.GetSkeletonData(true);
return skeletonDataAsset;
}
#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;
}
public AnimationStateData GetAnimationStateData () {
if (stateData != null)
return stateData;
GetSkeletonData(false);
return stateData;
}
/// <summary>Loads, caches and returns the SkeletonData from the skeleton data file. Returns the cached SkeletonData after the first time it is called. Pass false to prevent direct errors from being logged.</summary>
public SkeletonData GetSkeletonData (bool quiet) {
if (skeletonJSON == null) {
if (!quiet)
Debug.LogError("Skeleton JSON file not set for SkeletonData asset: " + name, this);
Clear();
return null;
}
// Disabled to support attachmentless/skinless SkeletonData.
// if (atlasAssets == null) {
// atlasAssets = new AtlasAsset[0];
// if (!quiet)
// Debug.LogError("Atlas not set for SkeletonData asset: " + name, this);
// Clear();
// return null;
// }
// #if !SPINE_TK2D
// if (atlasAssets.Length == 0) {
// Clear();
// return null;
// }
// #else
// if (atlasAssets.Length == 0 && spriteCollection == null) {
// Clear();
// return null;
// }
// #endif
if (skeletonData != null)
return skeletonData;
AttachmentLoader attachmentLoader;
float skeletonDataScale;
Atlas[] atlasArray = this.GetAtlasArray();
#if !SPINE_TK2D
attachmentLoader = (atlasArray.Length == 0) ? (AttachmentLoader)new RegionlessAttachmentLoader() : (AttachmentLoader)new AtlasAttachmentLoader(atlasArray);
skeletonDataScale = scale;
#else
if (spriteCollection != null) {
attachmentLoader = new Spine.Unity.TK2D.SpriteCollectionAttachmentLoader(spriteCollection);
skeletonDataScale = (1.0f / (spriteCollection.invOrthoSize * spriteCollection.halfTargetHeight) * scale);
} else {
if (atlasArray.Length == 0) {
Reset();
if (!quiet) Debug.LogError("Atlas not set for SkeletonData asset: " + name, this);
return null;
}
attachmentLoader = new AtlasAttachmentLoader(atlasArray);
skeletonDataScale = scale;
}
#endif
bool isBinary = skeletonJSON.name.ToLower().Contains(".skel");
SkeletonData loadedSkeletonData;
try {
if (isBinary)
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.bytes, attachmentLoader, skeletonDataScale);
else
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.text, attachmentLoader, skeletonDataScale);
} catch (Exception ex) {
if (!quiet)
Debug.LogError("Error reading skeleton JSON file for SkeletonData asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this);
return null;
}
if (skeletonDataModifiers != null) {
foreach (var m in skeletonDataModifiers) {
if (m != null) m.Apply(loadedSkeletonData);
}
}
this.InitializeWithData(loadedSkeletonData);
return skeletonData;
}
internal void InitializeWithData (SkeletonData sd) {
this.skeletonData = sd;
this.stateData = new AnimationStateData(skeletonData);
FillStateData();
}
public void FillStateData () {
if (stateData != null) {
stateData.defaultMix = defaultMix;
for (int i = 0, n = fromAnimation.Length; i < n; i++) {
if (fromAnimation[i].Length == 0 || toAnimation[i].Length == 0)
continue;
stateData.SetMix(fromAnimation[i], toAnimation[i], duration[i]);
}
}
}
internal Atlas[] GetAtlasArray () {
var returnList = new System.Collections.Generic.List<Atlas>(atlasAssets.Length);
for (int i = 0; i < atlasAssets.Length; i++) {
var aa = atlasAssets[i];
if (aa == null) continue;
var a = aa.GetAtlas();
if (a == null) continue;
returnList.Add(a);
}
return returnList.ToArray();
}
internal static SkeletonData ReadSkeletonData (byte[] bytes, AttachmentLoader attachmentLoader, float scale) {
using (var input = new MemoryStream(bytes)) {
var binary = new SkeletonBinary(attachmentLoader) {
Scale = scale
};
return binary.ReadSkeletonData(input);
}
}
internal static SkeletonData ReadSkeletonData (string text, AttachmentLoader attachmentLoader, float scale) {
var input = new StringReader(text);
var json = new SkeletonJson(attachmentLoader) {
Scale = scale
};
return json.ReadSkeletonData(input);
}
}
}

View File

@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: f1b3b4b945939a54ea0b23d3396115fb
timeCreated: 1536403985
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}
- skeletonJSON: {instanceID: 0}
- controller: {instanceID: 0}
executionOrder: 0
icon: {fileID: 2800000, guid: 68defdbc95b30a74a9ad396bfc9a2277, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,39 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>Can be stored by SkeletonDataAsset to automatically apply modifications to loaded SkeletonData.</summary>
public abstract class SkeletonDataModifierAsset : ScriptableObject {
public abstract void Apply (SkeletonData skeletonData);
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 79a44aba1f342f440965874280b4c318
timeCreated: 1536412736
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,245 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* 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 {
/// <summary>Loads and stores a Spine atlas and list of materials.</summary>
[CreateAssetMenu(fileName = "New Spine Atlas Asset", menuName = "Spine/Spine Atlas Asset")]
public class SpineAtlasAsset : AtlasAssetBase {
public TextAsset atlasFile;
public Material[] materials;
protected Atlas atlas;
public override bool IsLoaded { get { return this.atlas != null; } }
public override IEnumerable<Material> Materials { get { return materials; } }
public override int MaterialCount { get { return materials == null ? 0 : materials.Length; } }
public override Material PrimaryMaterial { get { return materials[0]; } }
#region Runtime Instantiation
/// <summary>
/// Creates a runtime AtlasAsset</summary>
public static SpineAtlasAsset CreateRuntimeInstance (TextAsset atlasText, Material[] materials, bool initialize) {
SpineAtlasAsset atlasAsset = ScriptableObject.CreateInstance<SpineAtlasAsset>();
atlasAsset.Reset();
atlasAsset.atlasFile = atlasText;
atlasAsset.materials = materials;
if (initialize)
atlasAsset.GetAtlas();
return atlasAsset;
}
/// <summary>
/// Creates a runtime AtlasAsset. Only providing the textures is slower because it has to search for atlas page matches. <seealso cref="Spine.Unity.SpineAtlasAsset.CreateRuntimeInstance(TextAsset, Material[], bool)"/></summary>
public static SpineAtlasAsset CreateRuntimeInstance (TextAsset atlasText, Texture2D[] textures, Material materialPropertySource, bool initialize) {
// Get atlas page names.
string atlasString = atlasText.text;
atlasString = atlasString.Replace("\r", "");
string[] atlasLines = atlasString.Split('\n');
var pages = new List<string>();
for (int i = 0; i < atlasLines.Length - 1; i++) {
if (atlasLines[i].Trim().Length == 0)
pages.Add(atlasLines[i + 1].Trim().Replace(".png", ""));
}
// Populate Materials[] by matching texture names with page names.
var materials = new Material[pages.Count];
for (int i = 0, n = pages.Count; i < n; i++) {
Material mat = null;
// Search for a match.
string pageName = pages[i];
for (int j = 0, m = textures.Length; j < m; j++) {
if (string.Equals(pageName, textures[j].name, System.StringComparison.OrdinalIgnoreCase)) {
// Match found.
mat = new Material(materialPropertySource);
mat.mainTexture = textures[j];
break;
}
}
if (mat != null)
materials[i] = mat;
else
throw new ArgumentException("Could not find matching atlas page in the texture array.");
}
// Create AtlasAsset normally
return CreateRuntimeInstance(atlasText, materials, initialize);
}
/// <summary>
/// Creates a runtime AtlasAsset. Only providing the textures is slower because it has to search for atlas page matches. <seealso cref="Spine.Unity.AtlasAssetBase.CreateRuntimeInstance(TextAsset, Material[], bool)"/></summary>
public static SpineAtlasAsset CreateRuntimeInstance (TextAsset atlasText, Texture2D[] textures, Shader shader, bool initialize) {
if (shader == null)
shader = Shader.Find("Spine/Skeleton");
Material materialProperySource = new Material(shader);
var oa = CreateRuntimeInstance(atlasText, textures, materialProperySource, initialize);
return oa;
}
#endregion
void Reset () {
Clear();
}
public override void Clear () {
atlas = null;
}
/// <returns>The atlas or null if it could not be loaded.</returns>
public override Atlas GetAtlas () {
if (atlasFile == null) {
Debug.LogError("Atlas file not set for atlas asset: " + name, this);
Clear();
return null;
}
if (materials == null || materials.Length == 0) {
Debug.LogError("Materials not set for atlas asset: " + name, this);
Clear();
return null;
}
if (atlas != null) return atlas;
try {
atlas = new Atlas(new StringReader(atlasFile.text), "", new MaterialsTextureLoader(this));
atlas.FlipV();
return atlas;
} catch (Exception ex) {
Debug.LogError("Error reading atlas file for atlas asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this);
return null;
}
}
public Mesh GenerateMesh (string name, Mesh mesh, out Material material, float scale = 0.01f) {
AtlasRegion region = atlas.FindRegion(name);
material = null;
if (region != null) {
if (mesh == null) {
mesh = new Mesh();
mesh.name = name;
}
Vector3[] verts = new Vector3[4];
Vector2[] uvs = new Vector2[4];
Color[] colors = { Color.white, Color.white, Color.white, Color.white };
int[] triangles = { 0, 1, 2, 2, 3, 0 };
float left, right, top, bottom;
left = region.width / -2f;
right = left * -1f;
top = region.height / 2f;
bottom = top * -1;
verts[0] = new Vector3(left, bottom, 0) * scale;
verts[1] = new Vector3(left, top, 0) * scale;
verts[2] = new Vector3(right, top, 0) * scale;
verts[3] = new Vector3(right, bottom, 0) * scale;
float u, v, u2, v2;
u = region.u;
v = region.v;
u2 = region.u2;
v2 = region.v2;
if (!region.rotate) {
uvs[0] = new Vector2(u, v2);
uvs[1] = new Vector2(u, v);
uvs[2] = new Vector2(u2, v);
uvs[3] = new Vector2(u2, v2);
} else {
uvs[0] = new Vector2(u2, v2);
uvs[1] = new Vector2(u, v2);
uvs[2] = new Vector2(u, v);
uvs[3] = new Vector2(u2, v);
}
mesh.triangles = new int[0];
mesh.vertices = verts;
mesh.uv = uvs;
mesh.colors = colors;
mesh.triangles = triangles;
mesh.RecalculateNormals();
mesh.RecalculateBounds();
material = (Material)region.page.rendererObject;
} else {
mesh = null;
}
return mesh;
}
}
public class MaterialsTextureLoader : TextureLoader {
SpineAtlasAsset atlasAsset;
public MaterialsTextureLoader (SpineAtlasAsset atlasAsset) {
this.atlasAsset = atlasAsset;
}
public void Load (AtlasPage page, string path) {
String name = Path.GetFileNameWithoutExtension(path);
Material material = null;
foreach (Material other in atlasAsset.materials) {
if (other.mainTexture == null) {
Debug.LogError("Material is missing texture: " + other.name, other);
return;
}
if (other.mainTexture.name == name) {
material = other;
break;
}
}
if (material == null) {
Debug.LogError("Material with texture name \"" + name + "\" not found for atlas asset: " + atlasAsset.name, atlasAsset);
return;
}
page.rendererObject = material;
// Very old atlas files expected the texture's actual size to be used at runtime.
if (page.width == 0 || page.height == 0) {
page.width = material.mainTexture.width;
page.height = material.mainTexture.height;
}
}
public void Unload (object texture) { }
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: a6b194f808b1af6499c93410e504af42
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 3fc714a0dc1cf6b4b959e073fff2844e, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 954179821df28404683b8289f05d0c6f
folderAsset: yes
timeCreated: 1518344191
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,185 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using System;
using UnityEngine;
namespace Spine.Unity {
/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[AddComponentMenu("Spine/BoneFollower")]
public class BoneFollower : MonoBehaviour {
#region Inspector
public SkeletonRenderer skeletonRenderer;
public SkeletonRenderer SkeletonRenderer {
get { return skeletonRenderer; }
set {
skeletonRenderer = value;
Initialize();
}
}
/// <summary>If a bone isn't set in code, boneName is used to find the bone at the beginning. For runtime switching by name, use SetBoneByName. You can also set the BoneFollower.bone field directly.</summary>
[SpineBone(dataField: "skeletonRenderer")]
[SerializeField] public string boneName;
public bool followZPosition = true;
public bool followBoneRotation = true;
[Tooltip("Follows the skeleton's flip state by controlling this Transform's local scale.")]
public bool followSkeletonFlip = true;
[Tooltip("Follows the target bone's local scale. BoneFollower cannot inherit world/skewed scale because of UnityEngine.Transform property limitations.")]
public bool followLocalScale = false;
[UnityEngine.Serialization.FormerlySerializedAs("resetOnAwake")]
public bool initializeOnAwake = true;
#endregion
[NonSerialized] public bool valid;
[NonSerialized] public Bone bone;
Transform skeletonTransform;
bool skeletonTransformIsParent;
/// <summary>
/// Sets the target bone by its bone name. Returns false if no bone was found. To set the bone by reference, use BoneFollower.bone directly.</summary>
public bool SetBone (string name) {
bone = skeletonRenderer.skeleton.FindBone(name);
if (bone == null) {
Debug.LogError("Bone not found: " + name, this);
return false;
}
boneName = name;
return true;
}
public void Awake () {
if (initializeOnAwake) Initialize();
}
public void HandleRebuildRenderer (SkeletonRenderer skeletonRenderer) {
Initialize();
}
public void Initialize () {
bone = null;
valid = skeletonRenderer != null && skeletonRenderer.valid;
if (!valid) return;
skeletonTransform = skeletonRenderer.transform;
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
skeletonRenderer.OnRebuild += HandleRebuildRenderer;
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
if (!string.IsNullOrEmpty(boneName))
bone = skeletonRenderer.skeleton.FindBone(boneName);
#if UNITY_EDITOR
if (Application.isEditor)
LateUpdate();
#endif
}
void OnDestroy () {
if (skeletonRenderer != null)
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
}
public void LateUpdate () {
if (!valid) {
Initialize();
return;
}
#if UNITY_EDITOR
if (!Application.isPlaying)
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
#endif
if (bone == null) {
if (string.IsNullOrEmpty(boneName)) return;
bone = skeletonRenderer.skeleton.FindBone(boneName);
if (!SetBone(boneName)) return;
}
Transform thisTransform = this.transform;
if (skeletonTransformIsParent) {
// Recommended setup: Use local transform properties if Spine GameObject is the immediate parent
thisTransform.localPosition = new Vector3(bone.worldX, bone.worldY, followZPosition ? 0f : thisTransform.localPosition.z);
if (followBoneRotation) {
float halfRotation = Mathf.Atan2(bone.c, bone.a) * 0.5f;
if (followLocalScale && bone.scaleX < 0) // Negate rotation from negative scaleX. Don't use negative determinant. local scaleY doesn't factor into used rotation.
halfRotation += Mathf.PI * 0.5f;
var q = default(Quaternion);
q.z = Mathf.Sin(halfRotation);
q.w = Mathf.Cos(halfRotation);
thisTransform.localRotation = q;
}
} else {
// For special cases: Use transform world properties if transform relationship is complicated
Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY, 0f));
if (!followZPosition) targetWorldPosition.z = thisTransform.position.z;
float boneWorldRotation = bone.WorldRotationX;
Transform transformParent = thisTransform.parent;
if (transformParent != null) {
Matrix4x4 m = transformParent.localToWorldMatrix;
if (m.m00 * m.m11 - m.m01 * m.m10 < 0) // Determinant2D is negative
boneWorldRotation = -boneWorldRotation;
}
if (followBoneRotation) {
Vector3 worldRotation = skeletonTransform.rotation.eulerAngles;
if (followLocalScale && bone.scaleX < 0) boneWorldRotation += 180f;
thisTransform.SetPositionAndRotation(targetWorldPosition, Quaternion.Euler(worldRotation.x, worldRotation.y, worldRotation.z + boneWorldRotation));
} else {
thisTransform.position = targetWorldPosition;
}
}
Vector3 localScale = followLocalScale ? new Vector3(bone.scaleX, bone.scaleY, 1f) : new Vector3(1f, 1f, 1f);
if (followSkeletonFlip) localScale.y *= Mathf.Sign(bone.skeleton.scaleX * bone.skeleton.scaleY);
thisTransform.localScale = localScale;
}
}
}

View File

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

View File

@@ -0,0 +1,158 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[AddComponentMenu("Spine/Point Follower")]
public class PointFollower : MonoBehaviour, IHasSkeletonRenderer, IHasSkeletonComponent {
[SerializeField] public SkeletonRenderer skeletonRenderer;
public SkeletonRenderer SkeletonRenderer { get { return this.skeletonRenderer; } }
public ISkeletonComponent SkeletonComponent { get { return skeletonRenderer as ISkeletonComponent; } }
[SpineSlot(dataField:"skeletonRenderer", includeNone: true)]
public string slotName;
[SpineAttachment(slotField:"slotName", dataField: "skeletonRenderer", fallbackToTextField:true, includeNone: true)]
public string pointAttachmentName;
public bool followRotation = true;
public bool followSkeletonFlip = true;
public bool followSkeletonZPosition = false;
Transform skeletonTransform;
bool skeletonTransformIsParent;
PointAttachment point;
Bone bone;
bool valid;
public bool IsValid { get { return valid; } }
public void Initialize () {
valid = skeletonRenderer != null && skeletonRenderer.valid;
if (!valid)
return;
UpdateReferences();
#if UNITY_EDITOR
if (Application.isEditor) LateUpdate();
#endif
}
private void HandleRebuildRenderer (SkeletonRenderer skeletonRenderer) {
Initialize();
}
void UpdateReferences () {
skeletonTransform = skeletonRenderer.transform;
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
skeletonRenderer.OnRebuild += HandleRebuildRenderer;
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
bone = null;
point = null;
if (!string.IsNullOrEmpty(pointAttachmentName)) {
var skeleton = skeletonRenderer.Skeleton;
int slotIndex = skeleton.FindSlotIndex(slotName);
if (slotIndex >= 0) {
var slot = skeleton.slots.Items[slotIndex];
bone = slot.bone;
point = skeleton.GetAttachment(slotIndex, pointAttachmentName) as PointAttachment;
}
}
}
public void LateUpdate () {
#if UNITY_EDITOR
if (!Application.isPlaying) skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
#endif
if (point == null) {
if (string.IsNullOrEmpty(pointAttachmentName)) return;
UpdateReferences();
if (point == null) return;
}
Vector2 worldPos;
point.ComputeWorldPosition(bone, out worldPos.x, out worldPos.y);
float rotation = point.ComputeWorldRotation(bone);
Transform thisTransform = this.transform;
if (skeletonTransformIsParent) {
// Recommended setup: Use local transform properties if Spine GameObject is the immediate parent
thisTransform.localPosition = new Vector3(worldPos.x, worldPos.y, followSkeletonZPosition ? 0f : thisTransform.localPosition.z);
if (followRotation) {
float halfRotation = rotation * 0.5f * Mathf.Deg2Rad;
var q = default(Quaternion);
q.z = Mathf.Sin(halfRotation);
q.w = Mathf.Cos(halfRotation);
thisTransform.localRotation = q;
}
} else {
// For special cases: Use transform world properties if transform relationship is complicated
Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(worldPos.x, worldPos.y, 0f));
if (!followSkeletonZPosition)
targetWorldPosition.z = thisTransform.position.z;
Transform transformParent = thisTransform.parent;
if (transformParent != null) {
Matrix4x4 m = transformParent.localToWorldMatrix;
if (m.m00 * m.m11 - m.m01 * m.m10 < 0) // Determinant2D is negative
rotation = -rotation;
}
if (followRotation) {
Vector3 transformWorldRotation = skeletonTransform.rotation.eulerAngles;
thisTransform.SetPositionAndRotation(targetWorldPosition, Quaternion.Euler(transformWorldRotation.x, transformWorldRotation.y, transformWorldRotation.z + rotation));
} else {
thisTransform.position = targetWorldPosition;
}
}
if (followSkeletonFlip) {
Vector3 localScale = thisTransform.localScale;
localScale.y = Mathf.Abs(localScale.y) * Mathf.Sign(bone.skeleton.scaleX * bone.skeleton.scaleY);
thisTransform.localScale = localScale;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: daf461e4341180341a648c07e1899528
timeCreated: 1518094986
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,208 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[AddComponentMenu("Spine/SkeletonAnimation")]
public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation, IAnimationStateComponent {
#region IAnimationStateComponent
/// <summary>
/// This is the Spine.AnimationState object of this SkeletonAnimation. You can control animations through it.
/// Note that this object, like .skeleton, is not guaranteed to exist in Awake. Do all accesses and caching to it in Start</summary>
public Spine.AnimationState state;
/// <summary>
/// This is the Spine.AnimationState object of this SkeletonAnimation. You can control animations through it.
/// Note that this object, like .skeleton, is not guaranteed to exist in Awake. Do all accesses and caching to it in Start</summary>
public Spine.AnimationState AnimationState { get { return this.state; } }
#endregion
#region Bone Callbacks ISkeletonAnimation
protected event UpdateBonesDelegate _UpdateLocal;
protected event UpdateBonesDelegate _UpdateWorld;
protected event UpdateBonesDelegate _UpdateComplete;
/// <summary>
/// Occurs after the animations are applied and before world space values are resolved.
/// Use this callback when you want to set bone local values.
/// </summary>
public event UpdateBonesDelegate UpdateLocal { add { _UpdateLocal += value; } remove { _UpdateLocal -= value; } }
/// <summary>
/// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
/// Using this callback will cause the world space values to be solved an extra time.
/// Use this callback if want to use bone world space values, and also set bone local values.</summary>
public event UpdateBonesDelegate UpdateWorld { add { _UpdateWorld += value; } remove { _UpdateWorld -= value; } }
/// <summary>
/// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
/// Use this callback if you want to use bone world space values, but don't intend to modify bone local values.
/// This callback can also be used when setting world position and the bone matrix.</summary>
public event UpdateBonesDelegate UpdateComplete { add { _UpdateComplete += value; } remove { _UpdateComplete -= value; } }
#endregion
#region Serialized state and Beginner API
[SerializeField]
[SpineAnimation]
private string _animationName;
/// <summary>
/// Setting this property sets the animation of the skeleton. If invalid, it will store the animation name for the next time the skeleton is properly initialized.
/// Getting this property gets the name of the currently playing animation. If invalid, it will return the last stored animation name set through this property.</summary>
public string AnimationName {
get {
if (!valid) {
return _animationName;
} else {
TrackEntry entry = state.GetCurrent(0);
return entry == null ? null : entry.Animation.Name;
}
}
set {
if (_animationName == value)
return;
_animationName = value;
if (string.IsNullOrEmpty(value)) {
state.ClearTrack(0);
} else {
var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(value);
if (animationObject != null)
state.SetAnimation(0, animationObject, loop);
}
}
}
/// <summary>Whether or not <see cref="AnimationName"/> should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected.</summary>
public bool loop;
/// <summary>
/// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.</summary>
/// <remarks>AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively.</remarks>
public float timeScale = 1;
#endregion
#region Runtime Instantiation
/// <summary>Adds and prepares a SkeletonAnimation component to a GameObject at runtime.</summary>
/// <returns>The newly instantiated SkeletonAnimation</returns>
public static SkeletonAnimation AddToGameObject (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) {
return SkeletonRenderer.AddSpineComponent<SkeletonAnimation>(gameObject, skeletonDataAsset);
}
/// <summary>Instantiates a new UnityEngine.GameObject and adds a prepared SkeletonAnimation component to it.</summary>
/// <returns>The newly instantiated SkeletonAnimation component.</returns>
public static SkeletonAnimation NewSkeletonAnimationGameObject (SkeletonDataAsset skeletonDataAsset) {
return SkeletonRenderer.NewSpineGameObject<SkeletonAnimation>(skeletonDataAsset);
}
#endregion
/// <summary>
/// Clears the previously generated mesh, resets the skeleton's pose, and clears all previously active animations.</summary>
public override void ClearState () {
base.ClearState();
if (state != null) state.ClearTracks();
}
/// <summary>
/// Initialize this component. Attempts to load the SkeletonData and creates the internal Spine objects and buffers.</summary>
/// <param name="overwrite">If set to <c>true</c>, force overwrite an already initialized object.</param>
public override void Initialize (bool overwrite) {
if (valid && !overwrite)
return;
base.Initialize(overwrite);
if (!valid)
return;
state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
if (!string.IsNullOrEmpty(_animationName)) {
var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(_animationName);
if (animationObject != null) {
animationObject.PoseSkeleton(skeleton, 0f);
skeleton.UpdateWorldTransform();
#if UNITY_EDITOR
if (Application.isPlaying) {
#endif
// In Unity Editor edit mode, make this block not run.
state.SetAnimation(0, animationObject, loop);
#if UNITY_EDITOR
}
#endif
}
}
}
void Update () {
Update(Time.deltaTime);
}
/// <summary>Progresses the AnimationState according to the given deltaTime, and applies it to the Skeleton. Use Time.deltaTime to update manually. Use deltaTime 0 to update without progressing the time.</summary>
public void Update (float deltaTime) {
if (!valid)
return;
deltaTime *= timeScale;
skeleton.Update(deltaTime);
state.Update(deltaTime);
state.Apply(skeleton);
if (_UpdateLocal != null)
_UpdateLocal(this);
skeleton.UpdateWorldTransform();
if (_UpdateWorld != null) {
_UpdateWorld(this);
skeleton.UpdateWorldTransform();
}
if (_UpdateComplete != null) {
_UpdateComplete(this);
}
}
}
}

View File

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

View File

@@ -0,0 +1,496 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
using System.Collections.Generic;
namespace Spine.Unity {
[RequireComponent(typeof(Animator))]
public class SkeletonMecanim : SkeletonRenderer, ISkeletonAnimation {
[SerializeField] protected MecanimTranslator translator;
public MecanimTranslator Translator { get { return translator; } }
#region Bone Callbacks (ISkeletonAnimation)
protected event UpdateBonesDelegate _UpdateLocal;
protected event UpdateBonesDelegate _UpdateWorld;
protected event UpdateBonesDelegate _UpdateComplete;
/// <summary>
/// Occurs after the animations are applied and before world space values are resolved.
/// Use this callback when you want to set bone local values.</summary>
public event UpdateBonesDelegate UpdateLocal { add { _UpdateLocal += value; } remove { _UpdateLocal -= value; } }
/// <summary>
/// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
/// Using this callback will cause the world space values to be solved an extra time.
/// Use this callback if want to use bone world space values, and also set bone local values.</summary>
public event UpdateBonesDelegate UpdateWorld { add { _UpdateWorld += value; } remove { _UpdateWorld -= value; } }
/// <summary>
/// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
/// Use this callback if you want to use bone world space values, but don't intend to modify bone local values.
/// This callback can also be used when setting world position and the bone matrix.</summary>
public event UpdateBonesDelegate UpdateComplete { add { _UpdateComplete += value; } remove { _UpdateComplete -= value; } }
#endregion
public override void Initialize (bool overwrite) {
if (valid && !overwrite) return;
base.Initialize(overwrite);
if (!valid) return;
if (translator == null) translator = new MecanimTranslator();
translator.Initialize(GetComponent<Animator>(), this.skeletonDataAsset);
}
public void Update () {
if (!valid) return;
#if UNITY_EDITOR
var translatorAnimator = translator.Animator;
if (translatorAnimator != null && !translatorAnimator.isInitialized)
translatorAnimator.Rebind();
if (Application.isPlaying) {
translator.Apply(skeleton);
} else {
if (translatorAnimator != null && translatorAnimator.isInitialized &&
translatorAnimator.isActiveAndEnabled && translatorAnimator.runtimeAnimatorController != null) {
// Note: Rebind is required to prevent warning "Animator is not playing an AnimatorController" with prefabs
translatorAnimator.Rebind();
translator.Apply(skeleton);
}
}
#else
translator.Apply(skeleton);
#endif
// UpdateWorldTransform and Bone Callbacks
{
if (_UpdateLocal != null)
_UpdateLocal(this);
skeleton.UpdateWorldTransform();
if (_UpdateWorld != null) {
_UpdateWorld(this);
skeleton.UpdateWorldTransform();
}
if (_UpdateComplete != null)
_UpdateComplete(this);
}
}
[System.Serializable]
public class MecanimTranslator {
#region Inspector
public bool autoReset = true;
public MixMode[] layerMixModes = new MixMode[0];
public MixBlend[] layerBlendModes = new MixBlend[0];
#endregion
public enum MixMode { AlwaysMix, MixNext, SpineStyle }
readonly Dictionary<int, Spine.Animation> animationTable = new Dictionary<int, Spine.Animation>(IntEqualityComparer.Instance);
readonly Dictionary<AnimationClip, int> clipNameHashCodeTable = new Dictionary<AnimationClip, int>(AnimationClipEqualityComparer.Instance);
readonly List<Animation> previousAnimations = new List<Animation>();
protected class ClipInfos {
public bool isInterruptionActive = false;
public bool isLastFrameOfInterruption = false;
public int clipInfoCount = 0;
public int nextClipInfoCount = 0;
public int interruptingClipInfoCount = 0;
public readonly List<AnimatorClipInfo> clipInfos = new List<AnimatorClipInfo>();
public readonly List<AnimatorClipInfo> nextClipInfos = new List<AnimatorClipInfo>();
public readonly List<AnimatorClipInfo> interruptingClipInfos = new List<AnimatorClipInfo>();
public AnimatorStateInfo stateInfo;
public AnimatorStateInfo nextStateInfo;
public AnimatorStateInfo interruptingStateInfo;
public float interruptingClipTimeAddition = 0;
}
protected ClipInfos[] layerClipInfos = new ClipInfos[0];
Animator animator;
public Animator Animator { get { return this.animator; } }
public void Initialize(Animator animator, SkeletonDataAsset skeletonDataAsset) {
this.animator = animator;
previousAnimations.Clear();
animationTable.Clear();
var data = skeletonDataAsset.GetSkeletonData(true);
foreach (var a in data.Animations)
animationTable.Add(a.Name.GetHashCode(), a);
clipNameHashCodeTable.Clear();
ClearClipInfosForLayers();
}
public void Apply (Skeleton skeleton) {
if (layerMixModes.Length < animator.layerCount)
System.Array.Resize<MixMode>(ref layerMixModes, animator.layerCount);
#if UNITY_EDITOR
if (!Application.isPlaying) {
GetLayerBlendModes();
}
#endif
InitClipInfosForLayers();
for (int layer = 0, n = animator.layerCount; layer < n; layer++) {
GetStateUpdatesFromAnimator(layer);
}
//skeleton.Update(Time.deltaTime); // Doesn't actually do anything, currently. (Spine 3.6).
// Clear Previous
if (autoReset) {
var previousAnimations = this.previousAnimations;
for (int i = 0, n = previousAnimations.Count; i < n; i++)
previousAnimations[i].SetKeyedItemsToSetupPose(skeleton);
previousAnimations.Clear();
for (int layer = 0, n = animator.layerCount; layer < n; layer++) {
float layerWeight = (layer == 0) ? 1 : animator.GetLayerWeight(layer); // Animator.GetLayerWeight always returns 0 on the first layer. Should be interpreted as 1.
if (layerWeight <= 0) continue;
AnimatorStateInfo nextStateInfo = animator.GetNextAnimatorStateInfo(layer);
bool hasNext = nextStateInfo.fullPathHash != 0;
int clipInfoCount, nextClipInfoCount, interruptingClipInfoCount;
IList<AnimatorClipInfo> clipInfo, nextClipInfo, interruptingClipInfo;
bool isInterruptionActive, shallInterpolateWeightTo1;
GetAnimatorClipInfos(layer, out isInterruptionActive, out clipInfoCount, out nextClipInfoCount, out interruptingClipInfoCount,
out clipInfo, out nextClipInfo, out interruptingClipInfo, out shallInterpolateWeightTo1);
for (int c = 0; c < clipInfoCount; c++) {
var info = clipInfo[c];
float weight = info.weight * layerWeight; if (weight == 0) continue;
previousAnimations.Add(GetAnimation(info.clip));
}
if (hasNext) {
for (int c = 0; c < nextClipInfoCount; c++) {
var info = nextClipInfo[c];
float weight = info.weight * layerWeight; if (weight == 0) continue;
previousAnimations.Add(GetAnimation(info.clip));
}
}
if (isInterruptionActive) {
for (int c = 0; c < interruptingClipInfoCount; c++)
{
var info = interruptingClipInfo[c];
float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight;
float weight = clipWeight * layerWeight; if (weight == 0) continue;
previousAnimations.Add(GetAnimation(info.clip));
}
}
}
}
// Apply
for (int layer = 0, n = animator.layerCount; layer < n; layer++) {
float layerWeight = (layer == 0) ? 1 : animator.GetLayerWeight(layer); // Animator.GetLayerWeight always returns 0 on the first layer. Should be interpreted as 1.
bool isInterruptionActive;
AnimatorStateInfo stateInfo;
AnimatorStateInfo nextStateInfo;
AnimatorStateInfo interruptingStateInfo;
float interruptingClipTimeAddition;
GetAnimatorStateInfos(layer, out isInterruptionActive, out stateInfo, out nextStateInfo, out interruptingStateInfo, out interruptingClipTimeAddition);
bool hasNext = nextStateInfo.fullPathHash != 0;
int clipInfoCount, nextClipInfoCount, interruptingClipInfoCount;
IList<AnimatorClipInfo> clipInfo, nextClipInfo, interruptingClipInfo;
bool shallInterpolateWeightTo1;
GetAnimatorClipInfos(layer, out isInterruptionActive, out clipInfoCount, out nextClipInfoCount, out interruptingClipInfoCount,
out clipInfo, out nextClipInfo, out interruptingClipInfo, out shallInterpolateWeightTo1);
MixMode mode = layerMixModes[layer];
MixBlend layerBlendMode = (layer < layerBlendModes.Length) ? layerBlendModes[layer] : MixBlend.Replace;
if (mode == MixMode.AlwaysMix) {
// Always use Mix instead of Applying the first non-zero weighted clip.
for (int c = 0; c < clipInfoCount; c++) {
var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue;
GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(stateInfo.normalizedTime, info.clip.length, stateInfo.loop, stateInfo.speed < 0), stateInfo.loop, null, weight, layerBlendMode, MixDirection.In);
}
if (hasNext) {
for (int c = 0; c < nextClipInfoCount; c++) {
var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue;
GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(nextStateInfo.normalizedTime, info.clip.length, nextStateInfo.speed < 0), nextStateInfo.loop, null, weight, layerBlendMode, MixDirection.In);
}
}
if (isInterruptionActive) {
for (int c = 0; c < interruptingClipInfoCount; c++)
{
var info = interruptingClipInfo[c];
float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight;
float weight = clipWeight * layerWeight; if (weight == 0) continue;
GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(interruptingStateInfo.normalizedTime + interruptingClipTimeAddition, info.clip.length, interruptingStateInfo.speed < 0),
interruptingStateInfo.loop, null, weight, layerBlendMode, MixDirection.In);
}
}
} else { // case MixNext || SpineStyle
// Apply first non-zero weighted clip
int c = 0;
for (; c < clipInfoCount; c++) {
var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue;
GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(stateInfo.normalizedTime, info.clip.length, stateInfo.loop, stateInfo.speed < 0), stateInfo.loop, null, 1f, layerBlendMode, MixDirection.In);
break;
}
// Mix the rest
for (; c < clipInfoCount; c++) {
var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue;
GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(stateInfo.normalizedTime, info.clip.length, stateInfo.loop, stateInfo.speed < 0), stateInfo.loop, null, weight, layerBlendMode, MixDirection.In);
}
c = 0;
if (hasNext) {
// Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights)
if (mode == MixMode.SpineStyle) {
for (; c < nextClipInfoCount; c++) {
var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue;
GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(nextStateInfo.normalizedTime, info.clip.length, nextStateInfo.speed < 0), nextStateInfo.loop, null, 1f, layerBlendMode, MixDirection.In);
break;
}
}
// Mix the rest
for (; c < nextClipInfoCount; c++) {
var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue;
GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(nextStateInfo.normalizedTime, info.clip.length, nextStateInfo.speed < 0), nextStateInfo.loop, null, weight, layerBlendMode, MixDirection.In);
}
}
c = 0;
if (isInterruptionActive) {
// Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights)
if (mode == MixMode.SpineStyle) {
for (; c < interruptingClipInfoCount; c++) {
var info = interruptingClipInfo[c];
float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight;
float weight = clipWeight * layerWeight; if (weight == 0) continue;
GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(interruptingStateInfo.normalizedTime + interruptingClipTimeAddition, info.clip.length, interruptingStateInfo.speed < 0), interruptingStateInfo.loop, null, 1f, layerBlendMode, MixDirection.In);
break;
}
}
// Mix the rest
for (; c < interruptingClipInfoCount; c++) {
var info = interruptingClipInfo[c];
float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight;
float weight = clipWeight * layerWeight; if (weight == 0) continue;
GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(interruptingStateInfo.normalizedTime + interruptingClipTimeAddition, info.clip.length, interruptingStateInfo.speed < 0), interruptingStateInfo.loop, null, weight, layerBlendMode, MixDirection.In);
}
}
}
}
}
static float AnimationTime (float normalizedTime, float clipLength, bool loop, bool reversed) {
if (reversed)
normalizedTime = (1 - normalizedTime + (int)normalizedTime) + (int)normalizedTime;
float time = normalizedTime * clipLength;
if (loop) return time;
const float EndSnapEpsilon = 1f / 30f; // Workaround for end-duration keys not being applied.
return (clipLength - time < EndSnapEpsilon) ? clipLength : time; // return a time snapped to clipLength;
}
static float AnimationTime (float normalizedTime, float clipLength, bool reversed) {
if (reversed)
normalizedTime = (1 - normalizedTime + (int)normalizedTime) + (int)normalizedTime;
return normalizedTime * clipLength;
}
void InitClipInfosForLayers () {
if (layerClipInfos.Length < animator.layerCount) {
System.Array.Resize<ClipInfos>(ref layerClipInfos, animator.layerCount);
for (int layer = 0, n = animator.layerCount; layer < n; ++layer) {
if (layerClipInfos[layer] == null)
layerClipInfos[layer] = new ClipInfos();
}
}
}
void ClearClipInfosForLayers () {
for (int layer = 0, n = layerClipInfos.Length; layer < n; ++layer) {
if (layerClipInfos[layer] == null)
layerClipInfos[layer] = new ClipInfos();
else {
layerClipInfos[layer].isInterruptionActive = false;
layerClipInfos[layer].isLastFrameOfInterruption = false;
layerClipInfos[layer].clipInfos.Clear();
layerClipInfos[layer].nextClipInfos.Clear();
layerClipInfos[layer].interruptingClipInfos.Clear();
}
}
}
#if UNITY_EDITOR
void GetLayerBlendModes() {
if (layerBlendModes.Length < animator.layerCount) {
System.Array.Resize<MixBlend>(ref layerBlendModes, animator.layerCount);
}
for (int layer = 0, n = animator.layerCount; layer < n; ++layer) {
var controller = animator.runtimeAnimatorController as UnityEditor.Animations.AnimatorController;
if (controller != null) {
layerBlendModes[layer] = MixBlend.Replace;
if (layer > 0) {
layerBlendModes[layer] = controller.layers[layer].blendingMode == UnityEditor.Animations.AnimatorLayerBlendingMode.Additive ?
MixBlend.Add : MixBlend.Replace;
}
}
}
}
#endif
void GetStateUpdatesFromAnimator (int layer) {
var layerInfos = layerClipInfos[layer];
int clipInfoCount = animator.GetCurrentAnimatorClipInfoCount(layer);
int nextClipInfoCount = animator.GetNextAnimatorClipInfoCount(layer);
var clipInfos = layerInfos.clipInfos;
var nextClipInfos = layerInfos.nextClipInfos;
var interruptingClipInfos = layerInfos.interruptingClipInfos;
layerInfos.isInterruptionActive = (clipInfoCount == 0 && clipInfos.Count != 0 &&
nextClipInfoCount == 0 && nextClipInfos.Count != 0);
// Note: during interruption, GetCurrentAnimatorClipInfoCount and GetNextAnimatorClipInfoCount
// are returning 0 in calls above. Therefore we keep previous clipInfos and nextClipInfos
// until the interruption is over.
if (layerInfos.isInterruptionActive) {
// Note: The last frame of a transition interruption
// will have fullPathHash set to 0, therefore we have to use previous
// frame's infos about interruption clips and correct some values
// accordingly (normalizedTime and weight).
var interruptingStateInfo = animator.GetNextAnimatorStateInfo(layer);
layerInfos.isLastFrameOfInterruption = interruptingStateInfo.fullPathHash == 0;
if (!layerInfos.isLastFrameOfInterruption) {
layerInfos.interruptingClipInfoCount = interruptingClipInfos.Count;
animator.GetNextAnimatorClipInfo(layer, interruptingClipInfos);
float oldTime = layerInfos.interruptingStateInfo.normalizedTime;
float newTime = interruptingStateInfo.normalizedTime;
layerInfos.interruptingClipTimeAddition = newTime - oldTime;
layerInfos.interruptingStateInfo = interruptingStateInfo;
}
} else {
layerInfos.clipInfoCount = clipInfoCount;
layerInfos.nextClipInfoCount = nextClipInfoCount;
layerInfos.interruptingClipInfoCount = 0;
layerInfos.isLastFrameOfInterruption = false;
if (clipInfos.Capacity < clipInfoCount) clipInfos.Capacity = clipInfoCount;
if (nextClipInfos.Capacity < nextClipInfoCount) nextClipInfos.Capacity = nextClipInfoCount;
animator.GetCurrentAnimatorClipInfo(layer, clipInfos);
animator.GetNextAnimatorClipInfo(layer, nextClipInfos);
layerInfos.stateInfo = animator.GetCurrentAnimatorStateInfo(layer);
layerInfos.nextStateInfo = animator.GetNextAnimatorStateInfo(layer);
}
}
void GetAnimatorClipInfos (
int layer,
out bool isInterruptionActive,
out int clipInfoCount,
out int nextClipInfoCount,
out int interruptingClipInfoCount,
out IList<AnimatorClipInfo> clipInfo,
out IList<AnimatorClipInfo> nextClipInfo,
out IList<AnimatorClipInfo> interruptingClipInfo,
out bool shallInterpolateWeightTo1) {
var layerInfos = layerClipInfos[layer];
isInterruptionActive = layerInfos.isInterruptionActive;
clipInfoCount = layerInfos.clipInfoCount;
nextClipInfoCount = layerInfos.nextClipInfoCount;
interruptingClipInfoCount = layerInfos.interruptingClipInfoCount;
clipInfo = layerInfos.clipInfos;
nextClipInfo = layerInfos.nextClipInfos;
interruptingClipInfo = isInterruptionActive ? layerInfos.interruptingClipInfos : null;
shallInterpolateWeightTo1 = layerInfos.isLastFrameOfInterruption;
}
void GetAnimatorStateInfos (
int layer,
out bool isInterruptionActive,
out AnimatorStateInfo stateInfo,
out AnimatorStateInfo nextStateInfo,
out AnimatorStateInfo interruptingStateInfo,
out float interruptingClipTimeAddition) {
var layerInfos = layerClipInfos[layer];
isInterruptionActive = layerInfos.isInterruptionActive;
stateInfo = layerInfos.stateInfo;
nextStateInfo = layerInfos.nextStateInfo;
interruptingStateInfo = layerInfos.interruptingStateInfo;
interruptingClipTimeAddition = layerInfos.isLastFrameOfInterruption ? layerInfos.interruptingClipTimeAddition : 0;
}
Spine.Animation GetAnimation (AnimationClip clip) {
int clipNameHashCode;
if (!clipNameHashCodeTable.TryGetValue(clip, out clipNameHashCode)) {
clipNameHashCode = clip.name.GetHashCode();
clipNameHashCodeTable.Add(clip, clipNameHashCode);
}
Spine.Animation animation;
animationTable.TryGetValue(clipNameHashCode, out animation);
return animation;
}
class AnimationClipEqualityComparer : IEqualityComparer<AnimationClip> {
internal static readonly IEqualityComparer<AnimationClip> Instance = new AnimationClipEqualityComparer();
public bool Equals (AnimationClip x, AnimationClip y) { return x.GetInstanceID() == y.GetInstanceID(); }
public int GetHashCode (AnimationClip o) { return o.GetInstanceID(); }
}
class IntEqualityComparer : IEqualityComparer<int> {
internal static readonly IEqualityComparer<int> Instance = new IntEqualityComparer();
public bool Equals (int x, int y) { return x == y; }
public int GetHashCode(int o) { return o; }
}
}
}
}

View File

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

View File

@@ -0,0 +1,576 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
#if UNITY_2017_1_OR_NEWER
#define BUILT_IN_SPRITE_MASK_COMPONENT
#endif
#define SPINE_OPTIONAL_RENDEROVERRIDE
#define SPINE_OPTIONAL_MATERIALOVERRIDE
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>Base class of animated Spine skeleton components. This component manages and renders a skeleton.</summary>
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer)), DisallowMultipleComponent]
[HelpURL("http://esotericsoftware.com/spine-unity-rendering")]
public class SkeletonRenderer : MonoBehaviour, ISkeletonComponent, IHasSkeletonDataAsset {
[SerializeField] public SkeletonDataAsset skeletonDataAsset;
#region Initialization settings
/// <summary>Skin name to use when the Skeleton is initialized.</summary>
[SerializeField] [SpineSkin(defaultAsEmptyString:true)] public string initialSkinName;
/// <summary>Flip X and Y to use when the Skeleton is initialized.</summary>
[SerializeField] public bool initialFlipX, initialFlipY;
#endregion
#region Advanced Render Settings
// Submesh Separation
/// <summary>Slot names used to populate separatorSlots list when the Skeleton is initialized. Changing this after initialization does nothing.</summary>
[UnityEngine.Serialization.FormerlySerializedAs("submeshSeparators")][SerializeField][SpineSlot] protected string[] separatorSlotNames = new string[0];
/// <summary>Slots that determine where the render is split. This is used by components such as SkeletonRenderSeparator so that the skeleton can be rendered by two separate renderers on different GameObjects.</summary>
[System.NonSerialized] public readonly List<Slot> separatorSlots = new List<Slot>();
// Render Settings
[Range(-0.1f, 0f)] public float zSpacing;
/// <summary>Use Spine's clipping feature. If false, ClippingAttachments will be ignored.</summary>
public bool useClipping = true;
/// <summary>If true, triangles will not be updated. Enable this as an optimization if the skeleton does not make use of attachment swapping or hiding, or draw order keys. Otherwise, setting this to false may cause errors in rendering.</summary>
public bool immutableTriangles = false;
/// <summary>Multiply vertex color RGB with vertex color alpha. Set this to true if the shader used for rendering is a premultiplied alpha shader. Setting this to false disables single-batch additive slots.</summary>
public bool pmaVertexColors = true;
/// <summary>Clears the state of the render and skeleton when this component or its GameObject is disabled. This prevents previous state from being retained when it is enabled again. When pooling your skeleton, setting this to true can be helpful.</summary>
public bool clearStateOnDisable = false;
/// <summary>If true, second colors on slots will be added to the output Mesh as UV2 and UV3. A special "tint black" shader that interprets UV2 and UV3 as black point colors is required to render this properly.</summary>
public bool tintBlack = false;
/// <summary>If true, the renderer assumes the skeleton only requires one Material and one submesh to render. This allows the MeshGenerator to skip checking for changes in Materials. Enable this as an optimization if the skeleton only uses one Material.</summary>
/// <remarks>This disables SkeletonRenderSeparator functionality.</remarks>
public bool singleSubmesh = false;
/// <summary>If true, the mesh generator adds normals to the output mesh. For better performance and reduced memory requirements, use a shader that assumes the desired normal.</summary>
[UnityEngine.Serialization.FormerlySerializedAs("calculateNormals")] public bool addNormals = false;
/// <summary>If true, tangents are calculated every frame and added to the Mesh. Enable this when using a shader that uses lighting that requires tangents.</summary>
public bool calculateTangents = false;
#if BUILT_IN_SPRITE_MASK_COMPONENT
/// <summary>This enum controls the mode under which the sprite will interact with the masking system.</summary>
/// <remarks>Interaction modes with <see cref="UnityEngine.SpriteMask"/> components are identical to Unity's <see cref="UnityEngine.SpriteRenderer"/>,
/// see https://docs.unity3d.com/ScriptReference/SpriteMaskInteraction.html. </remarks>
public SpriteMaskInteraction maskInteraction = SpriteMaskInteraction.None;
[System.Serializable]
public class SpriteMaskInteractionMaterials {
/// <summary>Material references for switching material sets at runtime when <see cref="SkeletonRenderer.maskInteraction"/> changes to <see cref="SpriteMaskInteraction.None"/>.</summary>
public Material[] materialsMaskDisabled = new Material[0];
/// <summary>Material references for switching material sets at runtime when <see cref="SkeletonRenderer.maskInteraction"/> changes to <see cref="SpriteMaskInteraction.VisibleInsideMask"/>.</summary>
public Material[] materialsInsideMask = new Material[0];
/// <summary>Material references for switching material sets at runtime when <see cref="SkeletonRenderer.maskInteraction"/> changes to <see cref="SpriteMaskInteraction.VisibleOutsideMask"/>.</summary>
public Material[] materialsOutsideMask = new Material[0];
}
/// <summary>Material references for switching material sets at runtime when <see cref="SkeletonRenderer.maskInteraction"/> changes.</summary>
public SpriteMaskInteractionMaterials maskMaterials = new SpriteMaskInteractionMaterials();
/// <summary>Shader property ID used for the Stencil comparison function.</summary>
public static readonly int STENCIL_COMP_PARAM_ID = Shader.PropertyToID("_StencilComp");
/// <summary>Shader property value used as Stencil comparison function for <see cref="SpriteMaskInteraction.None"/>.</summary>
public const UnityEngine.Rendering.CompareFunction STENCIL_COMP_MASKINTERACTION_NONE = UnityEngine.Rendering.CompareFunction.Always;
/// <summary>Shader property value used as Stencil comparison function for <see cref="SpriteMaskInteraction.VisibleInsideMask"/>.</summary>
public const UnityEngine.Rendering.CompareFunction STENCIL_COMP_MASKINTERACTION_VISIBLE_INSIDE = UnityEngine.Rendering.CompareFunction.LessEqual;
/// <summary>Shader property value used as Stencil comparison function for <see cref="SpriteMaskInteraction.VisibleOutsideMask"/>.</summary>
public const UnityEngine.Rendering.CompareFunction STENCIL_COMP_MASKINTERACTION_VISIBLE_OUTSIDE = UnityEngine.Rendering.CompareFunction.Greater;
#if UNITY_EDITOR
private static bool haveStencilParametersBeenFixed = false;
#endif
#endif // #if BUILT_IN_SPRITE_MASK_COMPONENT
#endregion
#region Overrides
#if SPINE_OPTIONAL_RENDEROVERRIDE
// These are API for anything that wants to take over rendering for a SkeletonRenderer.
public bool disableRenderingOnOverride = true;
public delegate void InstructionDelegate (SkeletonRendererInstruction instruction);
event InstructionDelegate generateMeshOverride;
/// <summary>Allows separate code to take over rendering for this SkeletonRenderer component. The subscriber is passed a SkeletonRendererInstruction argument to determine how to render a skeleton.</summary>
public event InstructionDelegate GenerateMeshOverride {
add {
generateMeshOverride += value;
if (disableRenderingOnOverride && generateMeshOverride != null) {
Initialize(false);
meshRenderer.enabled = false;
}
}
remove {
generateMeshOverride -= value;
if (disableRenderingOnOverride && generateMeshOverride == null) {
Initialize(false);
meshRenderer.enabled = true;
}
}
}
/// <summary> Occurs after the vertex data is populated every frame, before the vertices are pushed into the mesh.</summary>
public event Spine.Unity.MeshGeneratorDelegate OnPostProcessVertices;
#endif
#if SPINE_OPTIONAL_MATERIALOVERRIDE
[System.NonSerialized] readonly Dictionary<Material, Material> customMaterialOverride = new Dictionary<Material, Material>();
/// <summary>Use this Dictionary to override a Material with a different Material.</summary>
public Dictionary<Material, Material> CustomMaterialOverride { get { return customMaterialOverride; } }
#endif
[System.NonSerialized] readonly Dictionary<Slot, Material> customSlotMaterials = new Dictionary<Slot, Material>();
/// <summary>Use this Dictionary to use a different Material to render specific Slots.</summary>
public Dictionary<Slot, Material> CustomSlotMaterials { get { return customSlotMaterials; } }
#endregion
#region Mesh Generator
[System.NonSerialized] readonly SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction();
readonly MeshGenerator meshGenerator = new MeshGenerator();
[System.NonSerialized] readonly MeshRendererBuffers rendererBuffers = new MeshRendererBuffers();
#endregion
#region Cached component references
MeshRenderer meshRenderer;
MeshFilter meshFilter;
#endregion
#region Skeleton
[System.NonSerialized] public bool valid;
[System.NonSerialized] public Skeleton skeleton;
public Skeleton Skeleton {
get {
Initialize(false);
return skeleton;
}
}
#endregion
public delegate void SkeletonRendererDelegate (SkeletonRenderer skeletonRenderer);
/// <summary>OnRebuild is raised after the Skeleton is successfully initialized.</summary>
public event SkeletonRendererDelegate OnRebuild;
public SkeletonDataAsset SkeletonDataAsset { get { return skeletonDataAsset; } } // ISkeletonComponent
#region Runtime Instantiation
public static T NewSpineGameObject<T> (SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer {
return SkeletonRenderer.AddSpineComponent<T>(new GameObject("New Spine GameObject"), skeletonDataAsset);
}
/// <summary>Add and prepare a Spine component that derives from SkeletonRenderer to a GameObject at runtime.</summary>
/// <typeparam name="T">T should be SkeletonRenderer or any of its derived classes.</typeparam>
public static T AddSpineComponent<T> (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer {
var c = gameObject.AddComponent<T>();
if (skeletonDataAsset != null) {
c.skeletonDataAsset = skeletonDataAsset;
c.Initialize(false);
}
return c;
}
/// <summary>Applies MeshGenerator settings to the SkeletonRenderer and its internal MeshGenerator.</summary>
public void SetMeshSettings (MeshGenerator.Settings settings) {
this.calculateTangents = settings.calculateTangents;
this.immutableTriangles = settings.immutableTriangles;
this.pmaVertexColors = settings.pmaVertexColors;
this.tintBlack = settings.tintBlack;
this.useClipping = settings.useClipping;
this.zSpacing = settings.zSpacing;
this.meshGenerator.settings = settings;
}
#endregion
public virtual void Awake () {
Initialize(false);
}
void OnDisable () {
if (clearStateOnDisable && valid)
ClearState();
}
void OnDestroy () {
rendererBuffers.Dispose();
valid = false;
}
/// <summary>
/// Clears the previously generated mesh and resets the skeleton's pose.</summary>
public virtual void ClearState () {
var meshFilter = GetComponent<MeshFilter>();
if (meshFilter != null) meshFilter.sharedMesh = null;
currentInstructions.Clear();
if (skeleton != null) skeleton.SetToSetupPose();
}
/// <summary>
/// Sets a minimum buffer size for the internal MeshGenerator to prevent excess allocations during animation.
/// </summary>
public void EnsureMeshGeneratorCapacity (int minimumVertexCount) {
meshGenerator.EnsureVertexCapacity(minimumVertexCount);
}
/// <summary>
/// Initialize this component. Attempts to load the SkeletonData and creates the internal Skeleton object and buffers.</summary>
/// <param name="overwrite">If set to <c>true</c>, it will overwrite internal objects if they were already generated. Otherwise, the initialized component will ignore subsequent calls to initialize.</param>
public virtual void Initialize (bool overwrite) {
if (valid && !overwrite)
return;
// Clear
{
if (meshFilter != null)
meshFilter.sharedMesh = null;
meshRenderer = GetComponent<MeshRenderer>();
if (meshRenderer != null && meshRenderer.enabled) meshRenderer.sharedMaterial = null;
currentInstructions.Clear();
rendererBuffers.Clear();
meshGenerator.Begin();
skeleton = null;
valid = false;
}
if (skeletonDataAsset == null)
return;
SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(false);
if (skeletonData == null) return;
valid = true;
meshFilter = GetComponent<MeshFilter>();
meshRenderer = GetComponent<MeshRenderer>();
rendererBuffers.Initialize();
skeleton = new Skeleton(skeletonData) {
scaleX = initialFlipX ? -1 : 1,
scaleY = initialFlipY ? -1 : 1
};
if (!string.IsNullOrEmpty(initialSkinName) && !string.Equals(initialSkinName, "default", System.StringComparison.Ordinal))
skeleton.SetSkin(initialSkinName);
separatorSlots.Clear();
for (int i = 0; i < separatorSlotNames.Length; i++)
separatorSlots.Add(skeleton.FindSlot(separatorSlotNames[i]));
LateUpdate(); // Generate mesh for the first frame it exists.
if (OnRebuild != null)
OnRebuild(this);
}
/// <summary>
/// Generates a new UnityEngine.Mesh from the internal Skeleton.</summary>
public virtual void LateUpdate () {
if (!valid) return;
#if SPINE_OPTIONAL_RENDEROVERRIDE
bool doMeshOverride = generateMeshOverride != null;
if ((!meshRenderer.enabled) && !doMeshOverride) return;
#else
const bool doMeshOverride = false;
if (!meshRenderer.enabled) return;
#endif
var currentInstructions = this.currentInstructions;
var workingSubmeshInstructions = currentInstructions.submeshInstructions;
var currentSmartMesh = rendererBuffers.GetNextMesh(); // Double-buffer for performance.
bool updateTriangles;
if (this.singleSubmesh) {
// STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes. =============================================
MeshGenerator.GenerateSingleSubmeshInstruction(currentInstructions, skeleton, skeletonDataAsset.atlasAssets[0].PrimaryMaterial);
// STEP 1.9. Post-process workingInstructions. ==================================================================================
#if SPINE_OPTIONAL_MATERIALOVERRIDE
if (customMaterialOverride.Count > 0) // isCustomMaterialOverridePopulated
MeshGenerator.TryReplaceMaterials(workingSubmeshInstructions, customMaterialOverride);
#endif
// STEP 2. Update vertex buffer based on verts from the attachments. ===========================================================
meshGenerator.settings = new MeshGenerator.Settings {
pmaVertexColors = this.pmaVertexColors,
zSpacing = this.zSpacing,
useClipping = this.useClipping,
tintBlack = this.tintBlack,
calculateTangents = this.calculateTangents,
addNormals = this.addNormals
};
meshGenerator.Begin();
updateTriangles = SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, currentSmartMesh.instructionUsed);
if (currentInstructions.hasActiveClipping) {
meshGenerator.AddSubmesh(workingSubmeshInstructions.Items[0], updateTriangles);
} else {
meshGenerator.BuildMeshWithArrays(currentInstructions, updateTriangles);
}
} else {
// STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes. =============================================
MeshGenerator.GenerateSkeletonRendererInstruction(currentInstructions, skeleton, customSlotMaterials, separatorSlots, doMeshOverride, this.immutableTriangles);
// STEP 1.9. Post-process workingInstructions. ==================================================================================
#if SPINE_OPTIONAL_MATERIALOVERRIDE
if (customMaterialOverride.Count > 0) // isCustomMaterialOverridePopulated
MeshGenerator.TryReplaceMaterials(workingSubmeshInstructions, customMaterialOverride);
#endif
#if SPINE_OPTIONAL_RENDEROVERRIDE
if (doMeshOverride) {
this.generateMeshOverride(currentInstructions);
if (disableRenderingOnOverride) return;
}
#endif
updateTriangles = SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, currentSmartMesh.instructionUsed);
// STEP 2. Update vertex buffer based on verts from the attachments. ===========================================================
meshGenerator.settings = new MeshGenerator.Settings {
pmaVertexColors = this.pmaVertexColors,
zSpacing = this.zSpacing,
useClipping = this.useClipping,
tintBlack = this.tintBlack,
calculateTangents = this.calculateTangents,
addNormals = this.addNormals
};
meshGenerator.Begin();
if (currentInstructions.hasActiveClipping)
meshGenerator.BuildMesh(currentInstructions, updateTriangles);
else
meshGenerator.BuildMeshWithArrays(currentInstructions, updateTriangles);
}
if (OnPostProcessVertices != null) OnPostProcessVertices.Invoke(this.meshGenerator.Buffers);
// STEP 3. Move the mesh data into a UnityEngine.Mesh ===========================================================================
var currentMesh = currentSmartMesh.mesh;
meshGenerator.FillVertexData(currentMesh);
rendererBuffers.UpdateSharedMaterials(workingSubmeshInstructions);
if (updateTriangles) { // Check if the triangles should also be updated.
meshGenerator.FillTriangles(currentMesh);
meshRenderer.sharedMaterials = rendererBuffers.GetUpdatedSharedMaterialsArray();
} else if (rendererBuffers.MaterialsChangedInLastUpdate()) {
meshRenderer.sharedMaterials = rendererBuffers.GetUpdatedSharedMaterialsArray();
}
meshGenerator.FillLateVertexData(currentMesh);
// STEP 4. The UnityEngine.Mesh is ready. Set it as the MeshFilter's mesh. Store the instructions used for that mesh. ===========
meshFilter.sharedMesh = currentMesh;
currentSmartMesh.instructionUsed.Set(currentInstructions);
#if BUILT_IN_SPRITE_MASK_COMPONENT
if (meshRenderer != null) {
AssignSpriteMaskMaterials();
}
#endif
}
public void FindAndApplySeparatorSlots (string startsWith, bool clearExistingSeparators = true, bool updateStringArray = false) {
if (string.IsNullOrEmpty(startsWith)) return;
FindAndApplySeparatorSlots(
(slotName) => slotName.StartsWith(startsWith),
clearExistingSeparators,
updateStringArray
);
}
public void FindAndApplySeparatorSlots (System.Func<string, bool> slotNamePredicate, bool clearExistingSeparators = true, bool updateStringArray = false) {
if (slotNamePredicate == null) return;
if (!valid) return;
if (clearExistingSeparators)
separatorSlots.Clear();
var slots = skeleton.slots;
foreach (var slot in slots) {
if (slotNamePredicate.Invoke(slot.data.name))
separatorSlots.Add(slot);
}
if (updateStringArray) {
var detectedSeparatorNames = new List<string>();
foreach (var slot in skeleton.slots) {
string slotName = slot.data.name;
if (slotNamePredicate.Invoke(slotName))
detectedSeparatorNames.Add(slotName);
}
if (!clearExistingSeparators) {
string[] originalNames = this.separatorSlotNames;
foreach (string originalName in originalNames)
detectedSeparatorNames.Add(originalName);
}
this.separatorSlotNames = detectedSeparatorNames.ToArray();
}
}
public void ReapplySeparatorSlotNames () {
if (!valid)
return;
separatorSlots.Clear();
for (int i = 0, n = separatorSlotNames.Length; i < n; i++) {
var slot = skeleton.FindSlot(separatorSlotNames[i]);
if (slot != null) {
separatorSlots.Add(slot);
}
#if UNITY_EDITOR
else
{
Debug.LogWarning(separatorSlotNames[i] + " is not a slot in " + skeletonDataAsset.skeletonJSON.name);
}
#endif
}
}
#if BUILT_IN_SPRITE_MASK_COMPONENT
private void AssignSpriteMaskMaterials()
{
#if UNITY_EDITOR
if (!Application.isPlaying && !UnityEditor.EditorApplication.isUpdating) {
EditorFixStencilCompParameters();
}
#endif
if (maskMaterials.materialsMaskDisabled.Length > 0 && maskMaterials.materialsMaskDisabled[0] != null &&
maskInteraction == SpriteMaskInteraction.None) {
this.meshRenderer.materials = maskMaterials.materialsMaskDisabled;
}
else if (maskInteraction == SpriteMaskInteraction.VisibleInsideMask) {
if (maskMaterials.materialsInsideMask.Length == 0 || maskMaterials.materialsInsideMask[0] == null) {
if (!InitSpriteMaskMaterialsInsideMask())
return;
}
this.meshRenderer.materials = maskMaterials.materialsInsideMask;
}
else if (maskInteraction == SpriteMaskInteraction.VisibleOutsideMask) {
if (maskMaterials.materialsOutsideMask.Length == 0 || maskMaterials.materialsOutsideMask[0] == null) {
if (!InitSpriteMaskMaterialsOutsideMask())
return;
}
this.meshRenderer.materials = maskMaterials.materialsOutsideMask;
}
}
private bool InitSpriteMaskMaterialsInsideMask()
{
return InitSpriteMaskMaterialsForMaskType(STENCIL_COMP_MASKINTERACTION_VISIBLE_INSIDE, ref maskMaterials.materialsInsideMask);
}
private bool InitSpriteMaskMaterialsOutsideMask()
{
return InitSpriteMaskMaterialsForMaskType(STENCIL_COMP_MASKINTERACTION_VISIBLE_OUTSIDE, ref maskMaterials.materialsOutsideMask);
}
private bool InitSpriteMaskMaterialsForMaskType(UnityEngine.Rendering.CompareFunction maskFunction, ref Material[] materialsToFill)
{
#if UNITY_EDITOR
if (!Application.isPlaying) {
return false;
}
#endif
var originalMaterials = maskMaterials.materialsMaskDisabled;
materialsToFill = new Material[originalMaterials.Length];
for (int i = 0; i < originalMaterials.Length; i++) {
Material newMaterial = new Material(originalMaterials[i]);
newMaterial.SetFloat(STENCIL_COMP_PARAM_ID, (int)maskFunction);
materialsToFill[i] = newMaterial;
}
return true;
}
#if UNITY_EDITOR
private void EditorFixStencilCompParameters() {
if (!haveStencilParametersBeenFixed && HasAnyStencilComp0Material()) {
haveStencilParametersBeenFixed = true;
FixAllProjectMaterialsStencilCompParameters();
}
}
private void FixAllProjectMaterialsStencilCompParameters() {
string[] materialGUIDS = UnityEditor.AssetDatabase.FindAssets("t:material");
foreach (var guid in materialGUIDS) {
string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
if (!string.IsNullOrEmpty(path)) {
var mat = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(path);
if (mat.HasProperty(STENCIL_COMP_PARAM_ID) && mat.GetFloat(STENCIL_COMP_PARAM_ID) == 0) {
mat.SetFloat(STENCIL_COMP_PARAM_ID, (int)STENCIL_COMP_MASKINTERACTION_NONE);
}
}
}
UnityEditor.AssetDatabase.Refresh();
UnityEditor.AssetDatabase.SaveAssets();
}
private bool HasAnyStencilComp0Material() {
if (meshRenderer == null)
return false;
foreach (var mat in meshRenderer.sharedMaterials) {
if (mat != null && mat.HasProperty(STENCIL_COMP_PARAM_ID)) {
float currentCompValue = mat.GetFloat(STENCIL_COMP_PARAM_ID);
if (currentCompValue == 0)
return true;
}
}
return false;
}
#endif // UNITY_EDITOR
#endif //#if BUILT_IN_SPRITE_MASK_COMPONENT
}
}

View File

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

View File

@@ -0,0 +1,72 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
namespace Spine.Unity {
public delegate void UpdateBonesDelegate (ISkeletonAnimation animated);
/// <summary>A Spine-Unity Component that animates a Skeleton but not necessarily with a Spine.AnimationState.</summary>
public interface ISkeletonAnimation {
event UpdateBonesDelegate UpdateLocal;
event UpdateBonesDelegate UpdateWorld;
event UpdateBonesDelegate UpdateComplete;
Skeleton Skeleton { get; }
}
/// <summary>Holds a reference to a SkeletonDataAsset.</summary>
public interface IHasSkeletonDataAsset {
/// <summary>Gets the SkeletonDataAsset of the Spine Component.</summary>
SkeletonDataAsset SkeletonDataAsset { get; }
}
/// <summary>A Spine-Unity Component that manages a Spine.Skeleton instance, instantiated from a SkeletonDataAsset.</summary>
public interface ISkeletonComponent {
/// <summary>Gets the SkeletonDataAsset of the Spine Component.</summary>
//[System.Obsolete]
SkeletonDataAsset SkeletonDataAsset { get; }
/// <summary>Gets the Spine.Skeleton instance of the Spine Component. This is equivalent to SkeletonRenderer's .skeleton.</summary>
Skeleton Skeleton { get; }
}
/// <summary>A Spine-Unity Component that uses a Spine.AnimationState to animate its skeleton.</summary>
public interface IAnimationStateComponent {
/// <summary>Gets the Spine.AnimationState of the animated Spine Component. This is equivalent to SkeletonAnimation.state.</summary>
AnimationState AnimationState { get; }
}
/// <summary>A Spine-Unity Component that holds a reference to a SkeletonRenderer.</summary>
public interface IHasSkeletonRenderer {
SkeletonRenderer SkeletonRenderer { get; }
}
/// <summary>A Spine-Unity Component that holds a reference to an ISkeletonComponent.</summary>
public interface IHasSkeletonComponent {
ISkeletonComponent SkeletonComponent { get; }
}
}

View File

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

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: c5d065c4fe677ad4495a852580ec32fa
folderAsset: yes
timeCreated: 1455493477
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,45 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
namespace Spine.Unity {
public class DoubleBuffered<T> where T : new() {
readonly T a = new T();
readonly T b = new T();
bool usingA;
public T GetCurrent () {
return usingA ? a : b;
}
public T GetNext () {
usingA = !usingA;
return usingA ? a : b;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 08b76da7751523448a87e528c48a5399
timeCreated: 1457396939
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f834c8746034db645a52a9506ff1de89
timeCreated: 1455416715
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: a7935399edad9a14bb5708cf59c94b67
folderAsset: yes
timeCreated: 1455489260
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,263 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Spine;
using Spine.Unity;
namespace Spine.Unity.Modules {
//[CreateAssetMenu(menuName = "Spine/SkeletonData Modifiers/Animation Match", order = 200)]
public class AnimationMatchModifierAsset : SkeletonDataModifierAsset {
public bool matchAllAnimations = true;
public override void Apply (SkeletonData skeletonData) {
if (matchAllAnimations)
AnimationTools.MatchAnimationTimelines(skeletonData.animations, skeletonData);
}
public static class AnimationTools {
#region Filler Timelines
/// <summary>
/// Matches the animation timelines across the given set of animations.
/// This allows unkeyed properties to assume setup pose when animations are naively mixed using Animation.Apply.
/// </summary>
/// <param name="animations">An enumerable collection animations whose timelines will be matched.</param>
/// <param name="skeletonData">The SkeletonData where the animations belong.</param>
public static void MatchAnimationTimelines (IEnumerable<Spine.Animation> animations, SkeletonData skeletonData) {
if (animations == null) return;
if (skeletonData == null) throw new System.ArgumentNullException("skeletonData", "Timelines can't be matched without a SkeletonData source.");
// Build a reference collection of timelines to match
// and a collection of dummy timelines that can be used to fill-in missing items.
var timelineDictionary = new Dictionary<int, Spine.Timeline>();
foreach (var animation in animations) {
foreach (var timeline in animation.timelines) {
if (timeline is EventTimeline) continue;
int propertyID = timeline.PropertyId;
if (!timelineDictionary.ContainsKey(propertyID)) {
timelineDictionary.Add(propertyID, GetFillerTimeline(timeline, skeletonData));
}
}
}
var idsToMatch = new List<int>(timelineDictionary.Keys);
// For each animation in the list, check for and add missing timelines.
var currentAnimationIDs = new HashSet<int>();
foreach (var animation in animations) {
currentAnimationIDs.Clear();
foreach (var timeline in animation.timelines) {
if (timeline is EventTimeline) continue;
currentAnimationIDs.Add(timeline.PropertyId);
}
var animationTimelines = animation.timelines;
foreach (int propertyID in idsToMatch) {
if (!currentAnimationIDs.Contains(propertyID))
animationTimelines.Add(timelineDictionary[propertyID]);
}
}
// These are locals, but sometimes Unity's GC does weird stuff. So let's clean up.
timelineDictionary.Clear();
timelineDictionary = null;
idsToMatch.Clear();
idsToMatch = null;
currentAnimationIDs.Clear();
currentAnimationIDs = null;
}
static Timeline GetFillerTimeline (Timeline timeline, SkeletonData skeletonData) {
int propertyID = timeline.PropertyId;
int tt = propertyID >> 24;
var timelineType = (TimelineType)tt;
switch (timelineType) {
// Bone
case TimelineType.Rotate:
return GetFillerTimeline((RotateTimeline)timeline, skeletonData);
case TimelineType.Translate:
return GetFillerTimeline((TranslateTimeline)timeline, skeletonData);
case TimelineType.Scale:
return GetFillerTimeline((ScaleTimeline)timeline, skeletonData);
case TimelineType.Shear:
return GetFillerTimeline((ShearTimeline)timeline, skeletonData);
// Slot
case TimelineType.Attachment:
return GetFillerTimeline((AttachmentTimeline)timeline, skeletonData);
case TimelineType.Color:
return GetFillerTimeline((ColorTimeline)timeline, skeletonData);
case TimelineType.TwoColor:
return GetFillerTimeline((TwoColorTimeline)timeline, skeletonData);
case TimelineType.Deform:
return GetFillerTimeline((DeformTimeline)timeline, skeletonData);
// Skeleton
case TimelineType.DrawOrder:
return GetFillerTimeline((DrawOrderTimeline)timeline, skeletonData);
// IK Constraint
case TimelineType.IkConstraint:
return GetFillerTimeline((IkConstraintTimeline)timeline, skeletonData);
// TransformConstraint
case TimelineType.TransformConstraint:
return GetFillerTimeline((TransformConstraintTimeline)timeline, skeletonData);
// Path Constraint
case TimelineType.PathConstraintPosition:
return GetFillerTimeline((PathConstraintPositionTimeline)timeline, skeletonData);
case TimelineType.PathConstraintSpacing:
return GetFillerTimeline((PathConstraintSpacingTimeline)timeline, skeletonData);
case TimelineType.PathConstraintMix:
return GetFillerTimeline((PathConstraintMixTimeline)timeline, skeletonData);
}
return null;
}
static RotateTimeline GetFillerTimeline (RotateTimeline timeline, SkeletonData skeletonData) {
var t = new RotateTimeline(1);
t.boneIndex = timeline.boneIndex;
t.SetFrame(0, 0, 0);
return t;
}
static TranslateTimeline GetFillerTimeline (TranslateTimeline timeline, SkeletonData skeletonData) {
var t = new TranslateTimeline(1);
t.boneIndex = timeline.boneIndex;
t.SetFrame(0, 0, 0, 0);
return t;
}
static ScaleTimeline GetFillerTimeline (ScaleTimeline timeline, SkeletonData skeletonData) {
var t = new ScaleTimeline(1);
t.boneIndex = timeline.boneIndex;
t.SetFrame(0, 0, 0, 0);
return t;
}
static ShearTimeline GetFillerTimeline (ShearTimeline timeline, SkeletonData skeletonData) {
var t = new ShearTimeline(1);
t.boneIndex = timeline.boneIndex;
t.SetFrame(0, 0, 0, 0);
return t;
}
static AttachmentTimeline GetFillerTimeline (AttachmentTimeline timeline, SkeletonData skeletonData) {
var t = new AttachmentTimeline(1);
t.slotIndex = timeline.slotIndex;
var slotData = skeletonData.slots.Items[t.slotIndex];
t.SetFrame(0, 0, slotData.attachmentName);
return t;
}
static ColorTimeline GetFillerTimeline (ColorTimeline timeline, SkeletonData skeletonData) {
var t = new ColorTimeline(1);
t.slotIndex = timeline.slotIndex;
var slotData = skeletonData.slots.Items[t.slotIndex];
t.SetFrame(0, 0, slotData.r, slotData.g, slotData.b, slotData.a);
return t;
}
static TwoColorTimeline GetFillerTimeline (TwoColorTimeline timeline, SkeletonData skeletonData) {
var t = new TwoColorTimeline(1);
t.slotIndex = timeline.slotIndex;
var slotData = skeletonData.slots.Items[t.slotIndex];
t.SetFrame(0, 0, slotData.r, slotData.g, slotData.b, slotData.a, slotData.r2, slotData.g2, slotData.b2);
return t;
}
static DeformTimeline GetFillerTimeline (DeformTimeline timeline, SkeletonData skeletonData) {
var t = new DeformTimeline(1);
t.slotIndex = timeline.slotIndex;
t.attachment = timeline.attachment;
if (t.attachment.IsWeighted()) {
t.SetFrame(0, 0, new float[t.attachment.vertices.Length]);
} else {
t.SetFrame(0, 0, t.attachment.vertices.Clone() as float[]);
}
return t;
}
static DrawOrderTimeline GetFillerTimeline (DrawOrderTimeline timeline, SkeletonData skeletonData) {
var t = new DrawOrderTimeline(1);
t.SetFrame(0, 0, null); // null means use setup pose in DrawOrderTimeline.Apply.
return t;
}
static IkConstraintTimeline GetFillerTimeline (IkConstraintTimeline timeline, SkeletonData skeletonData) {
var t = new IkConstraintTimeline(1);
var ikConstraintData = skeletonData.ikConstraints.Items[timeline.ikConstraintIndex];
t.SetFrame(0, 0, ikConstraintData.mix, ikConstraintData.bendDirection, ikConstraintData.compress, ikConstraintData.stretch);
return t;
}
static TransformConstraintTimeline GetFillerTimeline (TransformConstraintTimeline timeline, SkeletonData skeletonData) {
var t = new TransformConstraintTimeline(1);
var data = skeletonData.transformConstraints.Items[timeline.transformConstraintIndex];
t.SetFrame(0, 0, data.rotateMix, data.translateMix, data.scaleMix, data.shearMix);
return t;
}
static PathConstraintPositionTimeline GetFillerTimeline (PathConstraintPositionTimeline timeline, SkeletonData skeletonData) {
var t = new PathConstraintPositionTimeline(1);
var data = skeletonData.pathConstraints.Items[timeline.pathConstraintIndex];
t.SetFrame(0, 0, data.position);
return t;
}
static PathConstraintSpacingTimeline GetFillerTimeline (PathConstraintSpacingTimeline timeline, SkeletonData skeletonData) {
var t = new PathConstraintSpacingTimeline(1);
var data = skeletonData.pathConstraints.Items[timeline.pathConstraintIndex];
t.SetFrame(0, 0, data.spacing);
return t;
}
static PathConstraintMixTimeline GetFillerTimeline (PathConstraintMixTimeline timeline, SkeletonData skeletonData) {
var t = new PathConstraintMixTimeline(1);
var data = skeletonData.pathConstraints.Items[timeline.pathConstraintIndex];
t.SetFrame(0, 0, data.rotateMix, data.translateMix);
return t;
}
#endregion
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: df7d457928e0f4041a439f9847f72290
timeCreated: 1537527074
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,6 @@
AnimationMatchModifierAsset
===========================
This is a SkeletonDataModifierAsset. Add it to a SkeletonDataAsset to apply its effects when its SkeletonData is loaded.
AnimationMatchModifierAsset processes animations so that their timelines match. This allows them to function with naive Animation Apply systems such as SkeletonMecanim without the need for autoreset functionality.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b4436c0c78cc5ee469089ed864f4e1ea
timeCreated: 1537528259
licenseType: Pro
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: df7d457928e0f4041a439f9847f72290, type: 3}
m_Name: Default Match All Animations
m_EditorClassIdentifier:
matchAllAnimations: 1

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 8623082208cae724e88314ec951691e1
timeCreated: 1537527914
licenseType: Pro
NativeFormatImporter:
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: f601b524e4da4704a9e0f6b7e05d6ca6
folderAsset: yes
timeCreated: 1478437840
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8dd46dbf979fcb7459246cd37aad09ef
timeCreated: 1478437807
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 86b1ae3ec8681c646aea49654969b73c
folderAsset: yes
timeCreated: 1455492474
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,248 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
using System.Collections.Generic;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
public class BoundingBoxFollower : MonoBehaviour {
internal static bool DebugMessages = true;
#region Inspector
public SkeletonRenderer skeletonRenderer;
[SpineSlot(dataField: "skeletonRenderer", containsBoundingBoxes: true)]
public string slotName;
public bool isTrigger;
public bool clearStateOnDisable = true;
#endregion
Slot slot;
BoundingBoxAttachment currentAttachment;
string currentAttachmentName;
PolygonCollider2D currentCollider;
public readonly Dictionary<BoundingBoxAttachment, PolygonCollider2D> colliderTable = new Dictionary<BoundingBoxAttachment, PolygonCollider2D>();
public readonly Dictionary<BoundingBoxAttachment, string> nameTable = new Dictionary<BoundingBoxAttachment, string>();
public Slot Slot { get { return slot; } }
public BoundingBoxAttachment CurrentAttachment { get { return currentAttachment; } }
public string CurrentAttachmentName { get { return currentAttachmentName; } }
public PolygonCollider2D CurrentCollider { get { return currentCollider; } }
public bool IsTrigger { get { return isTrigger; } }
void Start () {
Initialize();
}
void OnEnable () {
if (skeletonRenderer != null) {
skeletonRenderer.OnRebuild -= HandleRebuild;
skeletonRenderer.OnRebuild += HandleRebuild;
}
Initialize();
}
void HandleRebuild (SkeletonRenderer sr) {
//if (BoundingBoxFollower.DebugMessages) Debug.Log("Skeleton was rebuilt. Repopulating BoundingBoxFollower.");
Initialize();
}
/// <summary>
/// Initialize and instantiate the BoundingBoxFollower colliders. This is method checks if the BoundingBoxFollower has already been initialized for the skeleton instance and slotName and prevents overwriting unless it detects a new setup.</summary>
public void Initialize (bool overwrite = false) {
if (skeletonRenderer == null)
return;
skeletonRenderer.Initialize(false);
if (string.IsNullOrEmpty(slotName))
return;
// Don't reinitialize if the setup did not change.
if (!overwrite
&&
colliderTable.Count > 0 && slot != null // Slot is set and colliders already populated.
&&
skeletonRenderer.skeleton == slot.Skeleton // Skeleton object did not change.
&&
slotName == slot.data.name // Slot object did not change.
)
return;
DisposeColliders();
var skeleton = skeletonRenderer.skeleton;
slot = skeleton.FindSlot(slotName);
int slotIndex = skeleton.FindSlotIndex(slotName);
if (slot == null) {
if (BoundingBoxFollower.DebugMessages)
Debug.LogWarning(string.Format("Slot '{0}' not found for BoundingBoxFollower on '{1}'. (Previous colliders were disposed.)", slotName, this.gameObject.name));
return;
}
if (this.gameObject.activeInHierarchy) {
foreach (var skin in skeleton.Data.Skins)
AddSkin(skin, slotIndex);
if (skeleton.skin != null)
AddSkin(skeleton.skin, slotIndex);
}
if (BoundingBoxFollower.DebugMessages) {
bool valid = colliderTable.Count != 0;
if (!valid) {
if (this.gameObject.activeInHierarchy)
Debug.LogWarning("Bounding Box Follower not valid! Slot [" + slotName + "] does not contain any Bounding Box Attachments!");
else
Debug.LogWarning("Bounding Box Follower tried to rebuild as a prefab.");
}
}
}
void AddSkin (Skin skin, int slotIndex) {
if (skin == null) return;
var attachmentNames = new List<string>();
skin.FindNamesForSlot(slotIndex, attachmentNames);
foreach (var skinKey in attachmentNames) {
var attachment = skin.GetAttachment(slotIndex, skinKey);
var boundingBoxAttachment = attachment as BoundingBoxAttachment;
if (BoundingBoxFollower.DebugMessages && attachment != null && boundingBoxAttachment == null)
Debug.Log("BoundingBoxFollower tried to follow a slot that contains non-boundingbox attachments: " + slotName);
if (boundingBoxAttachment != null) {
if (!colliderTable.ContainsKey(boundingBoxAttachment)) {
var bbCollider = SkeletonUtility.AddBoundingBoxAsComponent(boundingBoxAttachment, slot, gameObject, isTrigger);
bbCollider.enabled = false;
bbCollider.hideFlags = HideFlags.NotEditable;
bbCollider.isTrigger = IsTrigger;
colliderTable.Add(boundingBoxAttachment, bbCollider);
nameTable.Add(boundingBoxAttachment, skinKey);
}
}
}
}
void OnDisable () {
if (clearStateOnDisable)
ClearState();
}
public void ClearState () {
if (colliderTable != null)
foreach (var col in colliderTable.Values)
col.enabled = false;
currentAttachment = null;
currentAttachmentName = null;
currentCollider = null;
}
void DisposeColliders () {
var colliders = GetComponents<PolygonCollider2D>();
if (colliders.Length == 0) return;
if (Application.isEditor) {
if (Application.isPlaying) {
foreach (var c in colliders) {
if (c != null)
Destroy(c);
}
} else {
foreach (var c in colliders)
if (c != null)
DestroyImmediate(c);
}
} else {
foreach (PolygonCollider2D c in colliders)
if (c != null)
Destroy(c);
}
slot = null;
currentAttachment = null;
currentAttachmentName = null;
currentCollider = null;
colliderTable.Clear();
nameTable.Clear();
}
void LateUpdate () {
if (slot != null && slot.Attachment != currentAttachment)
MatchAttachment(slot.Attachment);
}
/// <summary>Sets the current collider to match attachment.</summary>
/// <param name="attachment">If the attachment is not a bounding box, it will be treated as null.</param>
void MatchAttachment (Attachment attachment) {
var bbAttachment = attachment as BoundingBoxAttachment;
if (BoundingBoxFollower.DebugMessages && attachment != null && bbAttachment == null)
Debug.LogWarning("BoundingBoxFollower tried to match a non-boundingbox attachment. It will treat it as null.");
if (currentCollider != null)
currentCollider.enabled = false;
if (bbAttachment == null) {
currentCollider = null;
currentAttachment = null;
currentAttachmentName = null;
} else {
PolygonCollider2D foundCollider;
colliderTable.TryGetValue(bbAttachment, out foundCollider);
if (foundCollider != null) {
currentCollider = foundCollider;
currentCollider.enabled = true;
currentAttachment = bbAttachment;
currentAttachmentName = nameTable[bbAttachment];
} else {
currentCollider = null;
currentAttachment = bbAttachment;
currentAttachmentName = null;
if (BoundingBoxFollower.DebugMessages) Debug.LogFormat("Collider for BoundingBoxAttachment named '{0}' was not initialized. It is possibly from a new skin. currentAttachmentName will be null. You may need to call BoundingBoxFollower.Initialize(overwrite: true);", bbAttachment.Name);
}
}
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0317ee9ba6e1b1e49a030268e026d372
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a7236dbdc6a4e5a4989483dac97aee0b
folderAsset: yes
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,211 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
#define SPINE_OPTIONAL_MATERIALOVERRIDE
// Contributed by: Lost Polygon
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity.Modules {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
public class SkeletonRendererCustomMaterials : MonoBehaviour {
#region Inspector
public SkeletonRenderer skeletonRenderer;
[SerializeField] protected List<SlotMaterialOverride> customSlotMaterials = new List<SlotMaterialOverride>();
[SerializeField] protected List<AtlasMaterialOverride> customMaterialOverrides = new List<AtlasMaterialOverride>();
#if UNITY_EDITOR
void Reset () {
skeletonRenderer = GetComponent<SkeletonRenderer>();
// Populate atlas list
if (skeletonRenderer != null && skeletonRenderer.skeletonDataAsset != null) {
var atlasAssets = skeletonRenderer.skeletonDataAsset.atlasAssets;
var initialAtlasMaterialOverrides = new List<AtlasMaterialOverride>();
foreach (AtlasAssetBase atlasAsset in atlasAssets) {
foreach (Material atlasMaterial in atlasAsset.Materials) {
var atlasMaterialOverride = new AtlasMaterialOverride {
overrideDisabled = true,
originalMaterial = atlasMaterial
};
initialAtlasMaterialOverrides.Add(atlasMaterialOverride);
}
}
customMaterialOverrides = initialAtlasMaterialOverrides;
}
}
#endif
#endregion
void SetCustomSlotMaterials () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
for (int i = 0; i < customSlotMaterials.Count; i++) {
SlotMaterialOverride slotMaterialOverride = customSlotMaterials[i];
if (slotMaterialOverride.overrideDisabled || string.IsNullOrEmpty(slotMaterialOverride.slotName))
continue;
Slot slotObject = skeletonRenderer.skeleton.FindSlot(slotMaterialOverride.slotName);
skeletonRenderer.CustomSlotMaterials[slotObject] = slotMaterialOverride.material;
}
}
void RemoveCustomSlotMaterials () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
for (int i = 0; i < customSlotMaterials.Count; i++) {
SlotMaterialOverride slotMaterialOverride = customSlotMaterials[i];
if (string.IsNullOrEmpty(slotMaterialOverride.slotName))
continue;
Slot slotObject = skeletonRenderer.skeleton.FindSlot(slotMaterialOverride.slotName);
Material currentMaterial;
if (!skeletonRenderer.CustomSlotMaterials.TryGetValue(slotObject, out currentMaterial))
continue;
// Do not revert the material if it was changed by something else
if (currentMaterial != slotMaterialOverride.material)
continue;
skeletonRenderer.CustomSlotMaterials.Remove(slotObject);
}
}
void SetCustomMaterialOverrides () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
#if SPINE_OPTIONAL_MATERIALOVERRIDE
for (int i = 0; i < customMaterialOverrides.Count; i++) {
AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i];
if (atlasMaterialOverride.overrideDisabled)
continue;
skeletonRenderer.CustomMaterialOverride[atlasMaterialOverride.originalMaterial] = atlasMaterialOverride.replacementMaterial;
}
#endif
}
void RemoveCustomMaterialOverrides () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
#if SPINE_OPTIONAL_MATERIALOVERRIDE
for (int i = 0; i < customMaterialOverrides.Count; i++) {
AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i];
Material currentMaterial;
if (!skeletonRenderer.CustomMaterialOverride.TryGetValue(atlasMaterialOverride.originalMaterial, out currentMaterial))
continue;
// Do not revert the material if it was changed by something else
if (currentMaterial != atlasMaterialOverride.replacementMaterial)
continue;
skeletonRenderer.CustomMaterialOverride.Remove(atlasMaterialOverride.originalMaterial);
}
#endif
}
// OnEnable applies the overrides at runtime, and when the editor loads.
void OnEnable () {
if (skeletonRenderer == null)
skeletonRenderer = GetComponent<SkeletonRenderer>();
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
skeletonRenderer.Initialize(false);
SetCustomMaterialOverrides();
SetCustomSlotMaterials();
}
// OnDisable removes the overrides at runtime, and in the editor when the component is disabled or destroyed.
void OnDisable () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
RemoveCustomMaterialOverrides();
RemoveCustomSlotMaterials();
}
[Serializable]
public struct SlotMaterialOverride : IEquatable<SlotMaterialOverride> {
public bool overrideDisabled;
[SpineSlot]
public string slotName;
public Material material;
public bool Equals (SlotMaterialOverride other) {
return overrideDisabled == other.overrideDisabled && slotName == other.slotName && material == other.material;
}
}
[Serializable]
public struct AtlasMaterialOverride : IEquatable<AtlasMaterialOverride> {
public bool overrideDisabled;
public Material originalMaterial;
public Material replacementMaterial;
public bool Equals (AtlasMaterialOverride other) {
return overrideDisabled == other.overrideDisabled && originalMaterial == other.originalMaterial && replacementMaterial == other.replacementMaterial;
}
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 26947ae098a8447408d80c0c86e35b48
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
SkeletonRendererCustomMaterials by LostPolygon
===============================
This is a basic serialization and inspector implementation for custom material overrides for SkeletonRenderer and its derived classes (SkeletonAnimation, SkeletonAnimator).
## How to use
Right-click on your SkeletonRenderer and select "Add Basic Serialized Custom Materials". This will add and initialize the SkeletonRendererCustomMaterials to the same object.
You can use this to store material override settings for SkeletonRenderer instances/prefabs so they will be applied automatically when your scene starts or when the prefab is instantiated.
This script is not intended for use with code.
To dynamically set materials for your SkeletonRenderer through code, you can directly access `SkeletonRenderer.CustomMaterialOverride` for material array overrides and `SkeletonRenderer.CustomSlotMaterials` for slot material overrides.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3d4db6c367e463c4cb5566afc490163c
timeCreated: 1460572571
licenseType: Free
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: 13193c9d213765f4c85f4c1faa615711
folderAsset: yes
DefaultImporter:
userData:

View File

@@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: a0cee0de78cef7440ae0b5aac39ae971
folderAsset: yes
DefaultImporter:
userData:

View File

@@ -0,0 +1,67 @@
// - Unlit + no shadow
// - Premultiplied Alpha Blending (One OneMinusSrcAlpha)
// - Double-sided, no depth
Shader "Spine/Special/SkeletonGhost" {
Properties {
_Color ("Main Color", Color) = (1,1,1,1)
[NoScaleOffset] _MainTex ("Base (RGB) Alpha (A)", 2D) = "white" {}
_TextureFade ("Texture Fade Out", Range(0,1)) = 0
[HideInInspector] _StencilRef("Stencil Reference", Float) = 1.0
[Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp("Stencil Comparison", Float) = 8 // Set to Always as default
}
SubShader {
Tags {
"Queue"="Transparent"
"IgnoreProjector"="False"
"RenderType"="Transparent"
}
Fog { Mode Off }
Blend One OneMinusSrcAlpha
ZWrite Off
Cull Off
Stencil {
Ref[_StencilRef]
Comp[_StencilComp]
Pass Keep
}
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed4 _Color;
fixed _TextureFade;
struct VertexInput {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR;
};
struct VertexOutput {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR;
};
VertexOutput vert (VertexInput v) {
VertexOutput o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.color = v.color;
return o;
}
fixed4 frag (VertexOutput i) : COLOR {
fixed4 tc = tex2D(_MainTex, i.uv);
tc = fixed4(max(_TextureFade, tc.r), max(_TextureFade, tc.g), max(_TextureFade, tc.b), tc.a);
return tc * ((i.color * _Color) * tc.a);
}
ENDCG
}
}
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3873d4699ee8a4b4da8fa6b8c229b94d
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,201 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
// Contributed by: Mitch Thompson
using UnityEngine;
using System.Collections.Generic;
namespace Spine.Unity.Modules {
[RequireComponent(typeof(SkeletonRenderer))]
public class SkeletonGhost : MonoBehaviour {
// Internal Settings
const HideFlags GhostHideFlags = HideFlags.HideInHierarchy;
const string GhostingShaderName = "Spine/Special/SkeletonGhost";
[Header("Animation")]
public bool ghostingEnabled = true;
[Tooltip("The time between invididual ghost pieces being spawned.")]
[UnityEngine.Serialization.FormerlySerializedAs("spawnRate")]
public float spawnInterval = 1f/30f;
[Tooltip("Maximum number of ghosts that can exist at a time. If the fade speed is not fast enough, the oldest ghost will immediately disappear to enforce the maximum number.")]
public int maximumGhosts = 10;
public float fadeSpeed = 10;
[Header("Rendering")]
public Shader ghostShader;
public Color32 color = new Color32(0xFF, 0xFF, 0xFF, 0x00); // default for additive.
[Tooltip("Remember to set color alpha to 0 if Additive is true")]
public bool additive = true;
[Tooltip("0 is Color and Alpha, 1 is Alpha only.")]
[Range(0, 1)]
public float textureFade = 1;
[Header("Sorting")]
public bool sortWithDistanceOnly;
public float zOffset = 0f;
float nextSpawnTime;
SkeletonGhostRenderer[] pool;
int poolIndex = 0;
SkeletonRenderer skeletonRenderer;
MeshRenderer meshRenderer;
MeshFilter meshFilter;
readonly Dictionary<Material, Material> materialTable = new Dictionary<Material, Material>();
void Start () {
Initialize(false);
}
public void Initialize (bool overwrite) {
if (pool == null || overwrite) {
if (ghostShader == null)
ghostShader = Shader.Find(GhostingShaderName);
skeletonRenderer = GetComponent<SkeletonRenderer>();
meshFilter = GetComponent<MeshFilter>();
meshRenderer = GetComponent<MeshRenderer>();
nextSpawnTime = Time.time + spawnInterval;
pool = new SkeletonGhostRenderer[maximumGhosts];
for (int i = 0; i < maximumGhosts; i++) {
GameObject go = new GameObject(gameObject.name + " Ghost", typeof(SkeletonGhostRenderer));
pool[i] = go.GetComponent<SkeletonGhostRenderer>();
go.SetActive(false);
go.hideFlags = GhostHideFlags;
}
var skeletonAnimation = skeletonRenderer as Spine.Unity.IAnimationStateComponent;
if (skeletonAnimation != null)
skeletonAnimation.AnimationState.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.TrackEntry trackEntry, Spine.Event e) {
if (e.Data.Name.Equals("Ghosting", System.StringComparison.Ordinal)) {
ghostingEnabled = e.Int > 0;
if (e.Float > 0)
spawnInterval = e.Float;
if (!string.IsNullOrEmpty(e.stringValue))
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) {
shader = ghostShader,
color = Color.white
};
if (ghostMat.HasProperty("_TextureFade"))
ghostMat.SetFloat("_TextureFade", textureFade);
materialTable.Add(originalMat, ghostMat);
} else {
ghostMat = materialTable[originalMat];
}
materials[i] = ghostMat;
}
var goTransform = go.transform;
goTransform.parent = transform;
pool[poolIndex].Initialize(meshFilter.sharedMesh, materials, color, additive, fadeSpeed, meshRenderer.sortingLayerID, (sortWithDistanceOnly) ? meshRenderer.sortingOrder : meshRenderer.sortingOrder - 1);
goTransform.localPosition = new Vector3(0f, 0f, zOffset);
goTransform.localRotation = Quaternion.identity;
goTransform.localScale = Vector3.one;
goTransform.parent = null;
poolIndex++;
if (poolIndex == pool.Length)
poolIndex = 0;
nextSpawnTime = Time.time + spawnInterval;
}
}
void OnDestroy () {
if (pool != null) {
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) {
const System.Globalization.NumberStyles HexNumber = System.Globalization.NumberStyles.HexNumber;
if (hex.Length < 6)
return Color.magenta;
hex = hex.Replace("#", "");
byte r = byte.Parse(hex.Substring(0, 2), HexNumber);
byte g = byte.Parse(hex.Substring(2, 2), HexNumber);
byte b = byte.Parse(hex.Substring(4, 2), HexNumber);
byte a = 0xFF;
if (hex.Length == 8)
a = byte.Parse(hex.Substring(6, 2), HexNumber);
return new Color32(r, g, b, a);
}
}
}

View File

@@ -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:

View File

@@ -0,0 +1,128 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
// Contributed by: Mitch Thompson
using UnityEngine;
using System.Collections;
namespace Spine.Unity.Modules {
public class SkeletonGhostRenderer : MonoBehaviour {
static readonly Color32 TransparentBlack = new Color32(0, 0, 0, 0);
const string colorPropertyName = "_Color";
float fadeSpeed = 10;
Color32 startColor;
MeshFilter meshFilter;
MeshRenderer meshRenderer;
MaterialPropertyBlock mpb;
int colorId;
void Awake () {
meshRenderer = gameObject.AddComponent<MeshRenderer>();
meshFilter = gameObject.AddComponent<MeshFilter>();
colorId = Shader.PropertyToID(colorPropertyName);
mpb = new MaterialPropertyBlock();
}
public void Initialize (Mesh mesh, Material[] materials, Color32 color, bool additive, float speed, int sortingLayerID, int sortingOrder) {
StopAllCoroutines();
gameObject.SetActive(true);
meshRenderer.sharedMaterials = materials;
meshRenderer.sortingLayerID = sortingLayerID;
meshRenderer.sortingOrder = sortingOrder;
meshFilter.sharedMesh = Instantiate(mesh);
startColor = color;
mpb.SetColor(colorId, color);
meshRenderer.SetPropertyBlock(mpb);
fadeSpeed = speed;
if (additive)
StartCoroutine(FadeAdditive());
else
StartCoroutine(Fade());
}
IEnumerator Fade () {
Color32 c = startColor;
Color32 black = SkeletonGhostRenderer.TransparentBlack;
float t = 1f;
for (float hardTimeLimit = 5f; hardTimeLimit > 0; hardTimeLimit -= Time.deltaTime) {
c = Color32.Lerp(black, startColor, t);
mpb.SetColor(colorId, c);
meshRenderer.SetPropertyBlock(mpb);
t = Mathf.Lerp(t, 0, Time.deltaTime * fadeSpeed);
if (t <= 0)
break;
yield return null;
}
Destroy(meshFilter.sharedMesh);
gameObject.SetActive(false);
}
IEnumerator FadeAdditive () {
Color32 c = startColor;
Color32 black = SkeletonGhostRenderer.TransparentBlack;
float t = 1f;
for (float hardTimeLimit = 5f; hardTimeLimit > 0; hardTimeLimit -= Time.deltaTime) {
c = Color32.Lerp(black, startColor, t);
mpb.SetColor(colorId, c);
meshRenderer.SetPropertyBlock(mpb);
t = Mathf.Lerp(t, 0, Time.deltaTime * fadeSpeed);
if (t <= 0)
break;
yield return null;
}
Destroy(meshFilter.sharedMesh);
gameObject.SetActive(false);
}
public void Cleanup () {
if (meshFilter != null && meshFilter.sharedMesh != null)
Destroy(meshFilter.sharedMesh);
Destroy(gameObject);
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 58e3a9b80754b7545a1dff4d8475b51f
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: 90af663b37d994841b7ac03ae30fe2a9
folderAsset: yes
DefaultImporter:
userData:

View File

@@ -0,0 +1,404 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
// Contributed by: Mitch Thompson
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace Spine.Unity.Modules {
[RequireComponent(typeof(SkeletonRenderer))]
public class SkeletonRagdoll : MonoBehaviour {
static Transform parentSpaceHelper;
#region Inspector
[Header("Hierarchy")]
[SpineBone]
public string startingBoneName = "";
[SpineBone]
public List<string> stopBoneNames = new List<string>();
[Header("Parameters")]
public bool applyOnStart;
[Tooltip("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")]
public bool disableIK = true;
public bool disableOtherConstraints = false;
[Space(18)]
[Tooltip("Set RootRigidbody IsKinematic to true when Apply is called.")]
public bool pinStartBone;
[Tooltip("Enable Collision between adjacent ragdoll elements (IE: Neck and Head)")]
public bool enableJointCollision;
public bool useGravity = true;
[Tooltip("If no BoundingBox Attachment is attached to a bone, this becomes the default Width or Radius of a Bone's ragdoll Rigidbody")]
public float thickness = 0.125f;
[Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")]
public float rotationLimit = 20;
public float rootMass = 20;
[Tooltip("If your ragdoll seems unstable or uneffected by limits, try lowering this value.")]
[Range(0.01f, 1f)]
public float massFalloffFactor = 0.4f;
[Tooltip("The layer assigned to all of the rigidbody parts.")]
public int colliderLayer = 0;
[Range(0, 1)]
public float mix = 1;
#endregion
ISkeletonAnimation targetSkeletonComponent;
Skeleton skeleton;
Dictionary<Bone, Transform> boneTable = new Dictionary<Bone, Transform>();
Transform ragdollRoot;
public Rigidbody RootRigidbody { get; private set; }
public Bone StartingBone { get; private set; }
Vector3 rootOffset;
public Vector3 RootOffset { get { return this.rootOffset; } }
bool isActive;
public bool IsActive { get { return this.isActive; } }
IEnumerator Start () {
if (parentSpaceHelper == null) {
parentSpaceHelper = (new GameObject("Parent Space Helper")).transform;
parentSpaceHelper.hideFlags = HideFlags.HideInHierarchy;
}
targetSkeletonComponent = GetComponent<SkeletonRenderer>() as ISkeletonAnimation;
if (targetSkeletonComponent == null) Debug.LogError("Attached Spine component does not implement ISkeletonAnimation. This script is not compatible.");
skeleton = targetSkeletonComponent.Skeleton;
if (applyOnStart) {
yield return null;
Apply();
}
}
#region API
public Rigidbody[] RigidbodyArray {
get {
if (!isActive)
return new Rigidbody[0];
var rigidBodies = new Rigidbody[boneTable.Count];
int i = 0;
foreach (Transform t in boneTable.Values) {
rigidBodies[i] = t.GetComponent<Rigidbody>();
i++;
}
return rigidBodies;
}
}
public Vector3 EstimatedSkeletonPosition {
get { return RootRigidbody.position - rootOffset; }
}
/// <summary>Instantiates the ragdoll simulation and applies its transforms to the skeleton.</summary>
public void Apply () {
isActive = true;
mix = 1;
StartingBone = skeleton.FindBone(startingBoneName);
RecursivelyCreateBoneProxies(StartingBone);
RootRigidbody = boneTable[StartingBone].GetComponent<Rigidbody>();
RootRigidbody.isKinematic = pinStartBone;
RootRigidbody.mass = rootMass;
var boneColliders = new List<Collider>();
foreach (var pair in boneTable) {
var b = pair.Key;
var t = pair.Value;
Transform parentTransform;
boneColliders.Add(t.GetComponent<Collider>());
if (b == StartingBone) {
ragdollRoot = new GameObject("RagdollRoot").transform;
ragdollRoot.SetParent(transform, false);
if (b == skeleton.RootBone) { // RagdollRoot is skeleton root.
ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetPropagatedRotation(b));
} else {
ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetPropagatedRotation(b.Parent));
}
parentTransform = ragdollRoot;
rootOffset = t.position - transform.position;
} else {
parentTransform = boneTable[b.Parent];
}
// Add joint and attach to parent.
var rbParent = parentTransform.GetComponent<Rigidbody>();
if (rbParent != null) {
var joint = t.gameObject.AddComponent<HingeJoint>();
joint.connectedBody = rbParent;
Vector3 localPos = parentTransform.InverseTransformPoint(t.position);
localPos.x *= 1;
joint.connectedAnchor = localPos;
joint.axis = Vector3.forward;
joint.GetComponent<Rigidbody>().mass = joint.connectedBody.mass * massFalloffFactor;
joint.limits = new JointLimits {
min = -rotationLimit,
max = rotationLimit,
};
joint.useLimits = true;
joint.enableCollision = enableJointCollision;
}
}
// Ignore collisions among bones.
for (int x = 0; x < boneColliders.Count; x++) {
for (int y = 0; y < boneColliders.Count; y++) {
if (x == y) continue;
Physics.IgnoreCollision(boneColliders[x], boneColliders[y]);
}
}
// Destroy existing override-mode SkeletonUtilityBones.
var utilityBones = GetComponentsInChildren<SkeletonUtilityBone>();
if (utilityBones.Length > 0) {
var destroyedUtilityBoneNames = new List<string>();
foreach (var ub in utilityBones) {
if (ub.mode == SkeletonUtilityBone.Mode.Override) {
destroyedUtilityBoneNames.Add(ub.gameObject.name);
Destroy(ub.gameObject);
}
}
if (destroyedUtilityBoneNames.Count > 0) {
string msg = "Destroyed Utility Bones: ";
for (int i = 0; i < destroyedUtilityBoneNames.Count; i++) {
msg += destroyedUtilityBoneNames[i];
if (i != destroyedUtilityBoneNames.Count - 1) {
msg += ",";
}
}
Debug.LogWarning(msg);
}
}
// Disable skeleton constraints.
if (disableIK) {
var ikConstraints = skeleton.IkConstraints;
for (int i = 0, n = ikConstraints.Count; i < n; i++)
ikConstraints.Items[i].mix = 0;
}
if (disableOtherConstraints) {
var transformConstraints = skeleton.transformConstraints;
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
transformConstraints.Items[i].rotateMix = 0;
transformConstraints.Items[i].scaleMix = 0;
transformConstraints.Items[i].shearMix = 0;
transformConstraints.Items[i].translateMix = 0;
}
var pathConstraints = skeleton.pathConstraints;
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
pathConstraints.Items[i].rotateMix = 0;
pathConstraints.Items[i].translateMix = 0;
}
}
targetSkeletonComponent.UpdateWorld += UpdateSpineSkeleton;
}
/// <summary>Transitions the mix value from the current value to a target value.</summary>
public Coroutine SmoothMix (float target, float duration) {
return StartCoroutine(SmoothMixCoroutine(target, duration));
}
IEnumerator SmoothMixCoroutine (float target, float duration) {
float startTime = Time.time;
float startMix = mix;
while (mix > 0) {
skeleton.SetBonesToSetupPose();
mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration);
yield return null;
}
}
/// <summary>Set the transform world position while preserving the ragdoll parts world position.</summary>
public void SetSkeletonPosition (Vector3 worldPosition) {
if (!isActive) {
Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!");
return;
}
Vector3 offset = worldPosition - transform.position;
transform.position = worldPosition;
foreach (Transform t in boneTable.Values)
t.position -= offset;
UpdateSpineSkeleton(null);
skeleton.UpdateWorldTransform();
}
/// <summary>Removes the ragdoll instance and effect from the animated skeleton.</summary>
public void Remove () {
isActive = false;
foreach (var t in boneTable.Values)
Destroy(t.gameObject);
Destroy(ragdollRoot.gameObject);
boneTable.Clear();
targetSkeletonComponent.UpdateWorld -= UpdateSpineSkeleton;
}
public Rigidbody GetRigidbody (string boneName) {
var bone = skeleton.FindBone(boneName);
return (bone != null && boneTable.ContainsKey(bone)) ? boneTable[bone].GetComponent<Rigidbody>() : null;
}
#endregion
void RecursivelyCreateBoneProxies (Bone b) {
string boneName = b.data.name;
if (stopBoneNames.Contains(boneName))
return;
var boneGameObject = new GameObject(boneName);
boneGameObject.layer = colliderLayer;
Transform t = boneGameObject.transform;
boneTable.Add(b, t);
t.parent = transform;
t.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX - b.shearX);
t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 1);
// MITCH: You left "todo: proper ragdoll branching"
var colliders = AttachBoundingBoxRagdollColliders(b);
if (colliders.Count == 0) {
float length = b.Data.Length;
if (length == 0) {
var ball = boneGameObject.AddComponent<SphereCollider>();
ball.radius = thickness * 0.5f;
} else {
var box = boneGameObject.AddComponent<BoxCollider>();
box.size = new Vector3(length, thickness, thickness);
box.center = new Vector3(length * 0.5f, 0);
}
}
var rb = boneGameObject.AddComponent<Rigidbody>();
rb.constraints = RigidbodyConstraints.FreezePositionZ;
foreach (Bone child in b.Children)
RecursivelyCreateBoneProxies(child);
}
void UpdateSpineSkeleton (ISkeletonAnimation skeletonRenderer) {
bool flipX = skeleton.ScaleX < 0;
bool flipY = skeleton.ScaleY < 0;
bool flipXOR = flipX ^ flipY;
bool flipOR = flipX || flipY;
foreach (var pair in boneTable) {
var b = pair.Key;
var t = pair.Value;
bool isStartingBone = b == StartingBone;
Transform parentTransform = isStartingBone ? ragdollRoot : boneTable[b.Parent];
Vector3 parentTransformWorldPosition = parentTransform.position;
Quaternion parentTransformWorldRotation = parentTransform.rotation;
parentSpaceHelper.position = parentTransformWorldPosition;
parentSpaceHelper.rotation = parentTransformWorldRotation;
parentSpaceHelper.localScale = parentTransform.localScale;
Vector3 boneWorldPosition = t.position;
Vector3 right = parentSpaceHelper.InverseTransformDirection(t.right);
Vector3 boneLocalPosition = parentSpaceHelper.InverseTransformPoint(boneWorldPosition);
float boneLocalRotation = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg;
if (flipOR) {
if (isStartingBone) {
if (flipX) boneLocalPosition.x *= -1f;
if (flipY) boneLocalPosition.y *= -1f;
boneLocalRotation = boneLocalRotation * (flipXOR ? -1f : 1f);
if (flipX) boneLocalRotation += 180;
} else {
if (flipXOR) {
boneLocalRotation *= -1f;
boneLocalPosition.y *= -1f; // wtf??
}
}
}
b.x = Mathf.Lerp(b.x, boneLocalPosition.x, mix);
b.y = Mathf.Lerp(b.y, boneLocalPosition.y, mix);
b.rotation = Mathf.Lerp(b.rotation, boneLocalRotation, mix);
//b.AppliedRotation = Mathf.Lerp(b.AppliedRotation, boneLocalRotation, mix);
}
}
List<Collider> AttachBoundingBoxRagdollColliders (Bone b) {
const string AttachmentNameMarker = "ragdoll";
var colliders = new List<Collider>();
Transform t = boneTable[b];
GameObject go = t.gameObject;
var skin = skeleton.Skin ?? skeleton.Data.DefaultSkin;
var attachments = new List<Attachment>();
foreach (Slot s in skeleton.Slots) {
if (s.Bone == b) {
skin.FindAttachmentsForSlot(skeleton.Slots.IndexOf(s), attachments);
foreach (var a in attachments) {
var bbAttachment = a as BoundingBoxAttachment;
if (bbAttachment != null) {
if (!a.Name.ToLower().Contains(AttachmentNameMarker))
continue;
var bbCollider = go.AddComponent<BoxCollider>();
var bounds = SkeletonUtility.GetBoundingBoxBounds(bbAttachment, thickness);
bbCollider.center = bounds.center;
bbCollider.size = bounds.size;
colliders.Add(bbCollider);
}
}
}
}
return colliders;
}
static float GetPropagatedRotation (Bone b) {
Bone parent = b.Parent;
float a = b.AppliedRotation;
while (parent != null) {
a += parent.AppliedRotation;
parent = parent.parent;
}
return a;
}
public class LayerFieldAttribute : PropertyAttribute {}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 373527d2bf3351348b9fcc499ce9ea23
timeCreated: 1430552693
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,415 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, 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.
*
* THIS SOFTWARE IS 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 THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
// Contributed by: Mitch Thompson
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace Spine.Unity.Modules {
[RequireComponent(typeof(SkeletonRenderer))]
public class SkeletonRagdoll2D : MonoBehaviour {
static Transform parentSpaceHelper;
#region Inspector
[Header("Hierarchy")]
[SpineBone]
public string startingBoneName = "";
[SpineBone]
public List<string> stopBoneNames = new List<string>();
[Header("Parameters")]
public bool applyOnStart;
[Tooltip("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")]
public bool disableIK = true;
public bool disableOtherConstraints = false;
[Space]
[Tooltip("Set RootRigidbody IsKinematic to true when Apply is called.")]
public bool pinStartBone;
public float gravityScale = 1;
[Tooltip("If no BoundingBox Attachment is attached to a bone, this becomes the default Width or Radius of a Bone's ragdoll Rigidbody")]
public float thickness = 0.125f;
[Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")]
public float rotationLimit = 20;
public float rootMass = 20;
[Tooltip("If your ragdoll seems unstable or uneffected by limits, try lowering this value.")]
[Range(0.01f, 1f)]
public float massFalloffFactor = 0.4f;
[Tooltip("The layer assigned to all of the rigidbody parts.")]
[SkeletonRagdoll.LayerField]
public int colliderLayer = 0;
[Range(0, 1)]
public float mix = 1;
#endregion
ISkeletonAnimation targetSkeletonComponent;
Skeleton skeleton;
Dictionary<Bone, Transform> boneTable = new Dictionary<Bone, Transform>();
Transform ragdollRoot;
public Rigidbody2D RootRigidbody { get; private set; }
public Bone StartingBone { get; private set; }
Vector2 rootOffset;
public Vector3 RootOffset { get { return this.rootOffset; } }
bool isActive;
public bool IsActive { get { return this.isActive; } }
IEnumerator Start () {
if (parentSpaceHelper == null) {
parentSpaceHelper = (new GameObject("Parent Space Helper")).transform;
}
targetSkeletonComponent = GetComponent<SkeletonRenderer>() as ISkeletonAnimation;
if (targetSkeletonComponent == null) Debug.LogError("Attached Spine component does not implement ISkeletonAnimation. This script is not compatible.");
skeleton = targetSkeletonComponent.Skeleton;
if (applyOnStart) {
yield return null;
Apply();
}
}
#region API
public Rigidbody2D[] RigidbodyArray {
get {
if (!isActive)
return new Rigidbody2D[0];
var rigidBodies = new Rigidbody2D[boneTable.Count];
int i = 0;
foreach (Transform t in boneTable.Values) {
rigidBodies[i] = t.GetComponent<Rigidbody2D>();
i++;
}
return rigidBodies;
}
}
public Vector3 EstimatedSkeletonPosition {
get { return this.RootRigidbody.position - rootOffset; }
}
/// <summary>Instantiates the ragdoll simulation and applies its transforms to the skeleton.</summary>
public void Apply () {
isActive = true;
mix = 1;
Bone startingBone = this.StartingBone = skeleton.FindBone(startingBoneName);
RecursivelyCreateBoneProxies(startingBone);
RootRigidbody = boneTable[startingBone].GetComponent<Rigidbody2D>();
RootRigidbody.isKinematic = pinStartBone;
RootRigidbody.mass = rootMass;
var boneColliders = new List<Collider2D>();
foreach (var pair in boneTable) {
var b = pair.Key;
var t = pair.Value;
Transform parentTransform;
boneColliders.Add(t.GetComponent<Collider2D>());
if (b == startingBone) {
ragdollRoot = new GameObject("RagdollRoot").transform;
ragdollRoot.SetParent(transform, false);
if (b == skeleton.RootBone) { // RagdollRoot is skeleton root.
ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetPropagatedRotation(b));
} else {
ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetPropagatedRotation(b.Parent));
}
parentTransform = ragdollRoot;
rootOffset = t.position - transform.position;
} else {
parentTransform = boneTable[b.Parent];
}
// Add joint and attach to parent.
var rbParent = parentTransform.GetComponent<Rigidbody2D>();
if (rbParent != null) {
var joint = t.gameObject.AddComponent<HingeJoint2D>();
joint.connectedBody = rbParent;
Vector3 localPos = parentTransform.InverseTransformPoint(t.position);
joint.connectedAnchor = localPos;
joint.GetComponent<Rigidbody2D>().mass = joint.connectedBody.mass * massFalloffFactor;
joint.limits = new JointAngleLimits2D {
min = -rotationLimit,
max = rotationLimit
};
joint.useLimits = true;
}
}
// Ignore collisions among bones.
for (int x = 0; x < boneColliders.Count; x++) {
for (int y = 0; y < boneColliders.Count; y++) {
if (x == y) continue;
Physics2D.IgnoreCollision(boneColliders[x], boneColliders[y]);
}
}
// Destroy existing override-mode SkeletonUtility bones.
var utilityBones = GetComponentsInChildren<SkeletonUtilityBone>();
if (utilityBones.Length > 0) {
var destroyedUtilityBoneNames = new List<string>();
foreach (var ub in utilityBones) {
if (ub.mode == SkeletonUtilityBone.Mode.Override) {
destroyedUtilityBoneNames.Add(ub.gameObject.name);
Destroy(ub.gameObject);
}
}
if (destroyedUtilityBoneNames.Count > 0) {
string msg = "Destroyed Utility Bones: ";
for (int i = 0; i < destroyedUtilityBoneNames.Count; i++) {
msg += destroyedUtilityBoneNames[i];
if (i != destroyedUtilityBoneNames.Count - 1) {
msg += ",";
}
}
Debug.LogWarning(msg);
}
}
// Disable skeleton constraints.
if (disableIK) {
var ikConstraints = skeleton.IkConstraints;
for (int i = 0, n = ikConstraints.Count; i < n; i++)
ikConstraints.Items[i].mix = 0;
}
if (disableOtherConstraints) {
var transformConstraints = skeleton.transformConstraints;
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
transformConstraints.Items[i].rotateMix = 0;
transformConstraints.Items[i].scaleMix = 0;
transformConstraints.Items[i].shearMix = 0;
transformConstraints.Items[i].translateMix = 0;
}
var pathConstraints = skeleton.pathConstraints;
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
pathConstraints.Items[i].rotateMix = 0;
pathConstraints.Items[i].translateMix = 0;
}
}
targetSkeletonComponent.UpdateWorld += UpdateSpineSkeleton;
}
/// <summary>Transitions the mix value from the current value to a target value.</summary>
public Coroutine SmoothMix (float target, float duration) {
return StartCoroutine(SmoothMixCoroutine(target, duration));
}
IEnumerator SmoothMixCoroutine (float target, float duration) {
float startTime = Time.time;
float startMix = mix;
while (mix > 0) {
skeleton.SetBonesToSetupPose();
mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration);
yield return null;
}
}
/// <summary>Set the transform world position while preserving the ragdoll parts world position.</summary>
public void SetSkeletonPosition (Vector3 worldPosition) {
if (!isActive) {
Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!");
return;
}
Vector3 offset = worldPosition - transform.position;
transform.position = worldPosition;
foreach (Transform t in boneTable.Values)
t.position -= offset;
UpdateSpineSkeleton(null);
skeleton.UpdateWorldTransform();
}
/// <summary>Removes the ragdoll instance and effect from the animated skeleton.</summary>
public void Remove () {
isActive = false;
foreach (var t in boneTable.Values)
Destroy(t.gameObject);
Destroy(ragdollRoot.gameObject);
boneTable.Clear();
targetSkeletonComponent.UpdateWorld -= UpdateSpineSkeleton;
}
public Rigidbody2D GetRigidbody (string boneName) {
var bone = skeleton.FindBone(boneName);
return (bone != null && boneTable.ContainsKey(bone)) ? boneTable[bone].GetComponent<Rigidbody2D>() : null;
}
#endregion
/// <summary>Generates the ragdoll simulation's Transform and joint setup.</summary>
void RecursivelyCreateBoneProxies (Bone b) {
string boneName = b.data.name;
if (stopBoneNames.Contains(boneName))
return;
var boneGameObject = new GameObject(boneName);
boneGameObject.layer = this.colliderLayer;
Transform t = boneGameObject.transform;
boneTable.Add(b, t);
t.parent = transform;
t.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX - b.shearX);
t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 0);
// MITCH: You left "todo: proper ragdoll branching"
var colliders = AttachBoundingBoxRagdollColliders(b, boneGameObject, skeleton, this.gravityScale);
if (colliders.Count == 0) {
float length = b.data.length;
if (length == 0) {
var circle = boneGameObject.AddComponent<CircleCollider2D>();
circle.radius = thickness * 0.5f;
} else {
var box = boneGameObject.AddComponent<BoxCollider2D>();
box.size = new Vector2(length, thickness);
box.offset = new Vector2(length * 0.5f, 0); // box.center in UNITY_4
}
}
var rb = boneGameObject.GetComponent<Rigidbody2D>();
if (rb == null) rb = boneGameObject.AddComponent<Rigidbody2D>();
rb.gravityScale = this.gravityScale;
foreach (Bone child in b.Children)
RecursivelyCreateBoneProxies(child);
}
/// <summary>Performed every skeleton animation update to translate Unity Transforms positions into Spine bone transforms.</summary>
void UpdateSpineSkeleton (ISkeletonAnimation animatedSkeleton) {
bool flipX = skeleton.ScaleX < 0;
bool flipY = skeleton.ScaleY < 0;
bool flipXOR = flipX ^ flipY;
bool flipOR = flipX || flipY;
var startingBone = this.StartingBone;
foreach (var pair in boneTable) {
var b = pair.Key;
var t = pair.Value;
bool isStartingBone = (b == startingBone);
Transform parentTransform = isStartingBone ? ragdollRoot : boneTable[b.Parent];
Vector3 parentTransformWorldPosition = parentTransform.position;
Quaternion parentTransformWorldRotation = parentTransform.rotation;
parentSpaceHelper.position = parentTransformWorldPosition;
parentSpaceHelper.rotation = parentTransformWorldRotation;
parentSpaceHelper.localScale = parentTransform.localScale;
Vector3 boneWorldPosition = t.position;
Vector3 right = parentSpaceHelper.InverseTransformDirection(t.right);
Vector3 boneLocalPosition = parentSpaceHelper.InverseTransformPoint(boneWorldPosition);
float boneLocalRotation = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg;
if (flipOR) {
if (isStartingBone) {
if (flipX) boneLocalPosition.x *= -1f;
if (flipY) boneLocalPosition.y *= -1f;
boneLocalRotation = boneLocalRotation * (flipXOR ? -1f : 1f);
if (flipX) boneLocalRotation += 180;
} else {
if (flipXOR) {
boneLocalRotation *= -1f;
boneLocalPosition.y *= -1f; // wtf??
}
}
}
b.x = Mathf.Lerp(b.x, boneLocalPosition.x, mix);
b.y = Mathf.Lerp(b.y, boneLocalPosition.y, mix);
b.rotation = Mathf.Lerp(b.rotation, boneLocalRotation, mix);
//b.AppliedRotation = Mathf.Lerp(b.AppliedRotation, boneLocalRotation, mix);
}
}
static List<Collider2D> AttachBoundingBoxRagdollColliders (Bone b, GameObject go, Skeleton skeleton, float gravityScale) {
const string AttachmentNameMarker = "ragdoll";
var colliders = new List<Collider2D>();
var skin = skeleton.Skin ?? skeleton.Data.DefaultSkin;
var attachments = new List<Attachment>();
foreach (Slot slot in skeleton.Slots) {
if (slot.bone == b) {
skin.FindAttachmentsForSlot(skeleton.Slots.IndexOf(slot), attachments);
bool bbAttachmentAdded = false;
foreach (var a in attachments) {
var bbAttachment = a as BoundingBoxAttachment;
if (bbAttachment != null) {
if (!a.Name.ToLower().Contains(AttachmentNameMarker))
continue;
bbAttachmentAdded = true;
var bbCollider = SkeletonUtility.AddBoundingBoxAsComponent(bbAttachment, slot, go, isTrigger: false);
colliders.Add(bbCollider);
}
}
if (bbAttachmentAdded)
SkeletonUtility.AddBoneRigidbody2D(go, isKinematic: false, gravityScale: gravityScale);
}
}
return colliders;
}
static float GetPropagatedRotation (Bone b) {
Bone parent = b.Parent;
float a = b.AppliedRotation;
while (parent != null) {
a += parent.AppliedRotation;
parent = parent.parent;
}
return a;
}
static Vector3 FlipScale (bool flipX, bool flipY) {
return new Vector3(flipX ? -1f : 1f, flipY ? -1f : 1f, 1f);
}
#if UNITY_EDITOR
void OnDrawGizmosSelected () {
if (isActive) {
Gizmos.DrawWireSphere(transform.position, thickness * 1.2f);
Vector3 newTransformPos = RootRigidbody.position - rootOffset;
Gizmos.DrawLine(transform.position, newTransformPos);
Gizmos.DrawWireSphere(newTransformPos, thickness * 1.2f);
}
}
#endif
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e74a49a26242a214d9084fde00bfe3ab
timeCreated: 1431497383
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 3e6a65e2576c5b74dabb05c3d3fc2ae4
folderAsset: yes
timeCreated: 1479258132
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,113 @@
// - Unlit
// - Premultiplied Alpha Blending (Optional straight alpha input)
// - Double-sided, no depth
Shader "Spine/Skeleton Fill" {
Properties {
_FillColor ("FillColor", Color) = (1,1,1,1)
_FillPhase ("FillPhase", Range(0, 1)) = 0
[NoScaleOffset] _MainTex ("MainTex", 2D) = "white" {}
_Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1
[Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0
[HideInInspector] _StencilRef("Stencil Reference", Float) = 1.0
[Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp("Stencil Comparison", Float) = 8 // Set to Always as default
}
SubShader {
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" }
Blend One OneMinusSrcAlpha
Cull Off
ZWrite Off
Lighting Off
Stencil {
Ref[_StencilRef]
Comp[_StencilComp]
Pass Keep
}
Pass {
CGPROGRAM
#pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _FillColor;
float _FillPhase;
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 = (VertexOutput)0;
o.uv = v.uv;
o.vertexColor = v.vertexColor;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
float4 frag (VertexOutput i) : COLOR {
float4 rawColor = tex2D(_MainTex,i.uv);
float finalAlpha = (rawColor.a * i.vertexColor.a);
#if defined(_STRAIGHT_ALPHA_INPUT)
rawColor.rgb *= rawColor.a;
#endif
float3 finalColor = lerp((rawColor.rgb * i.vertexColor.rgb), (_FillColor.rgb * finalAlpha), _FillPhase); // make sure to PMA _FillColor.
return fixed4(finalColor, finalAlpha);
}
ENDCG
}
Pass {
Name "Caster"
Tags { "LightMode"="ShadowCaster" }
Offset 1, 1
ZWrite On
ZTest LEqual
Fog { Mode Off }
Cull Off
Lighting Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed _Cutoff;
struct VertexOutput {
V2F_SHADOW_CASTER;
float2 uv : TEXCOORD1;
};
VertexOutput vert (appdata_base v) {
VertexOutput o;
o.uv = v.texcoord;
TRANSFER_SHADOW_CASTER(o)
return o;
}
float4 frag (VertexOutput i) : COLOR {
fixed4 texcol = tex2D(_MainTex, i.uv);
clip(texcol.a - _Cutoff);
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
FallBack "Diffuse"
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 45495790b394f894a967dbf44489b57b
timeCreated: 1492385797
licenseType: Free
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,115 @@
// Spine/Skeleton Tint
// - Two color tint
// - unlit
// - Premultiplied alpha blending (Optional straight alpha input)
// - No depth, no backface culling, no fog.
Shader "Spine/Skeleton Tint" {
Properties {
_Color ("Tint Color", Color) = (1,1,1,1)
_Black ("Black Point", Color) = (0,0,0,0)
[NoScaleOffset] _MainTex ("MainTex", 2D) = "black" {}
[Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0
_Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1
[HideInInspector] _StencilRef("Stencil Reference", Float) = 1.0
[Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp("Stencil Comparison", Float) = 8 // Set to Always as default
}
SubShader {
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" }
Fog { Mode Off }
Cull Off
ZWrite Off
Blend One OneMinusSrcAlpha
Lighting Off
Stencil {
Ref[_StencilRef]
Comp[_StencilComp]
Pass Keep
}
Pass {
CGPROGRAM
#pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _Color;
float4 _Black;
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) + float4(((1-texColor.rgb) * _Black.rgb * texColor.a*_Color.a*i.vertexColor.a), 0);
}
ENDCG
}
Pass {
Name "Caster"
Tags { "LightMode"="ShadowCaster" }
Offset 1, 1
ZWrite On
ZTest LEqual
Fog { Mode Off }
Cull Off
Lighting Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed _Cutoff;
struct VertexOutput {
V2F_SHADOW_CASTER;
float2 uv : TEXCOORD1;
};
VertexOutput vert (appdata_base v) {
VertexOutput o;
o.uv = v.texcoord;
TRANSFER_SHADOW_CASTER(o)
return o;
}
float4 frag (VertexOutput i) : COLOR {
fixed4 texcol = tex2D(_MainTex, i.uv);
clip(texcol.a - _Cutoff);
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 522f03282fd79be47b306e2ef4b593fd
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,113 @@
// - Unlit
// - Premultiplied Alpha Blending (Optional straight alpha input)
// - Double-sided, no depth
Shader "Spine/Special/Skeleton Grayscale" {
Properties {
_GrayPhase ("Phase", Range(0, 1)) = 1
[NoScaleOffset] _MainTex ("MainTex", 2D) = "white" {}
_Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1
[Toggle(_STRAIGHT_ALPHA_INPUT)] _StraightAlphaInput("Straight Alpha Texture", Int) = 0
[HideInInspector] _StencilRef("Stencil Reference", Float) = 1.0
[Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp("Stencil Comparison", Float) = 8 // Set to Always as default
}
SubShader {
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" }
Blend One OneMinusSrcAlpha
Cull Off
ZWrite Off
Lighting Off
Stencil {
Ref[_StencilRef]
Comp[_StencilComp]
Pass Keep
}
Pass {
CGPROGRAM
#pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float _GrayPhase;
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 = (VertexOutput)0;
o.uv = v.uv;
o.vertexColor = v.vertexColor;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
float4 frag (VertexOutput i) : COLOR {
float4 rawColor = tex2D(_MainTex,i.uv);
float finalAlpha = (rawColor.a * i.vertexColor.a);
#if defined(_STRAIGHT_ALPHA_INPUT)
rawColor.rgb *= rawColor.a;
#endif
rawColor.rgb *= i.vertexColor.rgb;
float3 finalColor = lerp(rawColor.rgb, dot(rawColor.rgb, float3(0.3, 0.59, 0.11)), _GrayPhase);
return fixed4(finalColor, finalAlpha);
}
ENDCG
}
Pass {
Name "Caster"
Tags { "LightMode"="ShadowCaster" }
Offset 1, 1
ZWrite On
ZTest LEqual
Fog { Mode Off }
Cull Off
Lighting Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed _Cutoff;
struct VertexOutput {
V2F_SHADOW_CASTER;
float2 uv : TEXCOORD1;
};
VertexOutput vert (appdata_base v) {
VertexOutput o;
o.uv = v.texcoord;
TRANSFER_SHADOW_CASTER(o)
return o;
}
float4 frag (VertexOutput i) : SV_Target {
fixed4 texcol = tex2D(_MainTex, i.uv);
clip(texcol.a - _Cutoff);
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
FallBack "Diffuse"
}

View File

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

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: a831a8ed72a588a48b2fb892e7f37371
folderAsset: yes
timeCreated: 1479419399
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4b6fb48f295cd8248a7566315212a3c2
folderAsset: yes
timeCreated: 1494092464
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
#ifndef SHADER_MATHS_INCLUDED
#define SHADER_MATHS_INCLUDED
#include "UnityCG.cginc"
////////////////////////////////////////
// Maths functions
//
inline half3 safeNormalize(half3 inVec)
{
half dp3 = max(0.001f, dot(inVec, inVec));
return inVec * rsqrt(dp3);
}
inline float dotClamped(float3 a, float3 b)
{
#if (SHADER_TARGET < 30 || defined(SHADER_API_PS3))
return saturate(dot(a, b));
#else
return max(0.0h, dot(a, b));
#endif
}
inline float oneDividedBy(float value)
{
//Catches NANs
float sign_value = sign(value);
float sign_value_squared = sign_value*sign_value;
return sign_value_squared / ( value + sign_value_squared - 1.0);
}
inline half pow5 (half x)
{
return x*x*x*x*x;
}
inline float4 quat_from_axis_angle(float3 axis, float angleRadians)
{
float4 qr;
float half_angle = (angleRadians * 0.5);
qr.x = axis.x * sin(half_angle);
qr.y = axis.y * sin(half_angle);
qr.z = axis.z * sin(half_angle);
qr.w = cos(half_angle);
return qr;
}
inline float3 rotate_vertex_position(float3 position, float3 axis, float angleRadians)
{
float4 q = quat_from_axis_angle(axis, angleRadians);
float3 v = position.xyz;
return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
}
float3 EncodeFloatRGB(float value)
{
const float max24int = 256*256*256-1;
float3 decomp = floor( value * float3( max24int/(256*256), max24int/256, max24int ) ) / 255.0;
decomp.z -= decomp.y * 256.0;
decomp.y -= decomp.x * 256.0;
return decomp;
}
float DecodeFloatRGB(float3 decomp)
{
return dot( decomp.xyz, float3( 255.0/256, 255.0/(256*256), 255.0/(256*256*256) ) );
}
#endif // SHADER_MATHS_INCLUDED

View File

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

View File

@@ -0,0 +1,474 @@
// Upgrade NOTE: upgraded instancing buffer 'PerDrawSprite' to new syntax.
#ifndef SHADER_SHARED_INCLUDED
#define SHADER_SHARED_INCLUDED
#include "UnityCG.cginc"
#ifdef UNITY_INSTANCING_ENABLED
UNITY_INSTANCING_BUFFER_START(PerDrawSprite)
// SpriteRenderer.Color while Non-Batched/Instanced.
fixed4 unity_SpriteRendererColorArray[UNITY_INSTANCED_ARRAY_SIZE];
// this could be smaller but that's how bit each entry is regardless of type
float4 unity_SpriteFlipArray[UNITY_INSTANCED_ARRAY_SIZE];
UNITY_INSTANCING_BUFFER_END(PerDrawSprite)
#define _RendererColor unity_SpriteRendererColorArray[unity_InstanceID]
#define _Flip unity_SpriteFlipArray[unity_InstanceID]
#endif // instancing
CBUFFER_START(UnityPerDrawSprite)
#ifndef UNITY_INSTANCING_ENABLED
fixed4 _RendererColor;
float4 _Flip;
#endif
float _EnableExternalAlpha;
CBUFFER_END
////////////////////////////////////////
// Space functions
//
inline float4 calculateWorldPos(float4 vertex)
{
return mul(unity_ObjectToWorld, vertex);
}
inline float4 calculateLocalPos(float4 vertex)
{
#ifdef UNITY_INSTANCING_ENABLED
vertex.xy *= _Flip.xy;
#endif
float4 pos = UnityObjectToClipPos(vertex);
#ifdef PIXELSNAP_ON
pos = UnityPixelSnap(pos);
#endif
return pos;
}
inline half3 calculateWorldNormal(float3 normal)
{
return UnityObjectToWorldNormal(normal);
}
////////////////////////////////////////
// Normal map functions
//
#if defined(_NORMALMAP)
uniform sampler2D _BumpMap;
uniform half _BumpScale;
half3 UnpackScaleNormal(half4 packednormal, half bumpScale)
{
#if defined(UNITY_NO_DXT5nm)
return packednormal.xyz * 2 - 1;
#else
half3 normal;
normal.xy = (packednormal.wy * 2 - 1);
#if (SHADER_TARGET >= 30)
// SM2.0: instruction count limitation
// SM2.0: normal scaler is not supported
normal.xy *= bumpScale;
#endif
normal.z = sqrt(1.0 - saturate(dot(normal.xy, normal.xy)));
return normal;
#endif
}
inline half3 calculateWorldTangent(float4 tangent)
{
return UnityObjectToWorldDir(tangent);
}
inline half3 calculateWorldBinormal(half3 normalWorld, half3 tangentWorld, float tangentSign)
{
//When calculating the binormal we have to flip it when the mesh is scaled negatively.
//Normally this would just be unity_WorldTransformParams.w but this isn't set correctly by Unity for its SpriteRenderer meshes so get from objectToWorld matrix scale instead.
half worldTransformSign = sign(unity_ObjectToWorld[0][0] * unity_ObjectToWorld[1][1] * unity_ObjectToWorld[2][2]);
half sign = tangentSign * worldTransformSign;
return cross(normalWorld, tangentWorld) * sign;
}
inline half3 calculateNormalFromBumpMap(float2 texUV, half3 tangentWorld, half3 binormalWorld, half3 normalWorld)
{
half3 localNormal = UnpackScaleNormal(tex2D(_BumpMap, texUV), _BumpScale);
half3x3 rotation = half3x3(tangentWorld, binormalWorld, normalWorld);
half3 normal = normalize(mul(localNormal, rotation));
return normal;
}
#endif // _NORMALMAP
////////////////////////////////////////
// Blending functions
//
inline fixed4 calculateLitPixel(fixed4 texureColor, fixed4 color, fixed3 lighting) : SV_Target
{
fixed4 finalPixel;
#if defined(_ALPHABLEND_ON)
//Normal Alpha
finalPixel.a = texureColor.a * color.a;
finalPixel.rgb = texureColor.rgb * color.rgb * (lighting * finalPixel.a);
#elif defined(_ALPHAPREMULTIPLY_ON)
//Pre multiplied alpha
finalPixel = texureColor * color;
finalPixel.rgb *= lighting * color.a;
#elif defined(_MULTIPLYBLEND)
//Multiply
finalPixel = texureColor * color;
finalPixel.rgb *= lighting;
finalPixel = lerp(fixed4(1,1,1,1), finalPixel, finalPixel.a);
#elif defined(_MULTIPLYBLEND_X2)
//Multiply x2
finalPixel.rgb = texureColor.rgb * color.rgb * lighting * 2.0f;
finalPixel.a = color.a * texureColor.a;
finalPixel = lerp(fixed4(0.5f,0.5f,0.5f,0.5f), finalPixel, finalPixel.a);
#elif defined(_ADDITIVEBLEND)
//Additive
finalPixel = texureColor * 2.0f * color;
finalPixel.rgb *= lighting * color.a;
#elif defined(_ADDITIVEBLEND_SOFT)
//Additive soft
finalPixel = texureColor * color;
finalPixel.rgb *= lighting * finalPixel.a;
#else
//Opaque
finalPixel.a = 1;
finalPixel.rgb = texureColor.rgb * color.rgb * lighting;
#endif
return finalPixel;
}
inline fixed4 calculateLitPixel(fixed4 texureColor, fixed3 lighting) : SV_Target
{
fixed4 finalPixel;
#if defined(_ALPHABLEND_ON)
//Normal Alpha
finalPixel.a = texureColor.a;
finalPixel.rgb = texureColor.rgb * (lighting * finalPixel.a);
#elif defined(_ALPHAPREMULTIPLY_ON)
//Pre multiplied alpha
finalPixel = texureColor;
finalPixel.rgb *= lighting;
#elif defined(_MULTIPLYBLEND)
//Multiply
finalPixel = texureColor;
finalPixel.rgb *= lighting;
finalPixel = lerp(fixed4(1,1,1,1), finalPixel, finalPixel.a);
#elif defined(_MULTIPLYBLEND_X2)
//Multiply x2
finalPixel.rgb = texureColor.rgb * lighting * 2.0f;
finalPixel.a = texureColor.a;
finalPixel = lerp(fixed4(0.5f,0.5f,0.5f,0.5f), finalPixel, finalPixel.a);
#elif defined(_ADDITIVEBLEND)
//Additive
finalPixel = texureColor * 2.0f;
finalPixel.rgb *= lighting;
#elif defined(_ADDITIVEBLEND_SOFT)
//Additive soft
finalPixel = texureColor;
finalPixel.rgb *= lighting * finalPixel.a;
#else
//Opaque
finalPixel.a = 1;
finalPixel.rgb = texureColor.rgb * lighting;
#endif
return finalPixel;
}
inline fixed4 calculateAdditiveLitPixel(fixed4 texureColor, fixed4 color, fixed3 lighting) : SV_Target
{
fixed4 finalPixel;
#if defined(_ALPHABLEND_ON) || defined(_MULTIPLYBLEND) || defined(_MULTIPLYBLEND_X2) || defined(_ADDITIVEBLEND) || defined(_ADDITIVEBLEND_SOFT)
//Normal Alpha, Additive and Multiply modes
finalPixel.rgb = (texureColor.rgb * lighting * color.rgb) * (texureColor.a * color.a);
finalPixel.a = 1.0;
#elif defined(_ALPHAPREMULTIPLY_ON)
//Pre multiplied alpha
finalPixel.rgb = texureColor.rgb * lighting * color.rgb * color.a;
finalPixel.a = 1.0;
#else
//Opaque
finalPixel.rgb = texureColor.rgb * lighting * color.rgb;
finalPixel.a = 1.0;
#endif
return finalPixel;
}
inline fixed4 calculateAdditiveLitPixel(fixed4 texureColor, fixed3 lighting) : SV_Target
{
fixed4 finalPixel;
#if defined(_ALPHABLEND_ON) || defined(_MULTIPLYBLEND) || defined(_MULTIPLYBLEND_X2) || defined(_ADDITIVEBLEND) || defined(_ADDITIVEBLEND_SOFT)
//Normal Alpha, Additive and Multiply modes
finalPixel.rgb = (texureColor.rgb * lighting) * texureColor.a;
finalPixel.a = 1.0;
#else
//Pre multiplied alpha and Opaque
finalPixel.rgb = texureColor.rgb * lighting;
finalPixel.a = 1.0;
#endif
return finalPixel;
}
inline fixed4 calculatePixel(fixed4 texureColor, fixed4 color) : SV_Target
{
fixed4 finalPixel;
#if defined(_ALPHABLEND_ON)
//Normal Alpha
finalPixel.a = texureColor.a * color.a;
finalPixel.rgb = (texureColor.rgb * color.rgb) * finalPixel.a;
#elif defined(_ALPHAPREMULTIPLY_ON)
//Pre multiplied alpha
finalPixel = texureColor * color;
finalPixel.rgb *= color.a;
#elif defined(_MULTIPLYBLEND)
//Multiply
finalPixel = color * texureColor;
finalPixel = lerp(fixed4(1,1,1,1), finalPixel, finalPixel.a);
#elif defined(_MULTIPLYBLEND_X2)
//Multiply x2
finalPixel.rgb = texureColor.rgb * color.rgb * 2.0f;
finalPixel.a = color.a * texureColor.a;
finalPixel = lerp(fixed4(0.5f,0.5f,0.5f,0.5f), finalPixel, finalPixel.a);
#elif defined(_ADDITIVEBLEND)
//Additive
finalPixel = texureColor * 2.0f * color;
#elif defined(_ADDITIVEBLEND_SOFT)
//Additive soft
finalPixel = color * texureColor;
finalPixel.rgb *= finalPixel.a;
#else
//Opaque
finalPixel.a = 1;
finalPixel.rgb = texureColor.rgb * color.rgb;
#endif
return finalPixel;
}
inline fixed4 calculatePixel(fixed4 texureColor) : SV_Target
{
fixed4 finalPixel;
#if defined(_ALPHABLEND_ON)
//Normal Alpha
finalPixel.a = texureColor.a;
finalPixel.rgb = texureColor.rgb * finalPixel.a;
#elif defined(_ALPHAPREMULTIPLY_ON)
//Pre multiplied alpha
finalPixel = texureColor;
#elif defined(_MULTIPLYBLEND)
//Multiply
finalPixel = texureColor;
finalPixel = lerp(fixed4(1,1,1,1), finalPixel, finalPixel.a);
#elif defined(_MULTIPLYBLEND_X2)
//Multiply x2
finalPixel.rgb = texureColor.rgb * 2.0f;
finalPixel.a = texureColor.a;
finalPixel = lerp(fixed4(0.5f,0.5f,0.5f,0.5f), finalPixel, finalPixel.a);
#elif defined(_ADDITIVEBLEND)
//Additive
finalPixel = texureColor * 2.0f;
#elif defined(_ADDITIVEBLEND_SOFT)
//Additive soft
finalPixel = texureColor;
finalPixel.rgb *= finalPixel.a;
#else
//Opaque
finalPixel.a = 1;
finalPixel.rgb = texureColor.rgb;
#endif
return finalPixel;
}
////////////////////////////////////////
// Alpha Clipping
//
#if defined(_ALPHA_CLIP)
uniform fixed _Cutoff;
#define ALPHA_CLIP(pixel, color) clip((pixel.a * color.a) - _Cutoff);
#else
#define ALPHA_CLIP(pixel, color)
#endif
////////////////////////////////////////
// Color functions
//
uniform fixed4 _Color;
inline fixed4 calculateVertexColor(fixed4 color)
{
return color * _Color;
}
#if defined(_COLOR_ADJUST)
uniform float _Hue;
uniform float _Saturation;
uniform float _Brightness;
uniform fixed4 _OverlayColor;
float3 rgb2hsv(float3 c)
{
float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g));
float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
float3 hsv2rgb(float3 c)
{
c = float3(c.x, clamp(c.yz, 0.0, 1.0));
float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
inline fixed4 adjustColor(fixed4 color)
{
float3 hsv = rgb2hsv(color.rgb);
hsv.x += _Hue;
hsv.y *= _Saturation;
hsv.z *= _Brightness;
color.rgb = hsv2rgb(hsv);
return color;
}
#define COLORISE(pixel) pixel.rgb = lerp(pixel.rgb, _OverlayColor.rgb, _OverlayColor.a * pixel.a);
#define COLORISE_ADDITIVE(pixel) pixel.rgb = ((1.0-_OverlayColor.a) * pixel.rgb);
#else // !_COLOR_ADJUST
#define COLORISE(pixel)
#define COLORISE_ADDITIVE(pixel)
#endif // !_COLOR_ADJUST
////////////////////////////////////////
// Fog
//
#if defined(_FOG) && (defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2))
inline fixed4 applyFog(fixed4 pixel, float1 fogCoord)
{
#if defined(_ADDITIVEBLEND) || defined(_ADDITIVEBLEND_SOFT)
//In additive mode blend from clear to black based on luminance
float luminance = pixel.r * 0.3 + pixel.g * 0.59 + pixel.b * 0.11;
fixed4 fogColor = lerp(fixed4(0,0,0,0), fixed4(0,0,0,1), luminance);
#elif defined(_MULTIPLYBLEND)
//In multiplied mode fade to white based on inverse luminance
float luminance = pixel.r * 0.3 + pixel.g * 0.59 + pixel.b * 0.11;
fixed4 fogColor = lerp(fixed4(1,1,1,1), fixed4(0,0,0,0), luminance);
#elif defined(_MULTIPLYBLEND_X2)
//In multipliedx2 mode fade to grey based on inverse luminance
float luminance = pixel.r * 0.3 + pixel.g * 0.59 + pixel.b * 0.11;
fixed4 fogColor = lerp(fixed4(0.5f,0.5f,0.5f,0.5f), fixed4(0,0,0,0), luminance);
#elif defined(_ALPHABLEND_ON) || defined(_ALPHAPREMULTIPLY_ON)
//In alpha blended modes blend to fog color based on pixel alpha
fixed4 fogColor = lerp(fixed4(0,0,0,0), unity_FogColor, pixel.a);
#else
//In opaque mode just return fog color;
fixed4 fogColor = unity_FogColor;
#endif
UNITY_APPLY_FOG_COLOR(fogCoord, pixel, fogColor);
return pixel;
}
#define APPLY_FOG(pixel, input) pixel = applyFog(pixel, input.fogCoord);
#define APPLY_FOG_ADDITIVE(pixel, input) \
UNITY_APPLY_FOG_COLOR(input.fogCoord, pixel.rgb, fixed4(0,0,0,0)); // fog towards black in additive pass
#else
#define APPLY_FOG(pixel, input)
#define APPLY_FOG_ADDITIVE(pixel, input)
#endif
////////////////////////////////////////
// Texture functions
//
uniform sampler2D _MainTex;
#if ETC1_EXTERNAL_ALPHA
//External alpha texture for ETC1 compression
uniform sampler2D _AlphaTex;
#endif //ETC1_EXTERNAL_ALPHA
#if _TEXTURE_BLEND
uniform sampler2D _BlendTex;
uniform float _BlendAmount;
inline fixed4 calculateBlendedTexturePixel(float2 texcoord)
{
return (1.0-_BlendAmount) * tex2D(_MainTex, texcoord) + _BlendAmount * tex2D(_BlendTex, texcoord);
}
#endif // _TEXTURE_BLEND
inline fixed4 calculateTexturePixel(float2 texcoord)
{
fixed4 pixel;
#if _TEXTURE_BLEND
pixel = calculateBlendedTexturePixel(texcoord);
#else
pixel = tex2D(_MainTex, texcoord);
#endif // !_TEXTURE_BLEND
#if ETC1_EXTERNAL_ALPHA
fixed4 alpha = tex2D (_AlphaTex, texcoord);
pixel.a = lerp (pixel.a, alpha.r, _EnableExternalAlpha);
#endif
#if defined(_COLOR_ADJUST)
pixel = adjustColor(pixel);
#endif // _COLOR_ADJUST
return pixel;
}
uniform fixed4 _MainTex_ST;
inline float2 calculateTextureCoord(float4 texcoord)
{
return TRANSFORM_TEX(texcoord, _MainTex);
}
#endif // SHADER_SHARED_INCLUDED

View File

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

View File

@@ -0,0 +1,200 @@
#ifndef SPRITE_LIGHTING_INCLUDED
#define SPRITE_LIGHTING_INCLUDED
//Check for using mesh normals
#if !defined(_FIXED_NORMALS_VIEWSPACE) && !defined(_FIXED_NORMALS_VIEWSPACE_BACKFACE) && !defined(_FIXED_NORMALS_MODELSPACE) && !defined(_FIXED_NORMALS_MODELSPACE_BACKFACE)
#define MESH_NORMALS
#endif
//Check for fixing backfacing tangents
#if defined(_FIXED_NORMALS_VIEWSPACE_BACKFACE) || defined(_FIXED_NORMALS_MODELSPACE_BACKFACE)
#define FIXED_NORMALS_BACKFACE_RENDERING
#endif
////////////////////////////////////////
// Vertex structs
//
struct VertexInput
{
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
float4 color : COLOR;
#if defined(MESH_NORMALS)
float3 normal : NORMAL;
#endif // _FIXED_NORMALS
#if defined(_NORMALMAP)
float4 tangent : TANGENT;
#endif // _NORMALMAP
UNITY_VERTEX_INPUT_INSTANCE_ID
};
////////////////////////////////////////
// Normal functions
//
uniform float4 _FixedNormal = float4(0, 0, 1, 1);
inline float3 getFixedNormal()
{
return _FixedNormal.xyz;
}
inline float calculateBackfacingSign(float3 worldPos)
{
//If we're using fixed normals and mesh is facing away from camera, flip tangentSign
//Unity uses a left handed coordinate system so camera always looks down the negative z axis
float3 cameraForward = float3(0,0,-1);
float3 meshWorldForward = mul((float3x3)unity_ObjectToWorld, cameraForward);
float3 toCamera = _WorldSpaceCameraPos - worldPos;
return sign(dot(toCamera, meshWorldForward));
}
inline half3 calculateSpriteWorldNormal(VertexInput vertex, float backFaceSign)
{
#if defined(MESH_NORMALS)
return calculateWorldNormal(vertex.normal);
#else // !MESH_NORMALS
float3 normal = getFixedNormal();
#if defined(_FIXED_NORMALS_VIEWSPACE) || defined(_FIXED_NORMALS_VIEWSPACE_BACKFACE)
//View space fixed normal
//Rotate fixed normal by inverse view matrix to convert the fixed normal into world space
float3x3 invView = transpose((float3x3)UNITY_MATRIX_V);
return normalize(mul(invView, normal));
#else
//Model space fixed normal.
#if defined(FIXED_NORMALS_BACKFACE_RENDERING)
//If back face rendering is enabled and the sprite is facing away from the camera (ie we're rendering the backface) then need to flip the normal
normal *= backFaceSign;
#endif
return calculateWorldNormal(normal);
#endif
#endif // !MESH_NORMALS
}
inline half3 calculateSpriteViewNormal(VertexInput vertex, float backFaceSign)
{
#if defined(MESH_NORMALS)
return normalize(mul((float3x3)UNITY_MATRIX_IT_MV, vertex.normal));
#else // !MESH_NORMALS
float3 normal = getFixedNormal();
#if defined(_FIXED_NORMALS_VIEWSPACE) || defined(_FIXED_NORMALS_VIEWSPACE_BACKFACE)
//View space fixed normal
return normal;
#else
//Model space fixed normal
#if defined(FIXED_NORMALS_BACKFACE_RENDERING)
//If back face rendering is enabled and the sprite is facing away from the camera (ie we're rendering the backface) then need to flip the normal
normal *= backFaceSign;
#endif
return normalize(mul((float3x3)UNITY_MATRIX_IT_MV, normal));
#endif
#endif // !MESH_NORMALS
}
////////////////////////////////////////
// Normal map functions
//
#if defined(_NORMALMAP)
inline half3 calculateSpriteWorldBinormal(VertexInput vertex, half3 normalWorld, half3 tangentWorld, float backFaceSign)
{
float tangentSign = vertex.tangent.w;
#if defined(FIXED_NORMALS_BACKFACE_RENDERING)
tangentSign *= backFaceSign;
#endif
return calculateWorldBinormal(normalWorld, tangentWorld, tangentSign);
}
#endif // _NORMALMAP
#if defined(_DIFFUSE_RAMP)
////////////////////////////////////////
// Diffuse ramp functions
//
//Disable for softer, more traditional diffuse ramping
#define HARD_DIFFUSE_RAMP
uniform sampler2D _DiffuseRamp;
inline fixed3 calculateDiffuseRamp(float ramp)
{
return tex2D(_DiffuseRamp, float2(ramp, ramp)).rgb;
}
inline fixed3 calculateRampedDiffuse(fixed3 lightColor, float attenuation, float angleDot)
{
float d = angleDot * 0.5 + 0.5;
#if defined(HARD_DIFFUSE_RAMP)
half3 ramp = calculateDiffuseRamp(d * attenuation * 2);
return lightColor * ramp;
#else
half3 ramp = calculateDiffuseRamp(d);
return lightColor * ramp * (attenuation * 2);
#endif
}
#endif // _DIFFUSE_RAMP
////////////////////////////////////////
// Rim Lighting functions
//
#ifdef _RIM_LIGHTING
uniform float _RimPower;
uniform fixed4 _RimColor;
inline fixed3 applyRimLighting(fixed3 posWorld, fixed3 normalWorld, fixed4 pixel) : SV_Target
{
fixed3 viewDir = normalize(_WorldSpaceCameraPos - posWorld);
float invDot = 1.0 - saturate(dot(normalWorld, viewDir));
float rimPower = pow(invDot, _RimPower);
float rim = saturate(rimPower * _RimColor.a);
#if defined(_DIFFUSE_RAMP)
rim = calculateDiffuseRamp(rim).r;
#endif
return lerp(pixel.rgb, _RimColor.xyz * pixel.a, rim);
}
#endif //_RIM_LIGHTING
////////////////////////////////////////
// Emission functions
//
#ifdef _EMISSION
uniform sampler2D _EmissionMap;
uniform fixed4 _EmissionColor;
uniform float _EmissionPower;
#define APPLY_EMISSION(diffuse, uv) diffuse += tex2D(_EmissionMap, uv).rgb * _EmissionColor.rgb * _EmissionPower;
#define APPLY_EMISSION_SPECULAR(pixel, uv) pixel.rgb += (tex2D(_EmissionMap, uv).rgb * _EmissionColor.rgb * _EmissionPower) * pixel.a;
#else //!_EMISSION
#define APPLY_EMISSION(diffuse, uv)
#define APPLY_EMISSION_SPECULAR(pixel, uv)
#endif //!_EMISSION
#endif // SPRITE_LIGHTING_INCLUDED

View File

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

View File

@@ -0,0 +1,252 @@
#ifndef SPRITE_PIXEL_LIGHTING_INCLUDED
#define SPRITE_PIXEL_LIGHTING_INCLUDED
#include "ShaderShared.cginc"
#include "SpriteLighting.cginc"
#include "SpriteSpecular.cginc"
#include "AutoLight.cginc"
////////////////////////////////////////
// Defines
//
////////////////////////////////////////
// Vertex output struct
//
#if defined(_NORMALMAP)
#define _VERTEX_LIGHTING_INDEX TEXCOORD5
#define _LIGHT_COORD_INDEX_0 6
#define _LIGHT_COORD_INDEX_1 7
#define _FOG_COORD_INDEX 8
#else
#define _VERTEX_LIGHTING_INDEX TEXCOORD3
#define _LIGHT_COORD_INDEX_0 4
#define _LIGHT_COORD_INDEX_1 5
#define _FOG_COORD_INDEX 6
#endif // _NORMALMAP
struct VertexOutput
{
float4 pos : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 posWorld : TEXCOORD1;
half3 normalWorld : TEXCOORD2;
#if defined(_NORMALMAP)
half3 tangentWorld : TEXCOORD3;
half3 binormalWorld : TEXCOORD4;
#endif // _NORMALMAP
fixed3 vertexLighting : _VERTEX_LIGHTING_INDEX;
LIGHTING_COORDS(_LIGHT_COORD_INDEX_0, _LIGHT_COORD_INDEX_1)
#if defined(_FOG)
UNITY_FOG_COORDS(_FOG_COORD_INDEX)
#endif // _FOG
UNITY_VERTEX_OUTPUT_STEREO
};
////////////////////////////////////////
// Light calculations
//
uniform fixed4 _LightColor0;
inline fixed3 calculateLightDiffuse(VertexOutput input, float3 normalWorld, inout fixed4 albedo)
{
//For directional lights _WorldSpaceLightPos0.w is set to zero
float3 lightWorldDirection = normalize(_WorldSpaceLightPos0.xyz - input.posWorld.xyz * _WorldSpaceLightPos0.w);
float attenuation = LIGHT_ATTENUATION(input);
float angleDot = max(0, dot(normalWorld, lightWorldDirection));
#if defined(_DIFFUSE_RAMP)
fixed3 lightDiffuse = calculateRampedDiffuse(_LightColor0.rgb, attenuation, angleDot);
#else
fixed3 lightDiffuse = _LightColor0.rgb * (attenuation * angleDot);
#endif // _DIFFUSE_RAMP
return lightDiffuse;
}
inline float3 calculateNormalWorld(VertexOutput input)
{
#if defined(_NORMALMAP)
return calculateNormalFromBumpMap(input.texcoord, input.tangentWorld, input.binormalWorld, input.normalWorld);
#else
return input.normalWorld;
#endif
}
fixed3 calculateVertexLighting(float3 posWorld, float3 normalWorld)
{
fixed3 vertexLighting = fixed3(0,0,0);
#ifdef VERTEXLIGHT_ON
//Get approximated illumination from non-important point lights
vertexLighting = Shade4PointLights ( unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
unity_4LightAtten0, posWorld, normalWorld) * 0.5;
#endif
return vertexLighting;
}
fixed3 calculateAmbientLight(half3 normalWorld)
{
#if defined(_SPHERICAL_HARMONICS)
fixed3 ambient = ShadeSH9(half4(normalWorld, 1.0));
#else
fixed3 ambient = unity_AmbientSky.rgb;
#endif
return ambient;
}
#if defined(SPECULAR)
fixed4 calculateSpecularLight(SpecularCommonData s, float3 viewDir, float3 normal, float3 lightDir, float3 lightColor, half3 ambient)
{
SpecularLightData data = calculatePhysicsBasedSpecularLight (s.specColor, s.oneMinusReflectivity, s.smoothness, normal, viewDir, lightDir, lightColor, ambient, unity_IndirectSpecColor.rgb);
fixed4 pixel = calculateLitPixel(fixed4(s.diffColor, s.alpha), data.lighting);
pixel.rgb += data.specular * s.alpha;
return pixel;
}
fixed4 calculateSpecularLightAdditive(SpecularCommonData s, float3 viewDir, float3 normal, float3 lightDir, float3 lightColor)
{
SpecularLightData data = calculatePhysicsBasedSpecularLight (s.specColor, s.oneMinusReflectivity, s.smoothness, normal, viewDir, lightDir, lightColor, half3(0,0,0), half3(0,0,0));
fixed4 pixel = calculateAdditiveLitPixel(fixed4(s.diffColor, s.alpha), data.lighting);
pixel.rgb += data.specular * s.alpha;
return pixel;
}
#endif //SPECULAR
////////////////////////////////////////
// Vertex program
//
VertexOutput vert(VertexInput v)
{
VertexOutput output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
output.pos = calculateLocalPos(v.vertex);
output.color = calculateVertexColor(v.color);
output.texcoord = calculateTextureCoord(v.texcoord);
output.posWorld = calculateWorldPos(v.vertex);
float backFaceSign = 1;
#if defined(FIXED_NORMALS_BACKFACE_RENDERING)
backFaceSign = calculateBackfacingSign(output.posWorld.xyz);
#endif
output.normalWorld = calculateSpriteWorldNormal(v, backFaceSign);
output.vertexLighting = calculateVertexLighting(output.posWorld, output.normalWorld);
#if defined(_NORMALMAP)
output.tangentWorld = calculateWorldTangent(v.tangent);
output.binormalWorld = calculateSpriteWorldBinormal(v, output.normalWorld, output.tangentWorld, backFaceSign);
#endif
TRANSFER_VERTEX_TO_FRAGMENT(output)
#if defined(_FOG)
UNITY_TRANSFER_FOG(output,output.pos);
#endif // _FOG
return output;
}
////////////////////////////////////////
// Fragment programs
//
fixed4 fragBase(VertexOutput input) : SV_Target
{
fixed4 texureColor = calculateTexturePixel(input.texcoord);
ALPHA_CLIP(texureColor, input.color)
//Get normal direction
fixed3 normalWorld = calculateNormalWorld(input);
//Get Ambient diffuse
fixed3 ambient = calculateAmbientLight(normalWorld);
#if defined(SPECULAR)
//For directional lights _WorldSpaceLightPos0.w is set to zero
float3 lightWorldDirection = normalize(_WorldSpaceLightPos0.xyz - input.posWorld.xyz * _WorldSpaceLightPos0.w);
float attenuation = LIGHT_ATTENUATION(input);
//Returns pixel lit by light, texture color should inlcluded alpha
half3 viewDir = normalize(_WorldSpaceCameraPos - input.posWorld.xyz);
fixed4 pixel = calculateSpecularLight(getSpecularData(input.texcoord.xy, texureColor, input.color), viewDir, normalWorld, lightWorldDirection, _LightColor0.rgb * attenuation, ambient + input.vertexLighting);
APPLY_EMISSION_SPECULAR(pixel, input.texcoord)
#else
//Get primary pixel light diffuse
fixed3 diffuse = calculateLightDiffuse(input, normalWorld, texureColor);
//Combine along with vertex lighting for the base lighting pass
fixed3 lighting = ambient + diffuse + input.vertexLighting;
APPLY_EMISSION(lighting, input.texcoord)
fixed4 pixel = calculateLitPixel(texureColor, input.color, lighting);
#endif
#if defined(_RIM_LIGHTING)
pixel.rgb = applyRimLighting(input.posWorld, normalWorld, pixel);
#endif
COLORISE(pixel)
APPLY_FOG(pixel, input)
return pixel;
}
fixed4 fragAdd(VertexOutput input) : SV_Target
{
fixed4 texureColor = calculateTexturePixel(input.texcoord);
#if defined(_COLOR_ADJUST)
texureColor = adjustColor(texureColor);
#endif // _COLOR_ADJUST
ALPHA_CLIP(texureColor, input.color)
//Get normal direction
fixed3 normalWorld = calculateNormalWorld(input);
#if defined(SPECULAR)
//For directional lights _WorldSpaceLightPos0.w is set to zero
float3 lightWorldDirection = normalize(_WorldSpaceLightPos0.xyz - input.posWorld.xyz * _WorldSpaceLightPos0.w);
float attenuation = LIGHT_ATTENUATION(input);
half3 viewDir = normalize(_WorldSpaceCameraPos - input.posWorld.xyz);
fixed4 pixel = calculateSpecularLightAdditive(getSpecularData(input.texcoord.xy, texureColor, input.color), viewDir, normalWorld, lightWorldDirection, _LightColor0.rgb * attenuation);
#else
//Get light diffuse
fixed3 lighting = calculateLightDiffuse(input, normalWorld, texureColor);
fixed4 pixel = calculateAdditiveLitPixel(texureColor, input.color, lighting);
#endif
COLORISE_ADDITIVE(pixel)
APPLY_FOG_ADDITIVE(pixel, input)
return pixel;
}
#endif // SPRITE_PIXEL_LIGHTING_INCLUDED

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 7ffc57e05c42ec748838bea0a3aff9f9
timeCreated: 1494092582
licenseType: Free
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,49 @@
#ifndef SPRITE_SHADOWS_INCLUDED
#define SPRITE_SHADOWS_INCLUDED
#include "ShaderShared.cginc"
////////////////////////////////////////
// Vertex structs
//
struct vertexInput
{
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct vertexOutput
{
V2F_SHADOW_CASTER;
float2 texcoord : TEXCOORD1;
};
////////////////////////////////////////
// Vertex program
//
vertexOutput vert(vertexInput v)
{
vertexOutput o;
TRANSFER_SHADOW_CASTER(o)
o.texcoord = calculateTextureCoord(v.texcoord);
return o;
}
////////////////////////////////////////
// Fragment program
//
uniform fixed _ShadowAlphaCutoff;
fixed4 frag(vertexOutput IN) : SV_Target
{
fixed4 texureColor = calculateTexturePixel(IN.texcoord);
clip(texureColor.a - _ShadowAlphaCutoff);
SHADOW_CASTER_FRAGMENT(IN)
}
#endif // SPRITE_SHADOWS_INCLUDED

View File

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

View File

@@ -0,0 +1,246 @@
#ifndef SPRITE_SPECULAR_INCLUDED
#define SPRITE_SPECULAR_INCLUDED
#include "ShaderMaths.cginc"
////////////////////////////////////////
// Specular functions
//
#if defined(_SPECULAR) || defined(_SPECULAR_GLOSSMAP)
#define SPECULAR
//ALL THESE FUNCTIONS ARE TAKEN AND ADAPTED FROM UNITY'S OWN PHYSICS BASED STANDARD SHADER
uniform float _Metallic;
uniform float _Glossiness;
uniform float _GlossMapScale;
uniform sampler2D _MetallicGlossMap;
struct SpecularLightData
{
half3 lighting;
half3 specular;
};
struct SpecularCommonData
{
half3 diffColor, specColor;
// Note: smoothness & oneMinusReflectivity for optimization purposes, mostly for DX9 SM2.0 level.
// Most of the math is being done on these (1-x) values, and that saves a few precious ALU slots.
half oneMinusReflectivity, smoothness;
half alpha;
};
inline half2 getMetallicGloss(float2 uv)
{
half2 mg;
#ifdef _SPECULAR_GLOSSMAP
mg = tex2D(_MetallicGlossMap, uv).ra;
mg.g *= _GlossMapScale;
#else
mg.r = _Metallic;
mg.g = _Glossiness;
#endif
return mg;
}
inline half getOneMinusReflectivityFromMetallic(half metallic)
{
// We'll need oneMinusReflectivity, so
// 1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic)
// store (1-dielectricSpec) in unity_ColorSpaceDielectricSpec.a, then
// 1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) =
// = alpha - metallic * alpha
half oneMinusDielectricSpec = unity_ColorSpaceDielectricSpec.a;
return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
}
inline SpecularCommonData getSpecularData(float2 uv, half4 texureColor, fixed4 color)
{
half2 metallicGloss = getMetallicGloss(uv);
half metallic = metallicGloss.x;
half smoothness = metallicGloss.y; // this is 1 minus the square root of real roughness m.
fixed4 albedo = calculatePixel(texureColor, color);
half3 specColor = lerp (unity_ColorSpaceDielectricSpec.rgb, albedo, metallic);
half oneMinusReflectivity = getOneMinusReflectivityFromMetallic(metallic);
half3 diffColor = albedo * oneMinusReflectivity;
SpecularCommonData o = (SpecularCommonData)0;
o.diffColor = diffColor;
o.specColor = specColor;
o.oneMinusReflectivity = oneMinusReflectivity;
o.smoothness = smoothness;
#if defined(_ALPHAPREMULTIPLY_ON) && (SHADER_TARGET >= 30)
// Reflectivity 'removes' from the rest of components, including Transparency
// outAlpha = 1-(1-alpha)*(1-reflectivity) = 1-(oneMinusReflectivity - alpha*oneMinusReflectivity) =
// = 1-oneMinusReflectivity + alpha*oneMinusReflectivity
//o.alpha = 1-oneMinusReflectivity + albedo.a*oneMinusReflectivity;
o.alpha = albedo.a;
#else
o.alpha = albedo.a;
#endif
return o;
}
inline half SmoothnessToPerceptualRoughness(half smoothness)
{
return (1 - smoothness);
}
inline half PerceptualRoughnessToRoughness(half perceptualRoughness)
{
return perceptualRoughness * perceptualRoughness;
}
// Ref: http://jcgt.org/published/0003/02/03/paper.pdf
inline half SmithJointGGXVisibilityTerm (half NdotL, half NdotV, half roughness)
{
#if 0
// Original formulation:
// lambda_v = (-1 + sqrt(a2 * (1 - NdotL2) / NdotL2 + 1)) * 0.5f;
// lambda_l = (-1 + sqrt(a2 * (1 - NdotV2) / NdotV2 + 1)) * 0.5f;
// G = 1 / (1 + lambda_v + lambda_l);
// Reorder code to be more optimal
half a = roughness;
half a2 = a * a;
half lambdaV = NdotL * sqrt((-NdotV * a2 + NdotV) * NdotV + a2);
half lambdaL = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2);
// Simplify visibility term: (2.0f * NdotL * NdotV) / ((4.0f * NdotL * NdotV) * (lambda_v + lambda_l + 1e-5f));
return 0.5f / (lambdaV + lambdaL + 1e-5f); // This function is not intended to be running on Mobile,
// therefore epsilon is smaller than can be represented by half
#else
// Approximation of the above formulation (simplify the sqrt, not mathematically correct but close enough)
half a = roughness;
half lambdaV = NdotL * (NdotV * (1 - a) + a);
half lambdaL = NdotV * (NdotL * (1 - a) + a);
return 0.5f / (lambdaV + lambdaL + 1e-5f);
#endif
}
inline half GGXTerm (half NdotH, half roughness)
{
half a2 = roughness * roughness;
half d = (NdotH * a2 - NdotH) * NdotH + 1.0f; // 2 mad
return UNITY_INV_PI * a2 / (d * d + 1e-7f); // This function is not intended to be running on Mobile,
// therefore epsilon is smaller than what can be represented by half
}
inline half3 FresnelTerm (half3 F0, half cosA)
{
half t = pow5 (1 - cosA); // ala Schlick interpoliation
return F0 + (1-F0) * t;
}
inline half3 FresnelLerp (half3 F0, half F90, half cosA)
{
half t = pow5 (1 - cosA); // ala Schlick interpoliation
return lerp (F0, F90, t);
}
// Note: Disney diffuse must be multiply by diffuseAlbedo / PI. This is done outside of this function.
inline half DisneyDiffuse(half NdotV, half NdotL, half LdotH, half perceptualRoughness)
{
half fd90 = 0.5 + 2 * LdotH * LdotH * perceptualRoughness;
// Two schlick fresnel term
half lightScatter = (1 + (fd90 - 1) * pow5(1 - NdotL));
half viewScatter = (1 + (fd90 - 1) * pow5(1 - NdotV));
return lightScatter * viewScatter;
}
// Main Physically Based BRDF
// Derived from Disney work and based on Torrance-Sparrow micro-facet model
//
// BRDF = kD / pi + kS * (D * V * F) / 4
// I = BRDF * NdotL
//
// * NDF (depending on UNITY_BRDF_GGX):
// a) Normalized BlinnPhong
// b) GGX
// * Smith for Visiblity term
// * Schlick approximation for Fresnel
SpecularLightData calculatePhysicsBasedSpecularLight(half3 specColor, half oneMinusReflectivity, half smoothness, half3 normal, half3 viewDir, half3 lightdir, half3 lightColor, half3 indirectDiffuse, half3 indirectSpecular)
{
half perceptualRoughness = SmoothnessToPerceptualRoughness (smoothness);
half3 halfDir = safeNormalize (lightdir + viewDir);
// NdotV should not be negative for visible pixels, but it can happen due to perspective projection and normal mapping
// In this case normal should be modified to become valid (i.e facing camera) and not cause weird artifacts.
// but this operation adds few ALU and users may not want it. Alternative is to simply take the abs of NdotV (less correct but works too).
// Following define allow to control this. Set it to 0 if ALU is critical on your platform.
// This correction is interesting for GGX with SmithJoint visibility function because artifacts are more visible in this case due to highlight edge of rough surface
// Edit: Disable this code by default for now as it is not compatible with two sided lighting used in SpeedTree.
#define UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV 0
#if UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV
// The amount we shift the normal toward the view vector is defined by the dot product.
half shiftAmount = dot(normal, viewDir);
normal = shiftAmount < 0.0f ? normal + viewDir * (-shiftAmount + 1e-5f) : normal;
// A re-normalization should be applied here but as the shift is small we don't do it to save ALU.
//normal = normalize(normal);
half nv = saturate(dot(normal, viewDir)); // TODO: this saturate should no be necessary here
#else
half nv = abs(dot(normal, viewDir)); // This abs allow to limit artifact
#endif
half nl = saturate(dot(normal, lightdir));
half nh = saturate(dot(normal, halfDir));
half lv = saturate(dot(lightdir, viewDir));
half lh = saturate(dot(lightdir, halfDir));
// Diffuse term
half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness) * nl;
// Specular term
// HACK: theoretically we should divide diffuseTerm by Pi and not multiply specularTerm!
// BUT 1) that will make shader look significantly darker than Legacy ones
// and 2) on engine side "Non-important" lights have to be divided by Pi too in cases when they are injected into ambient SH
half roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
half V = SmithJointGGXVisibilityTerm (nl, nv, roughness);
half D = GGXTerm (nh, roughness);
half specularTerm = V*D * UNITY_PI; // Torrance-Sparrow model, Fresnel is applied later
# ifdef UNITY_COLORSPACE_GAMMA
specularTerm = sqrt(max(1e-4h, specularTerm));
# endif
// specularTerm * nl can be NaN on Metal in some cases, use max() to make sure it's a sane value
specularTerm = max(0, specularTerm * nl);
// surfaceReduction = Int D(NdotH) * NdotH * Id(NdotL>0) dH = 1/(roughness^2+1)
half surfaceReduction;
# ifdef UNITY_COLORSPACE_GAMMA
surfaceReduction = 1.0 - 0.28f * roughness * perceptualRoughness; // 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1]
# else
surfaceReduction = 1.0 / (roughness*roughness + 1.0); // fade \in [0.5;1]
# endif
// To provide true Lambert lighting, we need to be able to kill specular completely.
specularTerm *= any(specColor) ? 1.0 : 0.0;
half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity));
SpecularLightData outData = (SpecularLightData)0;
outData.lighting = indirectDiffuse + lightColor * diffuseTerm;
outData.specular = (specularTerm * lightColor * FresnelTerm (specColor, lh)) + (surfaceReduction * indirectSpecular * FresnelLerp (specColor, grazingTerm, nv));
return outData;
}
#endif // _SPECULAR && _SPECULAR_GLOSSMAP
#endif // SPRITE_SPECULAR_INCLUDED

View File

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

View File

@@ -0,0 +1,72 @@
#ifndef SPRITE_UNLIT_INCLUDED
#define SPRITE_UNLIT_INCLUDED
#include "ShaderShared.cginc"
////////////////////////////////////////
// Vertex structs
//
struct VertexInput
{
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
fixed4 color : COLOR;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct VertexOutput
{
float4 pos : SV_POSITION;
float2 texcoord : TEXCOORD0;
fixed4 color : COLOR;
#if defined(_FOG)
UNITY_FOG_COORDS(1)
#endif // _FOG
UNITY_VERTEX_OUTPUT_STEREO
};
////////////////////////////////////////
// Vertex program
//
VertexOutput vert(VertexInput input)
{
VertexOutput output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
output.pos = calculateLocalPos(input.vertex);
output.texcoord = calculateTextureCoord(input.texcoord);
output.color = calculateVertexColor(input.color);
#if defined(_FOG)
UNITY_TRANSFER_FOG(output,output.pos);
#endif // _FOG
return output;
}
////////////////////////////////////////
// Fragment program
//
fixed4 frag(VertexOutput input) : SV_Target
{
fixed4 texureColor = calculateTexturePixel(input.texcoord.xy);
ALPHA_CLIP(texureColor, input.color)
fixed4 pixel = calculatePixel(texureColor, input.color);
COLORISE(pixel)
APPLY_FOG(pixel, input)
return pixel;
}
#endif // SPRITE_UNLIT_INCLUDED

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 072e7b07ec7fb1346a9dcd3bcbbb7111
timeCreated: 1494092582
licenseType: Free
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,474 @@
#ifndef SPRITE_VERTEX_LIGHTING_INCLUDED
#define SPRITE_VERTEX_LIGHTING_INCLUDED
#include "ShaderShared.cginc"
#include "SpriteLighting.cginc"
#include "SpriteSpecular.cginc"
////////////////////////////////////////
// Defines
//
//Define to use spot lights (more expensive)
#define SPOT_LIGHTS
//Have to process lighting per pixel if using normal maps or a diffuse ramp or rim lighting or specular
#if defined(_NORMALMAP) || defined(_DIFFUSE_RAMP) || defined(_RIM_LIGHTING) || defined(SPECULAR)
#define PER_PIXEL_LIGHTING
#endif
//Turn off bump mapping and diffuse ramping on older shader models as they dont support needed number of outputs
#if defined(PER_PIXEL_LIGHTING) && (SHADER_TARGET < 30)
#undef PER_PIXEL_LIGHTING
#undef _NORMALMAP
#undef _DIFFUSE_RAMP
#undef _RIM_LIGHTING
#endif
//In D3D9 only have a max of 9 TEXCOORD so can't have diffuse ramping or fog or rim lighting if processing lights per pixel
#if defined(SHADER_API_D3D9) && defined(PER_PIXEL_LIGHTING)
#if defined(_NORMALMAP)
#undef _DIFFUSE_RAMP
#undef _FOG
#undef _RIM_LIGHTING
#elif defined(_DIFFUSE_RAMP)
#undef _FOG
#undef _RIM_LIGHTING
#elif defined(_RIM_LIGHTING)
#undef _FOG
#undef _DIFFUSE_RAMP
#else
#undef _DIFFUSE_RAMP
#undef _RIM_LIGHTING
#endif
#endif
#if defined(PER_PIXEL_LIGHTING)
#if defined(_NORMALMAP) && defined(_DIFFUSE_RAMP)
#define ATTENUATIONS TEXCOORD9
#if defined(_RIM_LIGHTING)
#define _POS_WORLD_INDEX TEXCOORD10
#define _FOG_COORD_INDEX 11
#else
#define _FOG_COORD_INDEX 10
#endif
#elif defined(_NORMALMAP) != defined(_DIFFUSE_RAMP)
#define ATTENUATIONS TEXCOORD8
#if defined(_RIM_LIGHTING)
#define _POS_WORLD_INDEX TEXCOORD9
#define _FOG_COORD_INDEX 10
#else
#define _FOG_COORD_INDEX 9
#endif
#else //!_DIFFUSE_RAMP && !_NORMALMAP
#if defined(_RIM_LIGHTING)
#define _POS_WORLD_INDEX TEXCOORD8
#define _FOG_COORD_INDEX 9
#else
#define _FOG_COORD_INDEX 8
#endif
#endif
#else //!PER_PIXEL_LIGHTING
#define _FOG_COORD_INDEX 2
#endif
////////////////////////////////////////
// Vertex output struct
//
struct VertexOutput
{
float4 pos : SV_POSITION;
fixed4 color : COLOR;
float3 texcoord : TEXCOORD0;
#if defined(PER_PIXEL_LIGHTING)
half4 VertexLightInfo0 : TEXCOORD1;
half4 VertexLightInfo1 : TEXCOORD2;
half4 VertexLightInfo2 : TEXCOORD3;
half4 VertexLightInfo3 : TEXCOORD4;
half4 VertexLightInfo4 : TEXCOORD5;
#if defined(_NORMALMAP)
half4 normalWorld : TEXCOORD6;
half4 tangentWorld : TEXCOORD7;
half4 binormalWorld : TEXCOORD8;
#else
half3 normalWorld : TEXCOORD6;
half3 VertexLightInfo5 : TEXCOORD7;
#endif
#if defined(_DIFFUSE_RAMP)
half4 LightAttenuations : ATTENUATIONS;
#endif
#if defined(_RIM_LIGHTING)
float4 posWorld : _POS_WORLD_INDEX;
#endif
#else //!PER_PIXEL_LIGHTING
half3 FullLighting : TEXCOORD1;
#endif // !PER_PIXEL_LIGHTING
#if defined(_FOG)
UNITY_FOG_COORDS(_FOG_COORD_INDEX)
#endif // _FOG
UNITY_VERTEX_OUTPUT_STEREO
};
////////////////////////////////////////
// Light calculations
//
struct VertexLightInfo
{
half3 lightDirection;
fixed3 lightColor;
#if defined(_DIFFUSE_RAMP)
float attenuation;
#endif // _DIFFUSE_RAMP
};
inline VertexLightInfo getVertexLightAttenuatedInfo(int index, float3 viewPos)
{
VertexLightInfo lightInfo;
//For directional lights unity_LightPosition.w is set to zero
lightInfo.lightDirection = unity_LightPosition[index].xyz - viewPos.xyz * unity_LightPosition[index].w;
float lengthSq = dot(lightInfo.lightDirection, lightInfo.lightDirection);
// don't produce NaNs if some vertex position overlaps with the light
lengthSq = max(lengthSq, 0.000001);
lightInfo.lightDirection *= rsqrt(lengthSq);
float attenuation = 1.0 / (1.0 + lengthSq * unity_LightAtten[index].z);
#if defined(SPOT_LIGHTS)
//Spot light attenuation - for non-spot lights unity_LightAtten.x is set to -1 and y is set to 1
{
float rho = max (0, dot(lightInfo.lightDirection, unity_SpotDirection[index].xyz));
float spotAtt = (rho - unity_LightAtten[index].x) * unity_LightAtten[index].y;
attenuation *= saturate(spotAtt);
}
#endif // SPOT_LIGHTS
//If using a diffuse ramp texture then need to pass through the lights attenuation, otherwise premultiply the light color with it
#if defined(_DIFFUSE_RAMP)
lightInfo.lightColor = unity_LightColor[index].rgb;
lightInfo.attenuation = attenuation;
#else
lightInfo.lightColor = unity_LightColor[index].rgb * attenuation;
#endif // _DIFFUSE_RAMP
return lightInfo;
}
fixed3 calculateAmbientLight(half3 normalWorld)
{
#if defined(_SPHERICAL_HARMONICS)
//Magic constants used to tweak ambient to approximate pixel shader spherical harmonics
static const fixed3 worldUp = fixed3(0,1,0);
static const float skyGroundDotMul = 2.5;
static const float minEquatorMix = 0.5;
static const float equatorColorBlur = 0.33;
float upDot = dot(normalWorld, worldUp);
//Fade between a flat lerp from sky to ground and a 3 way lerp based on how bright the equator light is.
//This simulates how directional lights get blurred using spherical harmonics
//Work out color from ground and sky, ignoring equator
float adjustedDot = upDot * skyGroundDotMul;
fixed3 skyGroundColor = lerp(unity_AmbientGround, unity_AmbientSky, saturate((adjustedDot + 1.0) * 0.5));
//Work out equator lights brightness
float equatorBright = saturate(dot(unity_AmbientEquator.rgb, unity_AmbientEquator.rgb));
//Blur equator color with sky and ground colors based on how bright it is.
fixed3 equatorBlurredColor = lerp(unity_AmbientEquator, saturate(unity_AmbientEquator + unity_AmbientGround + unity_AmbientSky), equatorBright * equatorColorBlur);
//Work out 3 way lerp inc equator light
fixed3 equatorColor = lerp(equatorBlurredColor, unity_AmbientGround, -upDot) * step(upDot, 0) + lerp(equatorBlurredColor, unity_AmbientSky, upDot) * step(0, upDot);
//Mix the two colors together based on how bright the equator light is
return lerp(skyGroundColor, equatorColor, saturate(equatorBright + minEquatorMix));
#else // !_SPHERICAL_HARMONICS
//Flat ambient is just the sky color
return unity_AmbientSky.rgb;
#endif // !_SPHERICAL_HARMONICS
}
////////////////////////////////////////
// Light Packing Functions
//
#if defined(_DIFFUSE_RAMP)
inline fixed3 calculateLightDiffuse(fixed3 lightColor, half3 viewNormal, half3 lightViewDir, float attenuation)
{
float angleDot = max(0, dot(viewNormal, lightViewDir));
fixed3 lightDiffuse = calculateRampedDiffuse(lightColor, attenuation, angleDot);
return lightDiffuse;
}
#else
inline fixed3 calculateLightDiffuse(fixed3 attenuatedLightColor, half3 viewNormal, half3 lightViewDir)
{
float angleDot = max(0, dot(viewNormal, lightViewDir));
fixed3 lightDiffuse = attenuatedLightColor * angleDot;
return lightDiffuse;
}
#endif // _NORMALMAP
#if defined(PER_PIXEL_LIGHTING)
#define VERTEX_LIGHT_0_DIR VertexLightInfo0.xyz
#define VERTEX_LIGHT_0_R VertexLightInfo4.x
#define VERTEX_LIGHT_0_G VertexLightInfo4.y
#define VERTEX_LIGHT_0_B VertexLightInfo4.z
#define VERTEX_LIGHT_1_DIR VertexLightInfo1.xyz
#define VERTEX_LIGHT_1_R VertexLightInfo0.w
#define VERTEX_LIGHT_1_G VertexLightInfo1.w
#define VERTEX_LIGHT_1_B VertexLightInfo2.w
#define VERTEX_LIGHT_2_DIR VertexLightInfo2.xyz
#define VERTEX_LIGHT_2_R VertexLightInfo3.w
#define VERTEX_LIGHT_2_G VertexLightInfo4.w
#define VERTEX_LIGHT_2_B texcoord.z
#define VERTEX_LIGHT_3_DIR VertexLightInfo3.xyz
#if defined(_NORMALMAP)
#define VERTEX_LIGHT_3_R normalWorld.w
#define VERTEX_LIGHT_3_G tangentWorld.w
#define VERTEX_LIGHT_3_B binormalWorld.w
#else
#define VERTEX_LIGHT_3_R VertexLightInfo5.x
#define VERTEX_LIGHT_3_G VertexLightInfo5.y
#define VERTEX_LIGHT_3_B VertexLightInfo5.z
#endif
#if defined(_DIFFUSE_RAMP)
#define LIGHT_DIFFUSE_ATTEN_0 LightAttenuations.x
#define LIGHT_DIFFUSE_ATTEN_1 LightAttenuations.y
#define LIGHT_DIFFUSE_ATTEN_2 LightAttenuations.z
#define LIGHT_DIFFUSE_ATTEN_3 LightAttenuations.w
#define PACK_VERTEX_LIGHT_DIFFUSE(index, output, lightInfo) \
{ \
output.LIGHT_DIFFUSE_ATTEN_##index = lightInfo.attenuation; \
}
#define ADD_VERTEX_LIGHT_DIFFUSE(index, diffuse, input, lightColor, viewNormal, lightViewDir) \
{ \
diffuse += calculateLightDiffuse(lightColor, viewNormal, lightViewDir, input.LIGHT_DIFFUSE_ATTEN_##index); \
}
#else
#define PACK_VERTEX_LIGHT_DIFFUSE(index, output, lightInfo)
#define ADD_VERTEX_LIGHT_DIFFUSE(index, diffuse, input, lightColor, viewNormal, lightViewDir) \
{ \
diffuse += calculateLightDiffuse(lightColor, viewNormal, lightViewDir); \
}
#endif
#define PACK_VERTEX_LIGHT(index, output, viewPos) \
{ \
VertexLightInfo lightInfo = getVertexLightAttenuatedInfo(index, viewPos); \
output.VERTEX_LIGHT_##index##_DIR = lightInfo.lightDirection; \
output.VERTEX_LIGHT_##index##_R = lightInfo.lightColor.r; \
output.VERTEX_LIGHT_##index##_G = lightInfo.lightColor.g; \
output.VERTEX_LIGHT_##index##_B = lightInfo.lightColor.b; \
PACK_VERTEX_LIGHT_DIFFUSE(index, output, lightInfo); \
}
#define ADD_VERTEX_LIGHT(index, input, viewNormal, diffuse) \
{ \
half3 lightViewDir = input.VERTEX_LIGHT_##index##_DIR; \
fixed3 lightColor = fixed3(input.VERTEX_LIGHT_##index##_R, input.VERTEX_LIGHT_##index##_G, input.VERTEX_LIGHT_##index##_B); \
ADD_VERTEX_LIGHT_DIFFUSE(index, diffuse, input, lightColor, viewNormal, lightViewDir) \
}
#if defined(SPECULAR)
#define ADD_VERTEX_LIGHT_SPEC(index, input, viewNormal, specData, combinedLightData, indirectDiffuse, indirectSpecular) \
{ \
half3 lightViewDir = input.VERTEX_LIGHT_##index##_DIR; \
fixed3 lightColor = fixed3(input.VERTEX_LIGHT_##index##_R, input.VERTEX_LIGHT_##index##_G, input.VERTEX_LIGHT_##index##_B); \
SpecularLightData lightData = calculatePhysicsBasedSpecularLight(specData.specColor, specData.oneMinusReflectivity, specData.smoothness, viewNormal, fixed3(0,0,1), lightViewDir, lightColor, indirectDiffuse, indirectSpecular); \
combinedLightData.lighting += lightData.lighting; \
combinedLightData.specular += lightData.specular; \
}
#endif
#else //!PER_PIXEL_LIGHTING
////////////////////////////////////////
// Vertex Only Functions
//
inline fixed3 calculateLightDiffuse(int index, float3 viewPos, half3 viewNormal)
{
VertexLightInfo lightInfo = getVertexLightAttenuatedInfo(index, viewPos);
float angleDot = max(0, dot(viewNormal, lightInfo.lightDirection));
return lightInfo.lightColor * angleDot;
}
#endif // !PER_PIXEL_LIGHTING
////////////////////////////////////////
// Vertex program
//
VertexOutput vert(VertexInput input)
{
VertexOutput output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
output.pos = calculateLocalPos(input.vertex);
output.color = calculateVertexColor(input.color);
output.texcoord = float3(calculateTextureCoord(input.texcoord), 0);
float3 viewPos = UnityObjectToViewPos(input.vertex); //float3 viewPos = mul(UNITY_MATRIX_MV, input.vertex); //
#if defined(FIXED_NORMALS_BACKFACE_RENDERING) || defined(_RIM_LIGHTING)
float4 powWorld = calculateWorldPos(input.vertex);
#endif
float backFaceSign = 1;
#if defined(FIXED_NORMALS_BACKFACE_RENDERING)
backFaceSign = calculateBackfacingSign(powWorld.xyz);
#endif
#if defined(PER_PIXEL_LIGHTING)
#if defined(_RIM_LIGHTING)
output.posWorld = powWorld;
#endif
PACK_VERTEX_LIGHT(0, output, viewPos)
PACK_VERTEX_LIGHT(1, output, viewPos)
PACK_VERTEX_LIGHT(2, output, viewPos)
PACK_VERTEX_LIGHT(3, output, viewPos)
output.normalWorld.xyz = calculateSpriteWorldNormal(input, backFaceSign);
#if defined(_NORMALMAP)
output.tangentWorld.xyz = calculateWorldTangent(input.tangent);
output.binormalWorld.xyz = calculateSpriteWorldBinormal(input, output.normalWorld, output.tangentWorld, backFaceSign);
#endif
#else // !PER_PIXEL_LIGHTING
//Just pack full lighting
float3 viewNormal = calculateSpriteViewNormal(input, backFaceSign);
//Get Ambient diffuse
float3 normalWorld = calculateSpriteWorldNormal(input, backFaceSign);
fixed3 ambient = calculateAmbientLight(normalWorld);
fixed3 diffuse = calculateLightDiffuse(0, viewPos, viewNormal);
diffuse += calculateLightDiffuse(1, viewPos, viewNormal);
diffuse += calculateLightDiffuse(2, viewPos, viewNormal);
diffuse += calculateLightDiffuse(3, viewPos, viewNormal);
output.FullLighting = ambient + diffuse;
#endif // !PER_PIXEL_LIGHTING
#if defined(_FOG)
UNITY_TRANSFER_FOG(output, output.pos);
#endif // _FOG
return output;
}
////////////////////////////////////////
// Fragment program
//
fixed4 frag(VertexOutput input) : SV_Target
{
fixed4 texureColor = calculateTexturePixel(input.texcoord.xy);
ALPHA_CLIP(texureColor, input.color)
#if defined(PER_PIXEL_LIGHTING)
#if defined(_NORMALMAP)
half3 normalWorld = calculateNormalFromBumpMap(input.texcoord.xy, input.tangentWorld.xyz, input.binormalWorld.xyz, input.normalWorld.xyz);
#else
half3 normalWorld = input.normalWorld.xyz;
#endif
//Get Ambient diffuse
fixed3 ambient = calculateAmbientLight(normalWorld);
half3 normalView = normalize(mul((float3x3)UNITY_MATRIX_V, normalWorld));
#if defined(SPECULAR)
SpecularCommonData specData = getSpecularData(input.texcoord.xy, texureColor, input.color);
SpecularLightData combinedLightData = (SpecularLightData)0;
ADD_VERTEX_LIGHT_SPEC(0, input, normalView, specData, combinedLightData, ambient, unity_IndirectSpecColor.rgb)
ADD_VERTEX_LIGHT_SPEC(1, input, normalView, specData, combinedLightData, fixed3(0,0,0), fixed3(0,0,0))
ADD_VERTEX_LIGHT_SPEC(2, input, normalView, specData, combinedLightData, fixed3(0,0,0), fixed3(0,0,0))
ADD_VERTEX_LIGHT_SPEC(3, input, normalView, specData, combinedLightData, fixed3(0,0,0), fixed3(0,0,0))
fixed4 pixel = calculateLitPixel(fixed4(specData.diffColor, specData.alpha), combinedLightData.lighting);
pixel.rgb += combinedLightData.specular * specData.alpha;
APPLY_EMISSION_SPECULAR(pixel, input.texcoord)
#else
//Find vertex light diffuse
fixed3 diffuse = fixed3(0,0,0);
//Add each vertex light to diffuse
ADD_VERTEX_LIGHT(0, input, normalView, diffuse)
ADD_VERTEX_LIGHT(1, input, normalView, diffuse)
ADD_VERTEX_LIGHT(2, input, normalView, diffuse)
ADD_VERTEX_LIGHT(3, input, normalView, diffuse)
fixed3 lighting = ambient + diffuse;
APPLY_EMISSION(lighting, input.texcoord.xy)
fixed4 pixel = calculateLitPixel(texureColor, input.color, lighting);
#endif
#if defined(_RIM_LIGHTING)
pixel.rgb = applyRimLighting(input.posWorld, normalWorld, pixel);
#endif
#else // !PER_PIXEL_LIGHTING
APPLY_EMISSION(input.FullLighting, input.texcoord.xy)
fixed4 pixel = calculateLitPixel(texureColor, input.color, input.FullLighting);
#endif // !PER_PIXEL_LIGHTING
COLORISE(pixel)
APPLY_FOG(pixel, input)
return pixel;
}
#endif // SPRITE_VERTEX_LIGHTING_INCLUDED

View File

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

View File

@@ -0,0 +1,522 @@
Shader "Hidden/Sprite-CameraDepthNormalsTexture" {
// Use this shader to render a DepthNormals texture for a camera with correct sprite normals (using camera.RenderWithShader with replacement tag "RenderType")
Properties {
_MainTex ("", 2D) = "white" {}
_Cutoff ("", Float) = 0.5
_Color ("", Color) = (1,1,1,1)
}
SubShader {
Tags { "RenderType"="Sprite" }
Pass {
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderShared.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
uniform float4 _FixedNormal;
v2f vert( appdata_base v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = calculateTextureCoord(v.texcoord);
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = calculateTexturePixel(i.uv );
float alpha = texcol.a*_Color.a;
clip( alpha - _Cutoff );
return EncodeDepthNormal (i.nz.w, i.nz.xyz);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="SpriteViewSpaceFixedNormal" }
Pass {
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderShared.cginc"
#include "CGIncludes/SpriteLighting.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_base v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = calculateTextureCoord(v.texcoord);
o.nz.xyz = getFixedNormal();
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = calculateTexturePixel(i.uv );
float alpha = texcol.a*_Color.a;
clip( alpha - _Cutoff );
return EncodeDepthNormal (i.nz.w, i.nz.xyz);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="SpriteModelSpaceFixedNormal" }
Pass {
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderShared.cginc"
#include "CGIncludes/SpriteLighting.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_base v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = calculateTextureCoord(v.texcoord);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex);
float3 normal = getFixedNormal();
//Only do this if backface is enabled :/
normal *= calculateBackfacingSign(worldPos.xyz);
//
o.nz.xyz = normalize(mul((float3x3)UNITY_MATRIX_IT_MV, normal));
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = calculateTexturePixel(i.uv );
float alpha = texcol.a*_Color.a;
clip( alpha - _Cutoff );
return EncodeDepthNormal (i.nz.w, i.nz.xyz);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="Opaque" }
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float4 nz : TEXCOORD0;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_base v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
fixed4 frag(v2f i) : SV_Target {
return EncodeDepthNormal (i.nz.w, i.nz.xyz);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TransparentCutout" }
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
uniform float4 _MainTex_ST;
v2f vert( appdata_base v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
uniform fixed4 _Color;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = tex2D( _MainTex, i.uv );
clip( texcol.a*_Color.a - _Cutoff );
return EncodeDepthNormal (i.nz.w, i.nz.xyz);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TreeBark" }
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityBuiltin3xTreeLibrary.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_full v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TreeVertBark(v);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
fixed4 frag( v2f i ) : SV_Target {
return EncodeDepthNormal (i.nz.w, i.nz.xyz);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TreeLeaf" }
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityBuiltin3xTreeLibrary.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_full v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TreeVertLeaf(v);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
fixed4 frag( v2f i ) : SV_Target {
half alpha = tex2D(_MainTex, i.uv).a;
clip (alpha - _Cutoff);
return EncodeDepthNormal (i.nz.w, i.nz.xyz);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TreeOpaque" "DisableBatching"="True" }
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
float4 nz : TEXCOORD0;
UNITY_VERTEX_OUTPUT_STEREO
};
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
fixed4 color : COLOR;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
v2f vert( appdata v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TerrainAnimateTree(v.vertex, v.color.w);
o.pos = UnityObjectToClipPos(v.vertex);
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
fixed4 frag(v2f i) : SV_Target {
return EncodeDepthNormal (i.nz.w, i.nz.xyz);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TreeTransparentCutout" "DisableBatching"="True" }
Pass {
Cull Back
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
fixed4 color : COLOR;
float4 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
v2f vert( appdata v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TerrainAnimateTree(v.vertex, v.color.w);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
half alpha = tex2D(_MainTex, i.uv).a;
clip (alpha - _Cutoff);
return EncodeDepthNormal (i.nz.w, i.nz.xyz);
}
ENDCG
}
Pass {
Cull Front
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
fixed4 color : COLOR;
float4 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
v2f vert( appdata v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TerrainAnimateTree(v.vertex, v.color.w);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.nz.xyz = -COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = tex2D( _MainTex, i.uv );
clip( texcol.a - _Cutoff );
return EncodeDepthNormal (i.nz.w, i.nz.xyz);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TreeBillboard" }
Pass {
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert (appdata_tree_billboard v) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.x = v.texcoord.x;
o.uv.y = v.texcoord.y > 0;
o.nz.xyz = float3(0,0,1);
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = tex2D( _MainTex, i.uv );
clip( texcol.a - 0.001 );
return EncodeDepthNormal (i.nz.w, i.nz.xyz);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="GrassBillboard" }
Pass {
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
fixed4 color : COLOR;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert (appdata_full v) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
WavingGrassBillboardVert (v);
o.color = v.color;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = tex2D( _MainTex, i.uv );
fixed alpha = texcol.a * i.color.a;
clip( alpha - _Cutoff );
return EncodeDepthNormal (i.nz.w, i.nz.xyz);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="Grass" }
Pass {
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
fixed4 color : COLOR;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert (appdata_full v) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
WavingGrassVert (v);
o.color = v.color;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = tex2D( _MainTex, i.uv );
fixed alpha = texcol.a * i.color.a;
clip( alpha - _Cutoff );
return EncodeDepthNormal (i.nz.w, i.nz.xyz);
}
ENDCG
}
}
Fallback Off
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4794ea6b2d07cc546ba97a809b5f9ada
timeCreated: 1494092583
licenseType: Free
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,518 @@
Shader "Hidden/Sprite-CameraDepthTexture" {
// Use this shader to render a Depth texture for a camera with soft edged Sprites (using camera.RenderWithShader with replacement tag "RenderType")
// Note the depth is encoded into the pixels RGB not the full RGBA (alpha is needed for blending)
Properties {
_MainTex ("", 2D) = "white" {}
_Cutoff ("", Float) = 0.5
_Color ("", Color) = (1,1,1,1)
}
SubShader {
Tags { "RenderType"="Sprite" }
Pass {
Cull Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "CGIncludes/ShaderShared.cginc"
#include "CGIncludes/ShaderMaths.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float depth : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_base v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = calculateTextureCoord(v.texcoord);
o.depth = COMPUTE_DEPTH_01;
return o;
}
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = calculateTexturePixel(i.uv );
float alpha = texcol.a*_Color.a;
clip( alpha - _Cutoff );
return fixed4(EncodeFloatRGB (i.depth), alpha);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="SpriteViewSpaceFixedNormal" }
Pass {
Cull Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "CGIncludes/ShaderShared.cginc"
#include "CGIncludes/ShaderMaths.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float depth : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_base v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = calculateTextureCoord(v.texcoord);
o.depth = COMPUTE_DEPTH_01;
return o;
}
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = calculateTexturePixel(i.uv );
float alpha = texcol.a*_Color.a;
clip( alpha - _Cutoff );
return fixed4(EncodeFloatRGB (i.depth), alpha);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="SpriteModelSpaceFixedNormal" }
Pass {
Cull Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "CGIncludes/ShaderShared.cginc"
#include "CGIncludes/ShaderMaths.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float depth : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_base v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = calculateTextureCoord(v.texcoord);
o.depth = COMPUTE_DEPTH_01;
return o;
}
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = calculateTexturePixel(i.uv );
float alpha = texcol.a*_Color.a;
clip( alpha - _Cutoff );
return fixed4(EncodeFloatRGB (i.depth), alpha);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="Opaque" }
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderMaths.cginc"
struct v2f {
float4 pos : SV_POSITION;
float depth : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_base v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.depth = COMPUTE_DEPTH_01;
return o;
}
fixed4 frag(v2f i) : SV_Target {
return fixed4(EncodeFloatRGB (i.depth), 1);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TransparentCutout" }
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderMaths.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float depth : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
uniform float4 _MainTex_ST;
v2f vert( appdata_base v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.depth = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
uniform fixed4 _Color;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = tex2D( _MainTex, i.uv );
clip( texcol.a*_Color.a - _Cutoff );
return fixed4(EncodeFloatRGB (i.depth), 1);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TreeBark" }
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderMaths.cginc"
#include "Lighting.cginc"
#include "UnityBuiltin3xTreeLibrary.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float depth : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_full v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TreeVertBark(v);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.depth = COMPUTE_DEPTH_01;
return o;
}
fixed4 frag( v2f i ) : SV_Target {
return fixed4(EncodeFloatRGB (i.depth), 1);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TreeLeaf" }
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderMaths.cginc"
#include "Lighting.cginc"
#include "UnityBuiltin3xTreeLibrary.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float depth : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_full v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TreeVertLeaf(v);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.depth = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
fixed4 frag( v2f i ) : SV_Target {
half alpha = tex2D(_MainTex, i.uv).a;
clip (alpha - _Cutoff);
return fixed4(EncodeFloatRGB (i.depth), 1);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TreeOpaque" "DisableBatching"="True" }
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderMaths.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
float depth : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
fixed4 color : COLOR;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
v2f vert( appdata v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TerrainAnimateTree(v.vertex, v.color.w);
o.pos = UnityObjectToClipPos(v.vertex);
o.depth = COMPUTE_DEPTH_01;
return o;
}
fixed4 frag(v2f i) : SV_Target {
return fixed4(EncodeFloatRGB (i.depth), 1);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TreeTransparentCutout" "DisableBatching"="True" }
Pass {
Cull Back
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderMaths.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float depth : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
fixed4 color : COLOR;
float4 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
v2f vert( appdata v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TerrainAnimateTree(v.vertex, v.color.w);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.depth = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
half alpha = tex2D(_MainTex, i.uv).a;
clip (alpha - _Cutoff);
return fixed4(EncodeFloatRGB (i.depth), 1);
}
ENDCG
}
Pass {
Cull Front
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderMaths.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float depth : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
fixed4 color : COLOR;
float4 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
v2f vert( appdata v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TerrainAnimateTree(v.vertex, v.color.w);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.depth = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = tex2D( _MainTex, i.uv );
clip( texcol.a - _Cutoff );
return fixed4(EncodeFloatRGB (i.depth), 1);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TreeBillboard" }
Pass {
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderMaths.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float depth : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert (appdata_tree_billboard v) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.x = v.texcoord.x;
o.uv.y = v.texcoord.y > 0;
o.depth = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = tex2D( _MainTex, i.uv );
clip( texcol.a - 0.001 );
return fixed4(EncodeFloatRGB (i.depth), 1);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="GrassBillboard" }
Pass {
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderMaths.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
fixed4 color : COLOR;
float2 uv : TEXCOORD0;
float depth : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert (appdata_full v) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
WavingGrassBillboardVert (v);
o.color = v.color;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.depth = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = tex2D( _MainTex, i.uv );
fixed alpha = texcol.a * i.color.a;
clip( alpha - _Cutoff );
return fixed4(EncodeFloatRGB (i.depth), 1);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="Grass" }
Pass {
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderMaths.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
fixed4 color : COLOR;
float2 uv : TEXCOORD0;
float depth : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert (appdata_full v) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
WavingGrassVert (v);
o.color = v.color;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
o.depth = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = tex2D( _MainTex, i.uv );
fixed alpha = texcol.a * i.color.a;
clip( alpha - _Cutoff );
return fixed4(EncodeFloatRGB (i.depth), 1);
}
ENDCG
}
}
Fallback Off
}

View File

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

View File

@@ -0,0 +1,522 @@
Shader "Hidden/Sprite-CameraNormalsTexture" {
// Use this shader to render a Normals texture for a camera with correct sprite normals (using camera.RenderWithShader with replacement tag "RenderType")
Properties {
_MainTex ("", 2D) = "white" {}
_Cutoff ("", Float) = 0.5
_Color ("", Color) = (1,1,1,1)
}
SubShader {
Tags { "RenderType"="Sprite" }
Pass {
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderShared.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
uniform float4 _FixedNormal;
v2f vert( appdata_base v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = calculateTextureCoord(v.texcoord);
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = calculateTexturePixel(i.uv );
float alpha = texcol.a*_Color.a;
clip( alpha - _Cutoff );
return i.nz;
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="SpriteViewSpaceFixedNormal" }
Pass {
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderShared.cginc"
#include "CGIncludes/SpriteLighting.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_base v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = calculateTextureCoord(v.texcoord);
o.nz.xyz = getFixedNormal();
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = calculateTexturePixel(i.uv );
float alpha = texcol.a*_Color.a;
clip( alpha - _Cutoff );
return i.nz;
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="SpriteModelSpaceFixedNormal" }
Pass {
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "CGIncludes/ShaderShared.cginc"
#include "CGIncludes/SpriteLighting.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_base v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = calculateTextureCoord(v.texcoord);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex);
float3 normal = getFixedNormal();
//Only do this if backface is enabled :/
normal *= calculateBackfacingSign(worldPos.xyz);
//
o.nz.xyz = normalize(mul((float3x3)UNITY_MATRIX_IT_MV, normal));
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = calculateTexturePixel(i.uv );
float alpha = texcol.a*_Color.a;
clip( alpha - _Cutoff );
return i.nz;
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="Opaque" }
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float4 nz : TEXCOORD0;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_base v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
fixed4 frag(v2f i) : SV_Target {
return i.nz;
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TransparentCutout" }
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
uniform float4 _MainTex_ST;
v2f vert( appdata_base v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
uniform fixed4 _Color;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = tex2D( _MainTex, i.uv );
clip( texcol.a*_Color.a - _Cutoff );
return i.nz;
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TreeBark" }
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityBuiltin3xTreeLibrary.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_full v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TreeVertBark(v);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
fixed4 frag( v2f i ) : SV_Target {
return i.nz;
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TreeLeaf" }
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityBuiltin3xTreeLibrary.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert( appdata_full v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TreeVertLeaf(v);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
fixed4 frag( v2f i ) : SV_Target {
half alpha = tex2D(_MainTex, i.uv).a;
clip (alpha - _Cutoff);
return i.nz;
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TreeOpaque" "DisableBatching"="True" }
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
float4 nz : TEXCOORD0;
UNITY_VERTEX_OUTPUT_STEREO
};
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
fixed4 color : COLOR;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
v2f vert( appdata v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TerrainAnimateTree(v.vertex, v.color.w);
o.pos = UnityObjectToClipPos(v.vertex);
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
fixed4 frag(v2f i) : SV_Target {
return i.nz;
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TreeTransparentCutout" "DisableBatching"="True" }
Pass {
Cull Back
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
fixed4 color : COLOR;
float4 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
v2f vert( appdata v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TerrainAnimateTree(v.vertex, v.color.w);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
half alpha = tex2D(_MainTex, i.uv).a;
clip (alpha - _Cutoff);
return i.nz;
}
ENDCG
}
Pass {
Cull Front
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
fixed4 color : COLOR;
float4 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
v2f vert( appdata v ) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TerrainAnimateTree(v.vertex, v.color.w);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.nz.xyz = -COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = tex2D( _MainTex, i.uv );
clip( texcol.a - _Cutoff );
return i.nz;
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="TreeBillboard" }
Pass {
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert (appdata_tree_billboard v) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.x = v.texcoord.x;
o.uv.y = v.texcoord.y > 0;
o.nz.xyz = float3(0,0,1);
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = tex2D( _MainTex, i.uv );
clip( texcol.a - 0.001 );
return i.nz;
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="GrassBillboard" }
Pass {
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
fixed4 color : COLOR;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert (appdata_full v) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
WavingGrassBillboardVert (v);
o.color = v.color;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = tex2D( _MainTex, i.uv );
fixed alpha = texcol.a * i.color.a;
clip( alpha - _Cutoff );
return i.nz;
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="Grass" }
Pass {
Cull Off
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "TerrainEngine.cginc"
struct v2f {
float4 pos : SV_POSITION;
fixed4 color : COLOR;
float2 uv : TEXCOORD0;
float4 nz : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert (appdata_full v) {
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
WavingGrassVert (v);
o.color = v.color;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
o.nz.xyz = COMPUTE_VIEW_NORMAL;
o.nz.w = COMPUTE_DEPTH_01;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
fixed4 frag(v2f i) : SV_Target {
fixed4 texcol = tex2D( _MainTex, i.uv );
fixed alpha = texcol.a * i.color.a;
clip( alpha - _Cutoff );
return i.nz;
}
ENDCG
}
}
Fallback Off
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 537141eca02c6df4bb8b4f77567e9de2
timeCreated: 1494092584
licenseType: Free
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,46 @@
Contributed by ToddRivers
# Unity Sprite Shaders
An Uber Shader specialised for rendering Sprites in Unity.
Even though it's designed for Sprites it can be used for a whole range of uses. It supports a wide range of optional shader features that won't effect performance unless they are used.
It also supports per-pixel effects such as normal maps and diffuse ramping whilst using Vertex Lit rendering.
### Lighting
The shaders support lighting using both Forward Rendering and Vertex Lit Rendering.
Forward rendering is more accurate but is slower and crucially means the sprite has to write to depth using alpha clipping to avoid overdraw.
Vertex lit means all lighting can be done in one pass meaning full alpha can be used.
### Normal Mapping
Normals maps are supported in both lighting modes (in Vertex Lit rendering data for normal mapping is packed into texture channels and then processed per pixel).
### Blend Modes
Easily switch between blend modes including pre-multiplied alpha, additive, multiply etc.
### Rim Lighting
Camera-space rim lighting is supported in both lighting modes.
### Diffuse Ramp
A ramp texture is optionally supported for toon shading effects.
### Shadows
Shadows are supported using alpha clipping.
### Gradient based Ambient lighting
Both lighting modes support using a gradient for ambient light. In Vertex Lit mode the Spherical Harmonics is approximated from the ground, equator and sky colors.
### Emission Map
An optional emission map is supported.
### Camera Space Normals
As sprites are 2d their normals will always be constant. The shaders allow you to define a fixed normal in camera space rather than pass through mesh normals.
This not only saves vertex throughput but means lighting looks less 'flat' for rendering sprites with a perspective camera.
### Color Adjustment
The shaders allow optional adjustment of hue / saturation and brightness as well as applying a solid color overlay effect for flashing a sprite to a solid color (eg. for damage effects).
### Fog
Fog is optionally supported
## To Use
On your object's material click the drop down for shader and select Spine\Sprite\Pixel Lit, Vertex Lit or Unlit.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cecd0ea162097a94c89a97af6baf0a66
timeCreated: 1479457854
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More