From 52cc1d44d821e72aa7b94b4db30a63dbc9fdd561 Mon Sep 17 00:00:00 2001 From: Davide Tantillo Date: Thu, 4 Jul 2024 15:15:25 +0200 Subject: [PATCH 01/30] [ts][pixi] Performance improvements. --- spine-ts/spine-pixi/src/DarkSlotMesh.ts | 34 +++++++++++++---------- spine-ts/spine-pixi/src/SlotMesh.ts | 36 ++++++++++++++----------- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/spine-ts/spine-pixi/src/DarkSlotMesh.ts b/spine-ts/spine-pixi/src/DarkSlotMesh.ts index 1f896eeef..c49c5d7fc 100644 --- a/spine-ts/spine-pixi/src/DarkSlotMesh.ts +++ b/spine-ts/spine-pixi/src/DarkSlotMesh.ts @@ -54,26 +54,30 @@ export class DarkSlotMesh extends DarkTintMesh implements ISlotMesh { const vertLenght = (finalVerticesLength / (darkTint ? 12 : 8)) * 2; - if (this.geometry.getBuffer("aTextureCoord").data?.length !== vertLenght) { - this.geometry.getBuffer("aTextureCoord").data = new Float32Array(vertLenght); + const textureCoord = this.geometry.getBuffer("aTextureCoord"); + if (textureCoord.data?.length !== vertLenght) { + textureCoord.data = new Float32Array(vertLenght); } - if (this.geometry.getBuffer("aVertexPosition").data?.length !== vertLenght) { - this.geometry.getBuffer("aVertexPosition").data = new Float32Array(vertLenght); + const vertexCoord = this.geometry.getBuffer("aVertexPosition"); + if (vertexCoord.data?.length !== vertLenght) { + vertexCoord.data = new Float32Array(vertLenght); } let vertIndex = 0; + let textureCoordData = textureCoord.data; + let vertexCoordData = vertexCoord.data; for (let i = 0; i < finalVerticesLength; i += darkTint ? 12 : 8) { let auxi = i; - this.geometry.getBuffer("aVertexPosition").data[vertIndex] = finalVertices[auxi++]; - this.geometry.getBuffer("aVertexPosition").data[vertIndex + 1] = finalVertices[auxi++]; + vertexCoordData[vertIndex] = finalVertices[auxi++]; + vertexCoordData[vertIndex + 1] = finalVertices[auxi++]; auxi += 4; // color - this.geometry.getBuffer("aTextureCoord").data[vertIndex] = finalVertices[auxi++]; - this.geometry.getBuffer("aTextureCoord").data[vertIndex + 1] = finalVertices[auxi++]; + textureCoordData[vertIndex] = finalVertices[auxi++]; + textureCoordData[vertIndex + 1] = finalVertices[auxi++]; vertIndex += 2; } @@ -101,18 +105,20 @@ export class DarkSlotMesh extends DarkTintMesh implements ISlotMesh { this.blendMode = SpineTexture.toPixiBlending(slotBlendMode); this.alpha = DarkSlotMesh.auxColor[3]; - if (this.geometry.indexBuffer.data.length !== finalIndices.length) { - this.geometry.indexBuffer.data = new Uint32Array(finalIndices); + const indexBuffer = this.geometry.indexBuffer; + if (indexBuffer.data.length !== finalIndices.length) { + indexBuffer.data = new Uint32Array(finalIndices); } else { + const indexBufferData = indexBuffer.data; for (let i = 0; i < finalIndicesLength; i++) { - this.geometry.indexBuffer.data[i] = finalIndices[i]; + indexBufferData[i] = finalIndices[i]; } } this.name = slotName; - this.geometry.getBuffer("aVertexPosition").update(); - this.geometry.getBuffer("aTextureCoord").update(); - this.geometry.indexBuffer.update(); + textureCoord.update(); + vertexCoord.update(); + indexBuffer.update(); } } diff --git a/spine-ts/spine-pixi/src/SlotMesh.ts b/spine-ts/spine-pixi/src/SlotMesh.ts index f62563f45..59fcc11e4 100644 --- a/spine-ts/spine-pixi/src/SlotMesh.ts +++ b/spine-ts/spine-pixi/src/SlotMesh.ts @@ -62,32 +62,34 @@ export class SlotMesh extends Mesh implements ISlotMesh { const vertLenght = (finalVerticesLength / (darkTint ? 12 : 8)) * 2; - if (this.geometry.getBuffer("aTextureCoord").data?.length !== vertLenght) { - this.geometry.getBuffer("aTextureCoord").data = new Float32Array(vertLenght); + const textureCoord = this.geometry.getBuffer("aTextureCoord"); + if (textureCoord.data?.length !== vertLenght) { + textureCoord.data = new Float32Array(vertLenght); } - if (this.geometry.getBuffer("aVertexPosition").data?.length !== vertLenght) { - this.geometry.getBuffer("aVertexPosition").data = new Float32Array(vertLenght); + const vertexCoord = this.geometry.getBuffer("aVertexPosition"); + if (vertexCoord.data?.length !== vertLenght) { + vertexCoord.data = new Float32Array(vertLenght); } let vertIndex = 0; + let textureCoordData = textureCoord.data; + let vertexCoordData = vertexCoord.data; for (let i = 0; i < finalVerticesLength; i += darkTint ? 12 : 8) { let auxi = i; - this.geometry.getBuffer("aVertexPosition").data[vertIndex] = finalVertices[auxi++]; - this.geometry.getBuffer("aVertexPosition").data[vertIndex + 1] = finalVertices[auxi++]; + vertexCoordData[vertIndex] = finalVertices[auxi++]; + vertexCoordData[vertIndex + 1] = finalVertices[auxi++]; auxi += 4; // color - this.geometry.getBuffer("aTextureCoord").data[vertIndex] = finalVertices[auxi++]; - this.geometry.getBuffer("aTextureCoord").data[vertIndex + 1] = finalVertices[auxi++]; + textureCoordData[vertIndex] = finalVertices[auxi++]; + textureCoordData[vertIndex + 1] = finalVertices[auxi++]; vertIndex += 2; } - // console.log(vertLenght, auxVert.length); - if (darkTint && !this.warnedTwoTint) { console.warn("DarkTint is not enabled by default. To enable use a DarkSlotMesh factory while creating the Spine object."); this.warnedTwoTint = true; @@ -102,18 +104,20 @@ export class SlotMesh extends Mesh implements ISlotMesh { this.alpha = SlotMesh.auxColor[3]; this.blendMode = SpineTexture.toPixiBlending(slotBlendMode); - if (this.geometry.indexBuffer.data.length !== finalIndices.length) { - this.geometry.indexBuffer.data = new Uint32Array(finalIndices); + const indexBuffer = this.geometry.indexBuffer; + if (indexBuffer.data.length !== finalIndices.length) { + indexBuffer.data = new Uint32Array(finalIndices); } else { + const indexBufferData = indexBuffer.data; for (let i = 0; i < finalIndicesLength; i++) { - this.geometry.indexBuffer.data[i] = finalIndices[i]; + indexBufferData[i] = finalIndices[i]; } } this.name = slotName; - this.geometry.getBuffer("aVertexPosition").update(); - this.geometry.getBuffer("aTextureCoord").update(); - this.geometry.indexBuffer.update(); + textureCoord.update(); + vertexCoord.update(); + indexBuffer.update(); } } From 369dfb8b83e3d8038f89ead3177f2d82fbf515d2 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 5 Jul 2024 12:28:45 +0200 Subject: [PATCH 02/30] [ts] Added SkeletonClipping.clipTrianglesUnpacked, changed NumberLikeArray, introduced IntLikeArray, made Texture more generic clipTrianglesUnpacked will write only x/y vertex positions to clippedVertices and u/v to clippedUVs. This can be used for runtimes that do not use packed vertices. --- spine-ts/spine-core/src/SkeletonClipping.ts | 87 +++++++++++++++++++++ spine-ts/spine-core/src/Texture.ts | 6 +- spine-ts/spine-core/src/Utils.ts | 9 ++- 3 files changed, 96 insertions(+), 6 deletions(-) diff --git a/spine-ts/spine-core/src/SkeletonClipping.ts b/spine-ts/spine-core/src/SkeletonClipping.ts index a6d88f841..9f5cae242 100644 --- a/spine-ts/spine-core/src/SkeletonClipping.ts +++ b/spine-ts/spine-core/src/SkeletonClipping.ts @@ -37,6 +37,7 @@ export class SkeletonClipping { private clippingPolygon = new Array(); private clipOutput = new Array(); clippedVertices = new Array(); + clippedUVs = new Array(); clippedTriangles = new Array(); private scratch = new Array(); @@ -303,6 +304,92 @@ export class SkeletonClipping { } } + public clipTrianglesUnpacked (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number, uvs: NumberArrayLike) { + let clipOutput = this.clipOutput, clippedVertices = this.clippedVertices, clippedUVs = this.clippedUVs; + let clippedTriangles = this.clippedTriangles; + let polygons = this.clippingPolygons!; + let polygonsCount = polygons.length; + + let index = 0; + clippedVertices.length = 0; + clippedUVs.length = 0; + clippedTriangles.length = 0; + for (let i = 0; i < trianglesLength; i += 3) { + let vertexOffset = triangles[i] << 1; + let x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1]; + let u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1]; + + vertexOffset = triangles[i + 1] << 1; + let x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1]; + let u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1]; + + vertexOffset = triangles[i + 2] << 1; + let x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1]; + let u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1]; + + for (let p = 0; p < polygonsCount; p++) { + let s = clippedVertices.length; + if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) { + let clipOutputLength = clipOutput.length; + if (clipOutputLength == 0) continue; + let d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1; + let d = 1 / (d0 * d2 + d1 * (y1 - y3)); + + let clipOutputCount = clipOutputLength >> 1; + let clipOutputItems = this.clipOutput; + let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * 2); + let clippedUVsItems = Utils.setArraySize(clippedUVs, s + clipOutputCount * 2); + for (let ii = 0; ii < clipOutputLength; ii += 2, s += 2) { + let x = clipOutputItems[ii], y = clipOutputItems[ii + 1]; + clippedVerticesItems[s] = x; + clippedVerticesItems[s + 1] = y; + let c0 = x - x3, c1 = y - y3; + let a = (d0 * c0 + d1 * c1) * d; + let b = (d4 * c0 + d2 * c1) * d; + let c = 1 - a - b; + clippedUVsItems[s] = u1 * a + u2 * b + u3 * c; + clippedUVsItems[s + 1] = v1 * a + v2 * b + v3 * c; + } + + s = clippedTriangles.length; + let clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3 * (clipOutputCount - 2)); + clipOutputCount--; + for (let ii = 1; ii < clipOutputCount; ii++, s += 3) { + clippedTrianglesItems[s] = index; + clippedTrianglesItems[s + 1] = (index + ii); + clippedTrianglesItems[s + 2] = (index + ii + 1); + } + index += clipOutputCount + 1; + + } else { + let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + 3 * 2); + clippedVerticesItems[s] = x1; + clippedVerticesItems[s + 1] = y1; + clippedVerticesItems[s + 2] = x2; + clippedVerticesItems[s + 3] = y2; + clippedVerticesItems[s + 4] = x3; + clippedVerticesItems[s + 5] = y3; + + let clippedUVSItems = Utils.setArraySize(clippedUVs, s + 3 * 2); + clippedUVSItems[s] = u1; + clippedUVSItems[s + 1] = v1; + clippedUVSItems[s + 2] = u2; + clippedUVSItems[s + 3] = v2; + clippedUVSItems[s + 4] = u3; + clippedUVSItems[s + 5] = v3; + + s = clippedTriangles.length; + let clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3); + clippedTrianglesItems[s] = index; + clippedTrianglesItems[s + 1] = (index + 1); + clippedTrianglesItems[s + 2] = (index + 2); + index += 3; + break; + } + } + } + } + /** Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping * area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */ clip (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, clippingArea: Array, output: Array) { diff --git a/spine-ts/spine-core/src/Texture.ts b/spine-ts/spine-core/src/Texture.ts index 7d57aefd4..c9b6fd409 100644 --- a/spine-ts/spine-core/src/Texture.ts +++ b/spine-ts/spine-core/src/Texture.ts @@ -28,13 +28,13 @@ *****************************************************************************/ export abstract class Texture { - protected _image: HTMLImageElement | ImageBitmap; + protected _image: HTMLImageElement | ImageBitmap | any; - constructor (image: HTMLImageElement | ImageBitmap) { + constructor (image: HTMLImageElement | ImageBitmap | any) { this._image = image; } - getImage (): HTMLImageElement | ImageBitmap { + getImage (): HTMLImageElement | ImageBitmap | any { return this._image; } diff --git a/spine-ts/spine-core/src/Utils.ts b/spine-ts/spine-core/src/Utils.ts index 43df6f430..c821117d2 100644 --- a/spine-ts/spine-core/src/Utils.ts +++ b/spine-ts/spine-core/src/Utils.ts @@ -87,10 +87,13 @@ export class StringSet { } } -export interface NumberArrayLike { +export type NumberArrayLike = Array | Float32Array; +export type IntArrayLike = Array | Int16Array; + +/*export interface NumberArrayLike { readonly length: number; [n: number]: number; -} +}*/ export interface Disposable { dispose (): void; @@ -313,7 +316,7 @@ export class Utils { } } - static newShortArray (size: number): NumberArrayLike { + static newShortArray (size: number): IntArrayLike { if (Utils.SUPPORTS_TYPED_ARRAYS) return new Int16Array(size) else { From e076a1c7d9bda03fa927d01ec23f0d4574769baa Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Fri, 5 Jul 2024 12:54:20 +0200 Subject: [PATCH 03/30] [unity] Added missing attribute drawer changes of last commits for ui-toolkit package (on spine-unity side). --- .../Editor/SpineAttributeDrawers.cs | 64 ++++++++++++++++++- .../Runtime/spine-unity/SpineAttributes.cs | 13 +++- spine-unity/Assets/Spine/package.json | 2 +- .../package.json | 4 +- 4 files changed, 74 insertions(+), 9 deletions(-) diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs index ddd1ff89d..fb597cb59 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs @@ -152,9 +152,24 @@ namespace Spine.Unity.Editor { Texture2D image = Icon; GUIStyle usedStyle = IsValueValid(property) ? EditorStyles.popup : ErrorPopupStyle; string propertyStringValue = (property.hasMultipleDifferentValues) ? SpineInspectorUtility.EmDash : property.stringValue; - if (GUI.Button(position, string.IsNullOrEmpty(propertyStringValue) ? NoneLabel(image) : - SpineInspectorUtility.TempContent(propertyStringValue, image), usedStyle)) - Selector(property); + + if (!TargetAttribute.avoidGenericMenu) { + if (GUI.Button(position, string.IsNullOrEmpty(propertyStringValue) ? NoneLabel(image) : + SpineInspectorUtility.TempContent(propertyStringValue, image), usedStyle)) + Selector(property); + } else { + SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(false); + List contentList = new List(); + List valueList = new List(); + PopulatePopupList(ref contentList, ref valueList, image, property, TargetAttribute, skeletonData); + int currentIndex = valueList.IndexOf(propertyStringValue); + int previousIndex = currentIndex; + currentIndex = EditorGUI.Popup(position, currentIndex, contentList.ToArray()); + if (previousIndex != currentIndex) { + property.stringValue = valueList[currentIndex]; + property.serializedObject.ApplyModifiedProperties(); + } + } } public ISkeletonComponent GetTargetSkeletonComponent (SerializedProperty property) { @@ -192,6 +207,12 @@ namespace Spine.Unity.Editor { serializedProperty.serializedObject.ApplyModifiedProperties(); } + protected virtual void PopulatePopupList (ref List contentList, ref List valueList, + Texture2D image, SerializedProperty property, T targetAttribute, SkeletonData data) { + contentList.Add(new GUIContent ("Type Not Supported")); + valueList.Add(string.Empty); + } + public override float GetPropertyHeight (SerializedProperty property, GUIContent label) { return 18; } @@ -302,6 +323,25 @@ namespace Spine.Unity.Editor { } } + protected override void PopulatePopupList (ref List contentList, ref List valueList, + Texture2D image, SerializedProperty property, SpineSkin targetAttribute, SkeletonData data) { + + if (targetAttribute.includeNone) { + contentList.Add(new GUIContent(NoneStringConstant, image)); + valueList.Add(string.Empty); + } + + for (int i = 0; i < data.Skins.Count; i++) { + string name = data.Skins.Items[i].Name; + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) { + bool isDefault = string.Equals(name, DefaultSkinName, StringComparison.Ordinal); + string choiceValue = TargetAttribute.defaultAsEmptyString && isDefault ? string.Empty : name; + contentList.Add(new GUIContent(name, image)); + valueList.Add(choiceValue); + } + } + } + } [CustomPropertyDrawer(typeof(SpineAnimation))] @@ -348,6 +388,24 @@ namespace Spine.Unity.Editor { } } + protected override void PopulatePopupList (ref List contentList, ref List valueList, + Texture2D image, SerializedProperty property, SpineAnimation targetAttribute, SkeletonData data) { + + ExposedList animations = data.Animations; + if (targetAttribute.includeNone) { + contentList.Add(new GUIContent(NoneString, image)); + valueList.Add(string.Empty); + } + + for (int i = 0; i < animations.Count; i++) { + string name = animations.Items[i].Name; + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) { + contentList.Add(new GUIContent(name, image)); + valueList.Add(name); + } + } + } + } [CustomPropertyDrawer(typeof(SpineEvent))] diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/SpineAttributes.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/SpineAttributes.cs index 1ad9e0160..86170b5e5 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/SpineAttributes.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/SpineAttributes.cs @@ -33,12 +33,13 @@ using UnityEngine; namespace Spine.Unity { - [AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = true, AllowMultiple = false)] public abstract class SpineAttributeBase : PropertyAttribute { public string dataField = ""; public string startsWith = ""; public bool includeNone = true; public bool fallbackToTextField = false; + public bool avoidGenericMenu = false; } public class SpineBone : SpineAttributeBase { @@ -103,11 +104,14 @@ namespace Spine.Unity { /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. /// - public SpineAnimation (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) { + public SpineAnimation (string startsWith = "", string dataField = "", + bool includeNone = true, bool fallbackToTextField = false, bool avoidGenericMenu = false) { + this.startsWith = startsWith; this.dataField = dataField; this.includeNone = includeNone; this.fallbackToTextField = fallbackToTextField; + this.avoidGenericMenu = avoidGenericMenu; } } @@ -205,12 +209,15 @@ namespace Spine.Unity { public bool defaultAsEmptyString = false; - public SpineSkin (string startsWith = "", string dataField = "", bool includeNone = false, bool fallbackToTextField = false, bool defaultAsEmptyString = false) { + public SpineSkin (string startsWith = "", string dataField = "", bool includeNone = false, + bool fallbackToTextField = false, bool defaultAsEmptyString = false, bool avoidGenericMenu = false) { + this.startsWith = startsWith; this.dataField = dataField; this.includeNone = includeNone; this.fallbackToTextField = fallbackToTextField; this.defaultAsEmptyString = defaultAsEmptyString; + this.avoidGenericMenu = avoidGenericMenu; } } diff --git a/spine-unity/Assets/Spine/package.json b/spine-unity/Assets/Spine/package.json index edc054335..36b034007 100644 --- a/spine-unity/Assets/Spine/package.json +++ b/spine-unity/Assets/Spine/package.json @@ -2,7 +2,7 @@ "name": "com.esotericsoftware.spine.spine-unity", "displayName": "spine-unity Runtime", "description": "This plugin provides the spine-unity runtime core.", - "version": "4.2.74", + "version": "4.2.75", "unity": "2018.3", "author": { "name": "Esoteric Software", diff --git a/spine-unity/Modules/com.esotericsoftware.spine.ui-toolkit/package.json b/spine-unity/Modules/com.esotericsoftware.spine.ui-toolkit/package.json index b1a46f600..c1e6f761b 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.ui-toolkit/package.json +++ b/spine-unity/Modules/com.esotericsoftware.spine.ui-toolkit/package.json @@ -1,7 +1,7 @@ { "name": "com.esotericsoftware.spine.ui-toolkit", "displayName": "Spine UI Toolkit [Experimental]", - "description": "This plugin provides UI Toolkit integration for the spine-unity runtime.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime, version 4.2.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)", + "description": "This plugin provides UI Toolkit integration for the spine-unity runtime.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime, version 4.2.75 or newer.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)", "version": "4.2.0-preview.1", "unity": "2023.2", "author": { @@ -11,7 +11,7 @@ }, "dependencies": { "com.unity.modules.uielements": "1.0.0", - "com.esotericsoftware.spine.spine-unity": "4.2.21" + "com.esotericsoftware.spine.spine-unity": "4.2.75" }, "keywords": [ "spine", From 2f44b90878345a3f2e1c2264bb65470b1dfe61f7 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Fri, 5 Jul 2024 12:58:37 +0200 Subject: [PATCH 04/30] [unity] OnDemandTextureLoader: Added TextureLoadFailed callback, used by Addressables extension package. --- .../spine-unity/Asset Types/OnDemandTextureLoader.cs | 9 +++++++++ .../Runtime/AddressablesTextureLoader.cs | 2 ++ .../com.esotericsoftware.spine.addressables/package.json | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/OnDemandTextureLoader.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/OnDemandTextureLoader.cs index a7b5c4613..8f18a0937 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/OnDemandTextureLoader.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/OnDemandTextureLoader.cs @@ -89,6 +89,7 @@ namespace Spine.Unity { public delegate void TextureLoadDelegate (OnDemandTextureLoader loader, Material material, int textureIndex); protected event TextureLoadDelegate onTextureRequested; protected event TextureLoadDelegate onTextureLoaded; + protected event TextureLoadDelegate onTextureLoadFailed; protected event TextureLoadDelegate onTextureUnloaded; public event TextureLoadDelegate TextureRequested { @@ -99,6 +100,10 @@ namespace Spine.Unity { add { onTextureLoaded += value; } remove { onTextureLoaded -= value; } } + public event TextureLoadDelegate TextureLoadFailed { + add { onTextureLoadFailed += value; } + remove { onTextureLoadFailed -= value; } + } public event TextureLoadDelegate TextureUnloaded { add { onTextureUnloaded += value; } remove { onTextureUnloaded -= value; } @@ -112,6 +117,10 @@ namespace Spine.Unity { if (onTextureLoaded != null) onTextureLoaded(this, material, textureIndex); } + protected void OnTextureLoadFailed (Material material, int textureIndex) { + if (onTextureLoadFailed != null) + onTextureLoadFailed(this, material, textureIndex); + } protected void OnTextureUnloaded (Material material, int textureIndex) { if (onTextureUnloaded != null) onTextureUnloaded(this, material, textureIndex); diff --git a/spine-unity/Modules/com.esotericsoftware.spine.addressables/Runtime/AddressablesTextureLoader.cs b/spine-unity/Modules/com.esotericsoftware.spine.addressables/Runtime/AddressablesTextureLoader.cs index 3411087f2..f79cc2549 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.addressables/Runtime/AddressablesTextureLoader.cs +++ b/spine-unity/Modules/com.esotericsoftware.spine.addressables/Runtime/AddressablesTextureLoader.cs @@ -87,6 +87,8 @@ namespace Spine.Unity { materialToUpdate.mainTexture = loadedTexture; OnTextureLoaded(materialToUpdate, textureIndex); if (onTextureLoaded != null) onTextureLoaded(loadedTexture); + } else { + OnTextureLoadFailed(materialToUpdate, textureIndex); } }; } diff --git a/spine-unity/Modules/com.esotericsoftware.spine.addressables/package.json b/spine-unity/Modules/com.esotericsoftware.spine.addressables/package.json index fb9553ae9..72247b3bf 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.addressables/package.json +++ b/spine-unity/Modules/com.esotericsoftware.spine.addressables/package.json @@ -2,7 +2,7 @@ "name": "com.esotericsoftware.spine.addressables", "displayName": "Spine Addressables Extensions [Experimental]", "description": "This experimental plugin provides integration of Addressables on-demand texture loading for the spine-unity runtime.\nPlease be sure to test this package first and create backups of your project before using.\n\nUsage: First declare your target Material textures as addressable. Then select the SpineAtlasAsset, right-click the SpineAtlasAsset Inspector heading and select 'Add Addressables Loader'. This generates an 'AddressableTextureLoader' asset providing configuration parameters and sets up low-resolution placeholder textures which are automatically assigned in a pre-build step when building your game executable.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime (via the spine-unity unitypackage), version 4.2.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)", - "version": "4.2.0-preview.3", + "version": "4.2.0-preview.4", "unity": "2018.3", "author": { "name": "Esoteric Software", From 853dc33e1902575e9bd73b9e5ec432a9b0bb8a1f Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Fri, 5 Jul 2024 13:01:18 +0200 Subject: [PATCH 05/30] [unity] Minor: fixed whitespace formatting. --- .../Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs index fb597cb59..3b1291b11 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs @@ -209,7 +209,7 @@ namespace Spine.Unity.Editor { protected virtual void PopulatePopupList (ref List contentList, ref List valueList, Texture2D image, SerializedProperty property, T targetAttribute, SkeletonData data) { - contentList.Add(new GUIContent ("Type Not Supported")); + contentList.Add(new GUIContent("Type Not Supported")); valueList.Add(string.Empty); } From a1f077d43c4f8d55a85d87ae5dd79aa6296aed33 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 5 Jul 2024 15:17:59 +0200 Subject: [PATCH 06/30] [ts][canvaskit] Added CanvasKit runtime for NodeJS and browser environments --- spine-ts/README.md | 27 ++- spine-ts/index.html | 4 + spine-ts/package-lock.json | 61 ++++++ spine-ts/package.json | 17 +- spine-ts/spine-canvaskit/LICENSE | 26 +++ spine-ts/spine-canvaskit/README.md | 3 + .../example/assets/spineboy-pro.skel | Bin 0 -> 64463 bytes .../example/assets/spineboy.atlas | 94 +++++++++ .../example/assets/spineboy.png | Bin 0 -> 245321 bytes spine-ts/spine-canvaskit/example/headless.js | 76 ++++++++ spine-ts/spine-canvaskit/example/index.html | 80 ++++++++ spine-ts/spine-canvaskit/package.json | 41 ++++ spine-ts/spine-canvaskit/src/index.ts | 180 ++++++++++++++++++ spine-ts/spine-canvaskit/tsconfig.json | 18 ++ spine-ts/spine-core/src/SkeletonBinary.ts | 4 +- spine-ts/spine-core/src/Utils.ts | 5 - spine-ts/spine-player/src/Player.ts | 2 +- spine-ts/spine-player/src/PlayerEditor.ts | 2 +- spine-ts/tsconfig.json | 53 +++--- 19 files changed, 642 insertions(+), 51 deletions(-) create mode 100644 spine-ts/spine-canvaskit/LICENSE create mode 100644 spine-ts/spine-canvaskit/README.md create mode 100644 spine-ts/spine-canvaskit/example/assets/spineboy-pro.skel create mode 100644 spine-ts/spine-canvaskit/example/assets/spineboy.atlas create mode 100644 spine-ts/spine-canvaskit/example/assets/spineboy.png create mode 100644 spine-ts/spine-canvaskit/example/headless.js create mode 100644 spine-ts/spine-canvaskit/example/index.html create mode 100644 spine-ts/spine-canvaskit/package.json create mode 100644 spine-ts/spine-canvaskit/src/index.ts create mode 100644 spine-ts/spine-canvaskit/tsconfig.json diff --git a/spine-ts/README.md b/spine-ts/README.md index 6343767fc..1161689b6 100644 --- a/spine-ts/README.md +++ b/spine-ts/README.md @@ -6,10 +6,11 @@ up into multiple modules: 1. `spine-core/`, the core classes to load and process Spine skeletons. 1. `spine-webgl/`, a self-contained WebGL backend, built on the core classes. 1. `spine-canvas/`, a self-contained Canvas backend, built on the core classes. -1. `spine-threejs/`, a self-contained THREE.JS backend, built on the core classes. +1. `spine-canvaskit/`, a self-contained [CanvasKit](https://skia.org/docs/user/modules/canvaskit/) backend, built on the core classes for CanvasKit, supporting both NodeJS for headless rendering, and browsers. +1. `spine-threejs/`, a self-contained [THREE.JS](https://threejs.org/) backend, built on the core classes. 1. `spine-player/`, a self-contained player to easily display Spine animations on your website, built on the core classes and WebGL backend. -1. `spine-phaser/`, a Phaser backend, built on the core classes. -1. `spine-pixi/`, a Pixi backend, built on the core classes. +1. `spine-phaser/`, a [Phaser](https://phaser.io/) backend, built on the core classes. +1. `spine-pixi/`, a [PixiJS](https://pixijs.com/) backend, built on the core classes. In most cases, the `spine-player` module is best suited for your needs. Please refer to the [Spine Web Player documentation](https://esotericsoftware.com/spine-player) for more information. @@ -35,9 +36,11 @@ For the official legal terms governing the Spine Runtimes, please read the [Spin spine-ts works with data exported from Spine 4.2.xx. -The spine-ts WebGL and Player backends support all Spine features. +spine-ts Canvas does not support mesh attachments, clipping attachments, or two-color tinting. Only the alpha channel from tint colors is applied. Experimental support for mesh attachments can be enabled by setting `spine.SkeletonRenderer.useTriangleRendering` to true. Note that this experimental mesh rendering is slow and render with artifacts on some browsers. -spine-ts Canvas does not support mesh attachments, clipping attachments, or color tinting. Only the alpha channel from tint colors is applied. Experimental support for mesh attachments can be enabled by setting `spine.SkeletonRenderer.useTriangleRendering` to true. Note that this experimental mesh rendering is slow and render with artifacts on some browsers. +spine-canvaskit supports all Spine features except two-color tinting. + +The spine-webgl and spine-player support all Spine features. spine-ts THREE.JS does not support two color tinting. The THREE.JS backend provides `SkeletonMesh.zOffset` to avoid z-fighting. Adjust to your near/far plane settings. @@ -50,20 +53,23 @@ All spine-ts modules are published to [npm](http://npmjs.com) for consumption vi You can include a module in your project via a ` -// spine-ts Canvas +// spine-canvas -// spine-ts WebGL +// spine-canvaskit + + +// spine-webgl -// spine-ts Player, which requires a spine-player.css as well +// spine-player, which requires a spine-player.css as well -// spine-ts ThreeJS +// spine-threejs // spine-phaser @@ -84,6 +90,7 @@ If your project dependencies are managed through NPM or Yarn, you can add spine- ``` npm install @esotericsoftware/spine-core npm install @esotericsoftware/spine-canvas +npm install @esotericsoftware/spine-canvaskit npm install @esotericsoftware/spine-webgl npm install @esotericsoftware/spine-player npm install @esotericsoftware/spine-threejs diff --git a/spine-ts/index.html b/spine-ts/index.html index 60518ab10..343e4d3fb 100644 --- a/spine-ts/index.html +++ b/spine-ts/index.html @@ -20,6 +20,10 @@ Mouse click +
  • CanvasKit
  • +
  • Pixi