This commit is contained in:
badlogic 2020-11-27 18:45:27 +01:00
commit 7bfcbe1b7a
9 changed files with 119 additions and 18 deletions

View File

@ -247,6 +247,7 @@
* Added `BoundingBoxFollowerGraphic` component. This class is a counterpart of `BoundingBoxFollower` that can be used with `SkeletonGraphic`.
* Added Inspector context menu functions `SkeletonRenderer - Add all BoundingBoxFollower GameObjects` and `SkeletonGraphic - Add all BoundingBoxFollowerGraphic GameObjects` that automatically generate bounding box follower GameObjects for every `BoundingBoxAttachment` for all skins of a skeleton.
* `GetRemappedClone()` now provides an additional parameter `pivotShiftsMeshUVCoords` for `MeshAttachment` to prevent uv shifts at a non-central Sprite pivot. This parameter defaults to `true` to maintain previous behaviour.
* `SkeletonRenderer` components now provide an additional update mode `Only Event Timelines` at the `Update When Invisible` property. This mode saves additional timeline updates compared to update mode `Everything Except Mesh`.
* **Changes of default values**
* `SkeletonMecanim`'s `Layer Mix Mode` now defaults to `MixMode.MixNext` instead of `MixMode.MixAlways`.

View File

@ -300,6 +300,43 @@ namespace Spine {
return applied;
}
/// <summary>Version of <see cref="Apply"/> only applying EventTimelines for lightweight off-screen updates.</summary>
// Note: This method is not part of the libgdx reference implementation.
public bool ApplyEventTimelinesOnly (Skeleton skeleton) {
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
var events = this.events;
bool applied = false;
var tracksItems = tracks.Items;
for (int i = 0, n = tracks.Count; i < n; i++) {
TrackEntry current = tracksItems[i];
if (current == null || current.delay > 0) continue;
applied = true;
// Apply mixing from entries first.
if (current.mixingFrom != null)
ApplyMixingFromEventTimelinesOnly(current, skeleton);
// Apply current entry.
float animationLast = current.animationLast, animationTime = current.AnimationTime;
int timelineCount = current.animation.timelines.Count;
var timelines = current.animation.timelines;
var timelinesItems = timelines.Items;
for (int ii = 0; ii < timelineCount; ii++) {
Timeline timeline = timelinesItems[ii];
if (timeline is EventTimeline)
timeline.Apply(skeleton, animationLast, animationTime, events, 1.0f, MixBlend.Setup, MixDirection.In);
}
QueueEvents(current, animationTime);
events.Clear(false);
current.nextAnimationLast = animationTime;
current.nextTrackLast = current.trackTime;
}
queue.Drain();
return applied;
}
private float ApplyMixingFrom (TrackEntry to, Skeleton skeleton, MixBlend blend) {
TrackEntry from = to.mixingFrom;
if (from.mixingFrom != null) ApplyMixingFrom(from, skeleton, blend);
@ -386,6 +423,43 @@ namespace Spine {
return mix;
}
/// <summary>Version of <see cref="ApplyMixingFrom"/> only applying EventTimelines for lightweight off-screen updates.</summary>
// Note: This method is not part of the libgdx reference implementation.
private float ApplyMixingFromEventTimelinesOnly (TrackEntry to, Skeleton skeleton) {
TrackEntry from = to.mixingFrom;
if (from.mixingFrom != null) ApplyMixingFromEventTimelinesOnly(from, skeleton);
float mix;
if (to.mixDuration == 0) { // Single frame mix to undo mixingFrom changes.
mix = 1;
}
else {
mix = to.mixTime / to.mixDuration;
if (mix > 1) mix = 1;
}
var eventBuffer = mix < from.eventThreshold ? this.events : null;
if (eventBuffer == null)
return mix;
float animationLast = from.animationLast, animationTime = from.AnimationTime;
var timelines = from.animation.timelines;
int timelineCount = timelines.Count;
var timelinesItems = timelines.Items;
for (int i = 0; i < timelineCount; i++) {
var timeline = timelinesItems[i];
if (timeline is EventTimeline)
timeline.Apply(skeleton, animationLast, animationTime, eventBuffer, 0, MixBlend.Setup, MixDirection.Out);
}
if (to.mixDuration > 0) QueueEvents(from, animationTime);
this.events.Clear(false);
from.nextAnimationLast = animationTime;
from.nextTrackLast = from.trackTime;
return mix;
}
/// <summary> Applies the attachment timeline and sets <see cref="Slot.attachmentState"/>.</summary>
/// <param name="attachments">False when: 1) the attachment timeline is mixing out, 2) mix < attachmentThreshold, and 3) the timeline
/// is not the last timeline to set the slot's attachment. In that case the timeline is applied only so subsequent

View File

@ -58,6 +58,9 @@ namespace Spine {
}
#endregion
public List<AtlasRegion> Regions { get { return regions; } }
public List<AtlasPage> Pages { get { return pages; } }
#if !(IS_UNITY)
#if WINDOWS_STOREAPP
private async Task ReadFile(string path, TextureLoader textureLoader) {

View File

@ -213,7 +213,10 @@ namespace Spine.Unity {
if (_BeforeApply != null)
_BeforeApply(this);
state.Apply(skeleton);
if (updateMode != UpdateMode.OnlyEventTimelines)
state.Apply(skeleton);
else
state.ApplyEventTimelinesOnly(skeleton);
if (_UpdateLocal != null)
_UpdateLocal(this);

View File

@ -62,7 +62,7 @@ namespace Spine.Unity {
/// <summary>Update mode to optionally limit updates to e.g. only apply animations but not update the mesh.</summary>
public UpdateMode UpdateMode { get { return updateMode; } set { updateMode = value; } }
[SerializeField] protected UpdateMode updateMode = UpdateMode.FullUpdate;
protected UpdateMode updateMode = UpdateMode.FullUpdate;
/// <summary>Update mode used when the MeshRenderer becomes invisible
/// (when <c>OnBecameInvisible()</c> is called). Update mode is automatically
@ -263,7 +263,10 @@ namespace Spine.Unity {
if (BeforeApply != null)
BeforeApply(this);
state.Apply(skeleton);
if (updateMode != UpdateMode.OnlyEventTimelines)
state.Apply(skeleton);
else
state.ApplyEventTimelinesOnly(skeleton);
if (UpdateLocal != null)
UpdateLocal(this);
@ -283,7 +286,7 @@ namespace Spine.Unity {
// instantiation can happen from Update() after this component, leading to a missing Update() call.
if (!wasUpdatedAfterInit) Update(0);
if (freeze) return;
if (updateMode <= UpdateMode.EverythingExceptMesh) return;
if (updateMode != UpdateMode.FullUpdate) return;
UpdateMesh();
}

View File

@ -82,7 +82,7 @@ namespace Spine.Unity {
/// <summary>Update mode to optionally limit updates to e.g. only apply animations but not update the mesh.</summary>
public UpdateMode UpdateMode { get { return updateMode; } set { updateMode = value; } }
[SerializeField] protected UpdateMode updateMode = UpdateMode.FullUpdate;
protected UpdateMode updateMode = UpdateMode.FullUpdate;
/// <summary>Update mode used when the MeshRenderer becomes invisible
/// (when <c>OnBecameInvisible()</c> is called). Update mode is automatically
@ -381,7 +381,7 @@ namespace Spine.Unity {
}
#endif
if (updateMode <= UpdateMode.EverythingExceptMesh) return;
if (updateMode != UpdateMode.FullUpdate) return;
#if SPINE_OPTIONAL_RENDEROVERRIDE
bool doMeshOverride = generateMeshOverride != null;

View File

@ -31,8 +31,10 @@ namespace Spine.Unity {
public enum UpdateMode {
Nothing = 0,
OnlyAnimationStatus,
EverythingExceptMesh,
FullUpdate
OnlyEventTimelines = 4, // added as index 4 to keep scene behavior unchanged.
EverythingExceptMesh = 2,
FullUpdate,
//Reserved 4 for OnlyEventTimelines
};
public delegate void UpdateBonesDelegate (ISkeletonAnimation animated);

View File

@ -5,6 +5,10 @@
#include "SpineCoreShaders/SpriteLighting.cginc"
#if defined(_RIM_LIGHTING) || defined(_ADDITIONAL_LIGHTS) || defined(MAIN_LIGHT_CALCULATE_SHADOWS)
#define NEEDS_POSITION_WS
#endif
////////////////////////////////////////
// Vertex output struct
//
@ -26,10 +30,10 @@ struct VertexOutputLWRP
#else
half3 normalWorld : TEXCOORD4;
#endif
#if defined(_MAIN_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
#if (defined(_MAIN_LIGHT_SHADOWS) || defined(MAIN_LIGHT_CALCULATE_SHADOWS)) && !defined(_RECEIVE_SHADOWS_OFF)
float4 shadowCoord : TEXCOORD7;
#endif
#if defined(_RIM_LIGHTING) || defined(_ADDITIONAL_LIGHTS)
#if defined(NEEDS_POSITION_WS)
float4 positionWS : TEXCOORD8;
#endif
UNITY_VERTEX_OUTPUT_STEREO
@ -80,7 +84,7 @@ half4 LightweightFragmentPBRSimplified(InputData inputData, half4 texAlbedoAlpha
brdfData.specular *= albedo.a;
#ifndef _MAIN_LIGHT_VERTEX
#if defined(_MAIN_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
#if (defined(_MAIN_LIGHT_SHADOWS) || defined(MAIN_LIGHT_CALCULATE_SHADOWS)) && !defined(_RECEIVE_SHADOWS_OFF)
Light mainLight = GetMainLight(inputData.shadowCoord);
#else
Light mainLight = GetMainLight();
@ -115,7 +119,7 @@ half4 LightweightFragmentBlinnPhongSimplified(InputData inputData, half4 texDiff
half4 diffuse = texDiffuseAlpha * vertexColor;
#ifndef _MAIN_LIGHT_VERTEX
#if defined(_MAIN_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
#if (defined(_MAIN_LIGHT_SHADOWS) || defined(MAIN_LIGHT_CALCULATE_SHADOWS)) && !defined(_RECEIVE_SHADOWS_OFF)
Light mainLight = GetMainLight(inputData.shadowCoord);
#else
Light mainLight = GetMainLight();
@ -170,12 +174,12 @@ VertexOutputLWRP ForwardPassVertexSprite(VertexInput input)
backFaceSign = calculateBackfacingSign(positionWS.xyz);
#endif
output.viewDirectionWS = GetCameraPositionWS() - positionWS;
#if defined(NEEDS_POSITION_WS)
output.positionWS = float4(positionWS, 1);
#endif
#if defined(PER_PIXEL_LIGHTING)
#if defined(_RIM_LIGHTING) || defined(_ADDITIONAL_LIGHTS)
output.positionWS = float4(positionWS, 1);
#endif
half3 normalWS = calculateSpriteWorldNormal(input, -backFaceSign);
output.normalWorld.xyz = normalWS;
@ -191,7 +195,8 @@ VertexOutputLWRP ForwardPassVertexSprite(VertexInput input)
#endif // !PER_PIXEL_LIGHTING
output.fogFactorAndVertexLight.yzw = LightweightLightVertexSimplified(positionWS, normalWS);
#if defined(_MAIN_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
#if (defined(_MAIN_LIGHT_SHADOWS) || defined(MAIN_LIGHT_CALCULATE_SHADOWS)) && !defined(_RECEIVE_SHADOWS_OFF)
VertexPositionInputs vertexInput;
vertexInput.positionWS = positionWS;
vertexInput.positionCS = output.pos;
@ -216,8 +221,16 @@ half4 ForwardPassFragmentSprite(VertexOutputLWRP input) : SV_Target
// fill out InputData struct
InputData inputData;
#if defined(_MAIN_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
inputData.shadowCoord = input.shadowCoord;
#if !defined(_RECEIVE_SHADOWS_OFF)
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
inputData.shadowCoord = input.shadowCoord;
#elif defined(MAIN_LIGHT_CALCULATE_SHADOWS)
inputData.shadowCoord = TransformWorldToShadowCoord(input.positionWS);
#elif defined(_MAIN_LIGHT_SHADOWS)
inputData.shadowCoord = input.shadowCoord;
#else
inputData.shadowCoord = float4(0, 0, 0, 0);
#endif
#endif
inputData.viewDirectionWS = input.viewDirectionWS;

View File

@ -100,6 +100,8 @@ Shader "Universal Render Pipeline/Spine/Sprite"
// -------------------------------------
// Universal Pipeline keywords
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ MAIN_LIGHT_CALCULATE_SHADOWS
#pragma multi_compile _ REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS