diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1182315d2..c231a8c96 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -376,6 +376,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`.
diff --git a/spine-csharp/src/AnimationState.cs b/spine-csharp/src/AnimationState.cs
index b093169f3..370cf9621 100644
--- a/spine-csharp/src/AnimationState.cs
+++ b/spine-csharp/src/AnimationState.cs
@@ -306,6 +306,43 @@ namespace Spine {
return applied;
}
+ /// Version of only applying EventTimelines for lightweight off-screen updates.
+ // 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);
@@ -398,6 +435,43 @@ namespace Spine {
return mix;
}
+ /// Version of only applying EventTimelines for lightweight off-screen updates.
+ // 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;
+ }
+
/// Applies the attachment timeline and sets .
/// 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
diff --git a/spine-csharp/src/Atlas.cs b/spine-csharp/src/Atlas.cs
index 3ef27c5d1..aeaf1050a 100644
--- a/spine-csharp/src/Atlas.cs
+++ b/spine-csharp/src/Atlas.cs
@@ -58,6 +58,9 @@ namespace Spine {
}
#endregion
+ public List Regions { get { return regions; } }
+ public List Pages { get { return pages; } }
+
#if !(IS_UNITY)
#if WINDOWS_STOREAPP
private async Task ReadFile(string path, TextureLoader textureLoader) {
diff --git a/spine-ts/build/spine-all.d.ts b/spine-ts/build/spine-all.d.ts
index f4e4bb9ec..6b2728474 100644
--- a/spine-ts/build/spine-all.d.ts
+++ b/spine-ts/build/spine-all.d.ts
@@ -1868,7 +1868,8 @@ declare module spine.webgl {
canvas: HTMLCanvasElement | OffscreenCanvas;
gl: WebGLRenderingContext;
private restorables;
- constructor(canvasOrContext: HTMLCanvasElement | WebGLRenderingContext | OffscreenCanvas | WebGL2RenderingContext, contextConfig?: any);
+ constructor(canvasOrContext: HTMLCanvasElement | WebGLRenderingContext | EventTarget | WebGL2RenderingContext, contextConfig?: any);
+ private setupCanvas;
addRestorable(restorable: Restorable): void;
removeRestorable(restorable: Restorable): void;
}
diff --git a/spine-ts/build/spine-all.js b/spine-ts/build/spine-all.js
index ef06f58b5..368cbde25 100644
--- a/spine-ts/build/spine-all.js
+++ b/spine-ts/build/spine-all.js
@@ -12192,30 +12192,32 @@ var spine;
(function (webgl) {
var ManagedWebGLRenderingContext = (function () {
function ManagedWebGLRenderingContext(canvasOrContext, contextConfig) {
- var _this = this;
if (contextConfig === void 0) { contextConfig = { alpha: "true" }; }
this.restorables = new Array();
- if (!((canvasOrContext instanceof WebGLRenderingContext) || (canvasOrContext instanceof WebGL2RenderingContext))) {
- var canvas_1 = canvasOrContext;
- this.gl = (canvas_1.getContext("webgl2", contextConfig) || canvas_1.getContext("webgl", contextConfig));
- this.canvas = canvas_1;
- canvas_1.addEventListener("webglcontextlost", function (e) {
- var event = e;
- if (e) {
- e.preventDefault();
- }
- });
- canvas_1.addEventListener("webglcontextrestored", function (e) {
- for (var i = 0, n = _this.restorables.length; i < n; i++) {
- _this.restorables[i].restore();
- }
- });
+ if (canvasOrContext instanceof HTMLCanvasElement || canvasOrContext instanceof EventTarget) {
+ this.setupCanvas(canvasOrContext, contextConfig);
}
else {
this.gl = canvasOrContext;
this.canvas = this.gl.canvas;
}
}
+ ManagedWebGLRenderingContext.prototype.setupCanvas = function (canvas, contextConfig) {
+ var _this = this;
+ this.gl = (canvas.getContext("webgl2", contextConfig) || canvas.getContext("webgl", contextConfig));
+ this.canvas = canvas;
+ canvas.addEventListener("webglcontextlost", function (e) {
+ var event = e;
+ if (e) {
+ e.preventDefault();
+ }
+ });
+ canvas.addEventListener("webglcontextrestored", function (e) {
+ for (var i = 0, n = _this.restorables.length; i < n; i++) {
+ _this.restorables[i].restore();
+ }
+ });
+ };
ManagedWebGLRenderingContext.prototype.addRestorable = function (restorable) {
this.restorables.push(restorable);
};
diff --git a/spine-ts/build/spine-player.d.ts b/spine-ts/build/spine-player.d.ts
index 4c85ca1ce..624d68c1c 100644
--- a/spine-ts/build/spine-player.d.ts
+++ b/spine-ts/build/spine-player.d.ts
@@ -1837,7 +1837,8 @@ declare module spine.webgl {
canvas: HTMLCanvasElement | OffscreenCanvas;
gl: WebGLRenderingContext;
private restorables;
- constructor(canvasOrContext: HTMLCanvasElement | WebGLRenderingContext | OffscreenCanvas | WebGL2RenderingContext, contextConfig?: any);
+ constructor(canvasOrContext: HTMLCanvasElement | WebGLRenderingContext | EventTarget | WebGL2RenderingContext, contextConfig?: any);
+ private setupCanvas;
addRestorable(restorable: Restorable): void;
removeRestorable(restorable: Restorable): void;
}
diff --git a/spine-ts/build/spine-player.js b/spine-ts/build/spine-player.js
index 0aee5c815..c9b3eeacd 100644
--- a/spine-ts/build/spine-player.js
+++ b/spine-ts/build/spine-player.js
@@ -11924,30 +11924,32 @@ var spine;
(function (webgl) {
var ManagedWebGLRenderingContext = (function () {
function ManagedWebGLRenderingContext(canvasOrContext, contextConfig) {
- var _this = this;
if (contextConfig === void 0) { contextConfig = { alpha: "true" }; }
this.restorables = new Array();
- if (!((canvasOrContext instanceof WebGLRenderingContext) || (canvasOrContext instanceof WebGL2RenderingContext))) {
- var canvas = canvasOrContext;
- this.gl = (canvas.getContext("webgl2", contextConfig) || canvas.getContext("webgl", contextConfig));
- this.canvas = canvas;
- canvas.addEventListener("webglcontextlost", function (e) {
- var event = e;
- if (e) {
- e.preventDefault();
- }
- });
- canvas.addEventListener("webglcontextrestored", function (e) {
- for (var i = 0, n = _this.restorables.length; i < n; i++) {
- _this.restorables[i].restore();
- }
- });
+ if (canvasOrContext instanceof HTMLCanvasElement || canvasOrContext instanceof EventTarget) {
+ this.setupCanvas(canvasOrContext, contextConfig);
}
else {
this.gl = canvasOrContext;
this.canvas = this.gl.canvas;
}
}
+ ManagedWebGLRenderingContext.prototype.setupCanvas = function (canvas, contextConfig) {
+ var _this = this;
+ this.gl = (canvas.getContext("webgl2", contextConfig) || canvas.getContext("webgl", contextConfig));
+ this.canvas = canvas;
+ canvas.addEventListener("webglcontextlost", function (e) {
+ var event = e;
+ if (e) {
+ e.preventDefault();
+ }
+ });
+ canvas.addEventListener("webglcontextrestored", function (e) {
+ for (var i = 0, n = _this.restorables.length; i < n; i++) {
+ _this.restorables[i].restore();
+ }
+ });
+ };
ManagedWebGLRenderingContext.prototype.addRestorable = function (restorable) {
this.restorables.push(restorable);
};
diff --git a/spine-ts/build/spine-webgl.d.ts b/spine-ts/build/spine-webgl.d.ts
index 03789e0a1..75c17dff2 100644
--- a/spine-ts/build/spine-webgl.d.ts
+++ b/spine-ts/build/spine-webgl.d.ts
@@ -1837,7 +1837,8 @@ declare module spine.webgl {
canvas: HTMLCanvasElement | OffscreenCanvas;
gl: WebGLRenderingContext;
private restorables;
- constructor(canvasOrContext: HTMLCanvasElement | WebGLRenderingContext | OffscreenCanvas | WebGL2RenderingContext, contextConfig?: any);
+ constructor(canvasOrContext: HTMLCanvasElement | WebGLRenderingContext | EventTarget | WebGL2RenderingContext, contextConfig?: any);
+ private setupCanvas;
addRestorable(restorable: Restorable): void;
removeRestorable(restorable: Restorable): void;
}
diff --git a/spine-ts/build/spine-webgl.js b/spine-ts/build/spine-webgl.js
index 48a38af70..92093a4c3 100644
--- a/spine-ts/build/spine-webgl.js
+++ b/spine-ts/build/spine-webgl.js
@@ -11924,30 +11924,32 @@ var spine;
(function (webgl) {
var ManagedWebGLRenderingContext = (function () {
function ManagedWebGLRenderingContext(canvasOrContext, contextConfig) {
- var _this = this;
if (contextConfig === void 0) { contextConfig = { alpha: "true" }; }
this.restorables = new Array();
- if (!((canvasOrContext instanceof WebGLRenderingContext) || (canvasOrContext instanceof WebGL2RenderingContext))) {
- var canvas = canvasOrContext;
- this.gl = (canvas.getContext("webgl2", contextConfig) || canvas.getContext("webgl", contextConfig));
- this.canvas = canvas;
- canvas.addEventListener("webglcontextlost", function (e) {
- var event = e;
- if (e) {
- e.preventDefault();
- }
- });
- canvas.addEventListener("webglcontextrestored", function (e) {
- for (var i = 0, n = _this.restorables.length; i < n; i++) {
- _this.restorables[i].restore();
- }
- });
+ if (canvasOrContext instanceof HTMLCanvasElement || canvasOrContext instanceof EventTarget) {
+ this.setupCanvas(canvasOrContext, contextConfig);
}
else {
this.gl = canvasOrContext;
this.canvas = this.gl.canvas;
}
}
+ ManagedWebGLRenderingContext.prototype.setupCanvas = function (canvas, contextConfig) {
+ var _this = this;
+ this.gl = (canvas.getContext("webgl2", contextConfig) || canvas.getContext("webgl", contextConfig));
+ this.canvas = canvas;
+ canvas.addEventListener("webglcontextlost", function (e) {
+ var event = e;
+ if (e) {
+ e.preventDefault();
+ }
+ });
+ canvas.addEventListener("webglcontextrestored", function (e) {
+ for (var i = 0, n = _this.restorables.length; i < n; i++) {
+ _this.restorables[i].restore();
+ }
+ });
+ };
ManagedWebGLRenderingContext.prototype.addRestorable = function (restorable) {
this.restorables.push(restorable);
};
diff --git a/spine-ts/webgl/src/WebGL.ts b/spine-ts/webgl/src/WebGL.ts
index b62507ab9..3632fd205 100644
--- a/spine-ts/webgl/src/WebGL.ts
+++ b/spine-ts/webgl/src/WebGL.ts
@@ -33,29 +33,32 @@ module spine.webgl {
public gl: WebGLRenderingContext;
private restorables = new Array();
- constructor(canvasOrContext: HTMLCanvasElement | WebGLRenderingContext | OffscreenCanvas | WebGL2RenderingContext, contextConfig: any = { alpha: "true" }) {
- if (!((canvasOrContext instanceof WebGLRenderingContext) || (canvasOrContext instanceof WebGL2RenderingContext))) {
- let canvas = canvasOrContext;
- this.gl = (canvas.getContext("webgl2", contextConfig) || canvas.getContext("webgl", contextConfig));
- this.canvas = canvas;
- canvas.addEventListener("webglcontextlost", (e: any) => {
- let event = e;
- if (e) {
- e.preventDefault();
- }
- });
-
- canvas.addEventListener("webglcontextrestored", (e: any) => {
- for (let i = 0, n = this.restorables.length; i < n; i++) {
- this.restorables[i].restore();
- }
- });
+ constructor(canvasOrContext: HTMLCanvasElement | WebGLRenderingContext | EventTarget | WebGL2RenderingContext, contextConfig: any = { alpha: "true" }) {
+ if (canvasOrContext instanceof HTMLCanvasElement || canvasOrContext instanceof EventTarget) {
+ this.setupCanvas(canvasOrContext, contextConfig);
} else {
this.gl = canvasOrContext;
this.canvas = this.gl.canvas;
}
}
+ private setupCanvas(canvas: any, contextConfig: any) {
+ this.gl = (canvas.getContext("webgl2", contextConfig) || canvas.getContext("webgl", contextConfig));
+ this.canvas = canvas;
+ canvas.addEventListener("webglcontextlost", (e: any) => {
+ let event = e;
+ if (e) {
+ e.preventDefault();
+ }
+ });
+
+ canvas.addEventListener("webglcontextrestored", (e: any) => {
+ for (let i = 0, n = this.restorables.length; i < n; i++) {
+ this.restorables[i].restore();
+ }
+ });
+ }
+
addRestorable(restorable: Restorable) {
this.restorables.push(restorable);
}
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs
index 514eaa341..cfc5ddf29 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs
@@ -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);
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs
index 429266bf1..457ba3124 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs
@@ -62,7 +62,7 @@ namespace Spine.Unity {
/// Update mode to optionally limit updates to e.g. only apply animations but not update the mesh.
public UpdateMode UpdateMode { get { return updateMode; } set { updateMode = value; } }
- [SerializeField] protected UpdateMode updateMode = UpdateMode.FullUpdate;
+ protected UpdateMode updateMode = UpdateMode.FullUpdate;
/// Update mode used when the MeshRenderer becomes invisible
/// (when OnBecameInvisible() 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();
}
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs
index 519bf334f..2fea2f7d4 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs
@@ -82,7 +82,7 @@ namespace Spine.Unity {
/// Update mode to optionally limit updates to e.g. only apply animations but not update the mesh.
public UpdateMode UpdateMode { get { return updateMode; } set { updateMode = value; } }
- [SerializeField] protected UpdateMode updateMode = UpdateMode.FullUpdate;
+ protected UpdateMode updateMode = UpdateMode.FullUpdate;
/// Update mode used when the MeshRenderer becomes invisible
/// (when OnBecameInvisible() 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;
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/ISkeletonAnimation.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/ISkeletonAnimation.cs
index 36e300ec9..90486e381 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/ISkeletonAnimation.cs
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/ISkeletonAnimation.cs
@@ -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);
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/MaterialChecks.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/MaterialChecks.cs
index 0cac07a37..d838a75c1 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/MaterialChecks.cs
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/MaterialChecks.cs
@@ -79,6 +79,12 @@ namespace Spine.Unity {
+ "This will lead to incorrect rendering on some devices.\n\n"
+ "Please change the assigned Material to e.g. 'SkeletonGraphicDefault' or change the used shader to one of the 'Spine/SkeletonGraphic *' shaders.\n\n"
+ "Note that 'Spine/SkeletonGraphic *' shall still be used when using URP.\n";
+ public static readonly string kNoSkeletonGraphicTintBlackMaterialMessage =
+ "\nWarning: Only enable 'Canvas Group Tint Black' when using a 'SkeletonGraphic Tint Black' shader!\n"
+ + "This will lead to incorrect rendering.\n\nPlease\n"
+ + "a) disable 'Canvas Group Tint Black' under 'Advanced' or\n"
+ + "b) use a 'SkeletonGraphic Tint Black' Material if you need Tint Black on a CanvasGroup.\n";
+
public static readonly string kTintBlackMessage =
"\nWarning: 'Advanced - Tint Black' required when using any 'Tint Black' shader!\n\nPlease\n"
+ "a) enable 'Tint Black' at the SkeletonRenderer/SkeletonGraphic component under 'Advanced' or\n"
@@ -143,6 +149,10 @@ namespace Spine.Unity {
isProblematic = true;
errorMessage += kCanvasTintBlackMessage;
}
+ if (settings.canvasGroupTintBlack == true && !IsSkeletonGraphicTintBlackMaterial(material)) {
+ isProblematic = true;
+ errorMessage += kNoSkeletonGraphicTintBlackMaterialMessage;
+ }
if (settings.canvasGroupTintBlack == true && !IsCanvasGroupCompatible(material)) {
isProblematic = true;
errorMessage += kCanvasGroupCompatibleMessage;
@@ -261,6 +271,11 @@ namespace Spine.Unity {
return material.shader.name.Contains("Spine") && !material.shader.name.Contains("SkeletonGraphic");
}
+ static bool IsSkeletonGraphicTintBlackMaterial (Material material) {
+ return material.shader.name.Contains("Spine") && material.shader.name.Contains("SkeletonGraphic")
+ && material.shader.name.Contains("Black");
+ }
+
static bool AreShadowsDisabled (Material material) {
return material.IsKeywordEnabled("_RECEIVE_SHADOWS_OFF");
}
diff --git a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Sprite-ForwardPass-URP.hlsl b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Sprite-ForwardPass-URP.hlsl
index d4f7ccd7a..452389590 100644
--- a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Sprite-ForwardPass-URP.hlsl
+++ b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Sprite-ForwardPass-URP.hlsl
@@ -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;
diff --git a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Spine-Sprite-URP.shader b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Spine-Sprite-URP.shader
index 41c3f7aeb..f40e5098b 100644
--- a/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Spine-Sprite-URP.shader
+++ b/spine-unity/Modules/com.esotericsoftware.spine.urp-shaders/Shaders/Spine-Sprite-URP.shader
@@ -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