[ts] Processed spine-ts (not player) with biome.

This commit is contained in:
Davide Tantillo 2025-10-27 10:57:30 +01:00
parent 9ea694b16f
commit c3ac62d1dd
62 changed files with 956 additions and 809 deletions

View File

@ -0,0 +1,64 @@
{
"skeleton": {
"hash": "kUVJGzLCHio",
"spine": "4.2.43",
"x": -66,
"y": -141.5,
"width": 142,
"height": 232,
"images": "./images/",
"audio": "./audio"
},
"bones": [
{ "name": "root" },
{ "name": "empty_1", "parent": "root", "x": -44, "y": 53.5 },
{ "name": "empty_8", "parent": "root", "x": 5, "y": -104.5 },
{ "name": "empty_7", "parent": "root", "x": -44, "y": -104.5 },
{ "name": "empty_6", "parent": "root", "x": 54, "y": -25.5 },
{ "name": "empty_5", "parent": "root", "x": 5, "y": -25.5 },
{ "name": "empty_4", "parent": "root", "x": -44, "y": -25.5 },
{ "name": "empty_3", "parent": "root", "x": 54, "y": 53.5 },
{ "name": "empty_2", "parent": "root", "x": 5, "y": 53.5 }
],
"slots": [
{ "name": "mask", "bone": "root", "attachment": "mask" },
{ "name": "empty_1", "bone": "empty_1", "attachment": "empty" },
{ "name": "empty_2", "bone": "empty_2", "attachment": "empty" },
{ "name": "empty_3", "bone": "empty_3", "attachment": "empty" },
{ "name": "empty_4", "bone": "empty_4", "attachment": "empty" },
{ "name": "empty_5", "bone": "empty_5", "attachment": "empty" },
{ "name": "empty_6", "bone": "empty_6", "attachment": "empty" },
{ "name": "empty_7", "bone": "empty_7", "attachment": "empty" },
{ "name": "empty_8", "bone": "empty_8", "attachment": "empty" }
],
"skins": [
{
"name": "default",
"attachments": {
"empty_1": { "empty": { "width": 44, "height": 74 } },
"empty_2": { "empty": { "width": 44, "height": 74 } },
"empty_3": { "empty": { "width": 44, "height": 74 } },
"empty_4": { "empty": { "width": 44, "height": 74 } },
"empty_5": { "empty": { "width": 44, "height": 74 } },
"empty_6": { "empty": { "width": 44, "height": 74 } },
"empty_7": { "empty": { "width": 44, "height": 74 } },
"empty_8": { "empty": { "width": 44, "height": 74 } },
"mask": {
"mask": {
"type": "clipping",
"end": "mask",
"vertexCount": 16,
"vertices": [
-53.67, -27.41, -147.5, -13.5, -65, 33.5, -92.5, 70.5, -21.01,
32.95, 12.5, 114.5, 36.79, 30.32, 129.5, 66.5, 93, 30.5, 124.5,
-5.5, 98, -51.5, 139.5, -97.5, 38.8, -65.21, -9.5, -181.5, -26.27,
-70.62, -106.5, -105.5
],
"color": "ce3a3aff"
}
}
}
}
],
"animations": { "animation": {} }
}

View File

@ -16,7 +16,8 @@
"useExponentiationOperator": "off"
},
"suspicious": {
"noAssignInExpressions": "off"
"noAssignInExpressions": "off",
"noPrototypeBuiltins": "off"
}
}
}

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Texture, TextureFilter, TextureWrap } from "@esotericsoftware/spine-core";
import { Texture, type TextureFilter, type TextureWrap } from "@esotericsoftware/spine-core";
export class CanvasTexture extends Texture {
constructor (image: HTMLImageElement | ImageBitmap) {

View File

@ -27,8 +27,8 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Utils, Color, Skeleton, RegionAttachment, BlendMode, MeshAttachment, Slot, TextureRegion, TextureAtlasRegion } from "@esotericsoftware/spine-core";
import { CanvasTexture } from "./CanvasTexture.js";
import { type BlendMode, Color, MeshAttachment, RegionAttachment, type Skeleton, type Slot, type TextureRegion, Utils } from "@esotericsoftware/spine-core";
import type { CanvasTexture } from "./CanvasTexture.js";
const worldVertices = Utils.newFloatArray(8);
@ -53,28 +53,28 @@ export class SkeletonRenderer {
}
private drawImages (skeleton: Skeleton) {
let ctx = this.ctx;
let color = this.tempColor;
let skeletonColor = skeleton.color;
let drawOrder = skeleton.drawOrder;
const ctx = this.ctx;
const color = this.tempColor;
const skeletonColor = skeleton.color;
const drawOrder = skeleton.drawOrder;
if (this.debugRendering) ctx.strokeStyle = "green";
for (let i = 0, n = drawOrder.length; i < n; i++) {
const slot = drawOrder[i];
let bone = slot.bone;
const bone = slot.bone;
if (!bone.active) continue;
let pose = slot.applied;
let attachment = pose.attachment;
const pose = slot.applied;
const attachment = pose.attachment;
if (!(attachment instanceof RegionAttachment)) continue;
attachment.computeWorldVertices(slot, worldVertices, 0, 2);
let region: TextureRegion = <TextureRegion>attachment.region;
const region: TextureRegion = <TextureRegion>attachment.region;
let image: HTMLImageElement = (<CanvasTexture>region.texture).getImage() as HTMLImageElement;
const image: HTMLImageElement = (<CanvasTexture>region.texture).getImage() as HTMLImageElement;
let slotColor = pose.color;
let regionColor = attachment.color;
const slotColor = pose.color;
const regionColor = attachment.color;
color.set(skeletonColor.r * slotColor.r * regionColor.r,
skeletonColor.g * slotColor.g * regionColor.g,
skeletonColor.b * slotColor.b * regionColor.b,
@ -86,13 +86,13 @@ export class SkeletonRenderer {
ctx.translate(attachment.offset[0], attachment.offset[1]);
ctx.rotate(attachment.rotation * Math.PI / 180);
let atlasScale = attachment.width / region.originalWidth;
const atlasScale = attachment.width / region.originalWidth;
ctx.scale(atlasScale * attachment.scaleX, atlasScale * attachment.scaleY);
let w = region.width, h = region.height;
ctx.translate(w / 2, h / 2);
if (attachment.region!.degrees == 90) {
let t = w;
if (attachment.region?.degrees === 90) {
const t = w;
w = h;
h = t;
ctx.rotate(-Math.PI / 2);
@ -108,10 +108,10 @@ export class SkeletonRenderer {
}
private drawTriangles (skeleton: Skeleton) {
let ctx = this.ctx;
let color = this.tempColor;
let skeletonColor = skeleton.color;
let drawOrder = skeleton.drawOrder;
const ctx = this.ctx;
const color = this.tempColor;
const skeletonColor = skeleton.color;
const drawOrder = skeleton.drawOrder;
let blendMode: BlendMode | null = null;
let vertices: ArrayLike<number> = this.vertices;
@ -119,28 +119,28 @@ export class SkeletonRenderer {
for (let i = 0, n = drawOrder.length; i < n; i++) {
const slot = drawOrder[i];
let pose = slot.applied;
let attachment = pose.attachment;
const pose = slot.applied;
const attachment = pose.attachment;
let texture: HTMLImageElement;
if (attachment instanceof RegionAttachment) {
let regionAttachment = <RegionAttachment>attachment;
const regionAttachment = <RegionAttachment>attachment;
vertices = this.computeRegionVertices(slot, regionAttachment, false);
triangles = SkeletonRenderer.QUAD_TRIANGLES;
texture = (<CanvasTexture>regionAttachment.region!.texture).getImage() as HTMLImageElement;
texture = (<CanvasTexture>regionAttachment.region?.texture).getImage() as HTMLImageElement;
} else if (attachment instanceof MeshAttachment) {
let mesh = <MeshAttachment>attachment;
const mesh = <MeshAttachment>attachment;
vertices = this.computeMeshVertices(slot, mesh, false);
triangles = mesh.triangles;
texture = (<CanvasTexture>mesh.region!.texture).getImage() as HTMLImageElement;
texture = (<CanvasTexture>mesh.region?.texture).getImage() as HTMLImageElement;
} else
continue;
if (texture) {
if (slot.data.blendMode != blendMode) blendMode = slot.data.blendMode;
if (slot.data.blendMode !== blendMode) blendMode = slot.data.blendMode;
let slotColor = pose.color;
let attachmentColor = attachment.color;
const slotColor = pose.color;
const attachmentColor = attachment.color;
color.set(skeletonColor.r * slotColor.r * attachmentColor.r,
skeletonColor.g * slotColor.g * attachmentColor.g,
skeletonColor.b * slotColor.b * attachmentColor.b,
@ -148,12 +148,12 @@ export class SkeletonRenderer {
ctx.globalAlpha = color.a;
for (var j = 0; j < triangles.length; j += 3) {
let t1 = triangles[j] * 8, t2 = triangles[j + 1] * 8, t3 = triangles[j + 2] * 8;
for (let j = 0; j < triangles.length; j += 3) {
const t1 = triangles[j] * 8, t2 = triangles[j + 1] * 8, t3 = triangles[j + 2] * 8;
let x0 = vertices[t1], y0 = vertices[t1 + 1], u0 = vertices[t1 + 6], v0 = vertices[t1 + 7];
let x1 = vertices[t2], y1 = vertices[t2 + 1], u1 = vertices[t2 + 6], v1 = vertices[t2 + 7];
let x2 = vertices[t3], y2 = vertices[t3 + 1], u2 = vertices[t3 + 6], v2 = vertices[t3 + 7];
const x0 = vertices[t1], y0 = vertices[t1 + 1], u0 = vertices[t1 + 6], v0 = vertices[t1 + 7];
const x1 = vertices[t2], y1 = vertices[t2 + 1], u1 = vertices[t2 + 6], v1 = vertices[t2 + 7];
const x2 = vertices[t3], y2 = vertices[t3 + 1], u2 = vertices[t3 + 6], v2 = vertices[t3 + 7];
this.drawTriangle(texture, x0, y0, u0, v0, x1, y1, u1, v1, x2, y2, u2, v2);
@ -178,7 +178,7 @@ export class SkeletonRenderer {
private drawTriangle (img: HTMLImageElement, x0: number, y0: number, u0: number, v0: number,
x1: number, y1: number, u1: number, v1: number,
x2: number, y2: number, u2: number, v2: number) {
let ctx = this.ctx;
const ctx = this.ctx;
const width = img.width - 1;
const height = img.height - 1;
@ -206,7 +206,7 @@ export class SkeletonRenderer {
v2 -= v0;
let det = u1 * v2 - u2 * v1;
if (det == 0) return;
if (det === 0) return;
det = 1 / det;
// linear transformation
@ -227,12 +227,12 @@ export class SkeletonRenderer {
}
private computeRegionVertices (slot: Slot, region: RegionAttachment, pma: boolean) {
let skeletonColor = slot.skeleton.color;
let slotColor = slot.applied.color;
let regionColor = region.color;
let alpha = skeletonColor.a * slotColor.a * regionColor.a;
let multiplier = pma ? alpha : 1;
let color = this.tempColor;
const skeletonColor = slot.skeleton.color;
const slotColor = slot.applied.color;
const regionColor = region.color;
const alpha = skeletonColor.a * slotColor.a * regionColor.a;
const multiplier = pma ? alpha : 1;
const color = this.tempColor;
color.set(skeletonColor.r * slotColor.r * regionColor.r * multiplier,
skeletonColor.g * slotColor.g * regionColor.g * multiplier,
skeletonColor.b * slotColor.b * regionColor.b * multiplier,
@ -240,8 +240,8 @@ export class SkeletonRenderer {
region.computeWorldVertices(slot, this.vertices, 0, SkeletonRenderer.VERTEX_SIZE);
let vertices = this.vertices;
let uvs = region.uvs;
const vertices = this.vertices;
const uvs = region.uvs;
vertices[RegionAttachment.C1R] = color.r;
vertices[RegionAttachment.C1G] = color.g;
@ -275,24 +275,24 @@ export class SkeletonRenderer {
}
private computeMeshVertices (slot: Slot, mesh: MeshAttachment, pma: boolean) {
let skeleton = slot.skeleton;
let skeletonColor = skeleton.color;
let slotColor = slot.applied.color;
let regionColor = mesh.color;
let alpha = skeletonColor.a * slotColor.a * regionColor.a;
let multiplier = pma ? alpha : 1;
let color = this.tempColor;
const skeleton = slot.skeleton;
const skeletonColor = skeleton.color;
const slotColor = slot.applied.color;
const regionColor = mesh.color;
const alpha = skeletonColor.a * slotColor.a * regionColor.a;
const multiplier = pma ? alpha : 1;
const color = this.tempColor;
color.set(skeletonColor.r * slotColor.r * regionColor.r * multiplier,
skeletonColor.g * slotColor.g * regionColor.g * multiplier,
skeletonColor.b * slotColor.b * regionColor.b * multiplier,
alpha);
let vertexCount = mesh.worldVerticesLength / 2;
const vertexCount = mesh.worldVerticesLength / 2;
let vertices = this.vertices;
if (vertices.length < mesh.worldVerticesLength) this.vertices = vertices = Utils.newFloatArray(mesh.worldVerticesLength);
mesh.computeWorldVertices(skeleton, slot, 0, mesh.worldVerticesLength, vertices, 0, SkeletonRenderer.VERTEX_SIZE);
let uvs = mesh.uvs;
const uvs = mesh.uvs;
for (let i = 0, u = 0, v = 2; i < vertexCount; i++) {
vertices[v++] = color.r;
vertices[v++] = color.g;

View File

@ -1,4 +1,4 @@
export * from "@esotericsoftware/spine-core"
export * from "./AssetManager.js";
export * from "./CanvasTexture.js";
export * from "./SkeletonRenderer.js";
export * from "@esotericsoftware/spine-core"
export * from "./SkeletonRenderer.js";

View File

@ -245,6 +245,7 @@ export class SkeletonRenderer {
positions,
uvs,
colors,
// biome-ignore lint/suspicious/noExplicitAny: canvaskit wants indices as an array of number
indices as any as number[],
false
);

View File

@ -27,29 +27,29 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { SPINE_GAME_OBJECT_TYPE } from "./keys.js";
import { SpinePlugin } from "./SpinePlugin.js";
import {
ComputedSizeMixin,
DepthMixin,
FlipMixin,
ScrollFactorMixin,
TransformMixin,
VisibleMixin,
AlphaMixin,
OriginMixin,
} from "./mixins.js";
import {
AnimationState,
AnimationStateData,
Bone,
type Bone,
MathUtils,
Physics,
Skeleton,
SkeletonClipping,
Skin,
Vector2,
type Vector2,
} from "@esotericsoftware/spine-core";
import { SPINE_GAME_OBJECT_TYPE } from "./keys.js";
import {
AlphaMixin,
ComputedSizeMixin,
DepthMixin,
FlipMixin,
OriginMixin,
ScrollFactorMixin,
TransformMixin,
VisibleMixin,
} from "./mixins.js";
import type { SpinePlugin } from "./SpinePlugin.js";
class BaseSpineGameObject extends Phaser.GameObjects.GameObject {
constructor (scene: Phaser.Scene, type: string) {
@ -99,7 +99,7 @@ export class SetupPoseBoundsProvider implements SpineGameObjectBoundsProvider {
skeleton.setupPose();
skeleton.updateWorldTransform(Physics.update);
const bounds = skeleton.getBoundsRect(this.clipping ? new SkeletonClipping() : undefined);
return bounds.width == Number.NEGATIVE_INFINITY
return bounds.width === Number.NEGATIVE_INFINITY
? { x: 0, y: 0, width: 0, height: 0 }
: bounds;
}
@ -137,7 +137,7 @@ export class SkinsAndAnimationBoundsProvider
const clipper = this.clipping ? new SkeletonClipping() : undefined;
const data = skeleton.data;
if (this.skins.length > 0) {
let customSkin = new Skin("custom-skin");
const customSkin = new Skin("custom-skin");
for (const skinName of this.skins) {
const skin = data.findSkin(skinName);
if (skin == null) continue;
@ -147,12 +147,11 @@ export class SkinsAndAnimationBoundsProvider
}
skeleton.setupPose();
const animation =
this.animation != null ? data.findAnimation(this.animation!) : null;
const animation = this.animation != null ? data.findAnimation(this.animation) : null;
if (animation == null) {
skeleton.updateWorldTransform(Physics.update);
const bounds = skeleton.getBoundsRect(clipper);
return bounds.width == Number.NEGATIVE_INFINITY
return bounds.width === Number.NEGATIVE_INFINITY
? { x: 0, y: 0, width: 0, height: 0 }
: bounds;
} else {
@ -182,7 +181,7 @@ export class SkinsAndAnimationBoundsProvider
width: maxX - minX,
height: maxY - minY,
};
return bounds.width == Number.NEGATIVE_INFINITY
return bounds.width === Number.NEGATIVE_INFINITY
? { x: 0, y: 0, width: 0, height: 0 }
: bounds;
}
@ -240,6 +239,7 @@ export class SpineGameObject extends DepthMixin(
atlasKey: string,
public boundsProvider: SpineGameObjectBoundsProvider = new SetupPoseBoundsProvider()
) {
// biome-ignore lint/suspicious/noExplicitAny: necessary
super(scene, (window as any).SPINE_GAME_OBJECT_TYPE ? (window as any).SPINE_GAME_OBJECT_TYPE : SPINE_GAME_OBJECT_TYPE);
this.setPosition(x, y);
@ -253,7 +253,7 @@ export class SpineGameObject extends DepthMixin(
updateSize () {
if (!this.skeleton) return;
let bounds = this.boundsProvider.calculateBounds(this);
const bounds = this.boundsProvider.calculateBounds(this);
this.width = bounds.width;
this.height = bounds.height;
this.setDisplayOrigin(-bounds.x, -bounds.y);
@ -263,15 +263,15 @@ export class SpineGameObject extends DepthMixin(
/** Converts a point from the skeleton coordinate system to the Phaser world coordinate system. */
skeletonToPhaserWorldCoordinates (point: { x: number; y: number }) {
let transform = this.getWorldTransformMatrix();
let a = transform.a,
const transform = this.getWorldTransformMatrix();
const a = transform.a,
b = transform.b,
c = transform.c,
d = transform.d,
tx = transform.tx,
ty = transform.ty;
let x = point.x;
let y = point.y;
const x = point.x;
const y = point.y;
point.x = x * a + y * c + tx;
point.y = x * b + y * d + ty;
}
@ -280,14 +280,14 @@ export class SpineGameObject extends DepthMixin(
phaserWorldCoordinatesToSkeleton (point: { x: number; y: number }) {
let transform = this.getWorldTransformMatrix();
transform = transform.invert();
let a = transform.a,
const a = transform.a,
b = transform.b,
c = transform.c,
d = transform.d,
tx = transform.tx,
ty = transform.ty;
let x = point.x;
let y = point.y;
const x = point.x;
const y = point.y;
point.x = x * a + y * c + tx;
point.y = x * b + y * d + ty;
}
@ -330,7 +330,7 @@ export class SpineGameObject extends DepthMixin(
if (!this.visible) result = false;
if (!result && this.parentContainer && this.plugin.webGLRenderer) {
var sceneRenderer = this.plugin.webGLRenderer;
const sceneRenderer = this.plugin.webGLRenderer;
if (this.plugin.gl && this.plugin.phaserRenderer instanceof Phaser.Renderer.WebGL.WebGLRenderer && sceneRenderer.batcher.isDrawing) {
sceneRenderer.end();
@ -350,27 +350,27 @@ export class SpineGameObject extends DepthMixin(
if (!this.skeleton || !this.animationState || !this.plugin.webGLRenderer)
return;
let sceneRenderer = this.plugin.webGLRenderer;
const sceneRenderer = this.plugin.webGLRenderer;
if (renderer.newType) {
renderer.pipelines.clear();
sceneRenderer.begin();
}
camera.addToRenderList(src);
let transform = Phaser.GameObjects.GetCalcMatrix(
const transform = Phaser.GameObjects.GetCalcMatrix(
src,
camera,
parentMatrix
).calc;
let a = transform.a,
const a = transform.a,
b = transform.b,
c = transform.c,
d = transform.d,
tx = transform.tx,
ty = transform.ty;
let offsetX = src.offsetX - src.displayOriginX;
let offsetY = src.offsetY - src.displayOriginY;
const offsetX = src.offsetX - src.displayOriginX;
const offsetY = src.offsetY - src.displayOriginY;
sceneRenderer.drawSkeleton(
src.skeleton,
@ -379,8 +379,8 @@ export class SpineGameObject extends DepthMixin(
-1,
(vertices, numVertices, stride) => {
for (let i = 0; i < numVertices; i += stride) {
let vx = vertices[i] + offsetX;
let vy = vertices[i + 1] + offsetY;
const vx = vertices[i] + offsetX;
const vy = vertices[i + 1] + offsetY;
vertices[i] = vx * a + vy * c + tx;
vertices[i + 1] = vx * b + vy * d + ty;
}
@ -402,22 +402,23 @@ export class SpineGameObject extends DepthMixin(
if (!this.skeleton || !this.animationState || !this.plugin.canvasRenderer)
return;
let context = renderer.currentContext;
let skeletonRenderer = this.plugin.canvasRenderer;
const context = renderer.currentContext;
const skeletonRenderer = this.plugin.canvasRenderer;
// biome-ignore lint/suspicious/noExplicitAny: necessary for phaser
(skeletonRenderer as any).ctx = context;
camera.addToRenderList(src);
let transform = Phaser.GameObjects.GetCalcMatrix(
const transform = Phaser.GameObjects.GetCalcMatrix(
src,
camera,
parentMatrix
).calc;
let skeleton = this.skeleton;
const skeleton = this.skeleton;
skeleton.x = transform.tx;
skeleton.y = transform.ty;
skeleton.scaleX = transform.scaleX;
skeleton.scaleY = transform.scaleY;
let root = skeleton.getRootBone()!;
const root = skeleton.getRootBone() as Bone;
root.applied.rotation = -MathUtils.radiansToDegrees * transform.rotationNormalized;
this.skeleton.updateWorldTransform(Physics.update);

View File

@ -88,7 +88,6 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
constructor (scene: Phaser.Scene, pluginManager: Phaser.Plugins.PluginManager, pluginKey: string) {
super(scene, pluginManager, pluginKey);
console.log(pluginKey);
this.game = pluginManager.game;
this.isWebGL = this.game.config.renderType === 2;
this.gl = this.isWebGL ? (this.game.renderer as Phaser.Renderer.WebGL.WebGLRenderer).gl : null;

View File

@ -27,17 +27,21 @@
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
export * from "./require-shim.js"
export * from "./SpinePlugin.js"
export * from "./SpineGameObject.js"
export * from "./mixins.js"
export * from "@esotericsoftware/spine-core";
export * from "@esotericsoftware/spine-webgl";
import { SpineGameObjectConfig, SpinePlugin } from "./SpinePlugin.js";
export * from "./mixins.js"
export * from "./require-shim.js"
export * from "./SpineGameObject.js"
export * from "./SpinePlugin.js"
import { type SpineGameObjectConfig, SpinePlugin } from "./SpinePlugin.js";
// biome-ignore lint/suspicious/noExplicitAny: need to add spine to window
(window as any).spine = { SpinePlugin: SpinePlugin };
// biome-ignore lint/suspicious/noExplicitAny: need to add spine to window
(window as any)["spine.SpinePlugin"] = SpinePlugin;
import { SpineGameObject, SpineGameObjectBoundsProvider } from "./SpineGameObject.js";
import type { SpineGameObject, SpineGameObjectBoundsProvider } from "./SpineGameObject.js";
declare global {
namespace Phaser.Loader {

View File

@ -24,7 +24,9 @@ SOFTWARE.
// Adapted from https://github.com/agogpixel/phaser3-ts-utils/tree/main
let components = (Phaser.GameObjects.Components as any);
// biome-ignore-all lint: ignore biome for this file
const components = (Phaser.GameObjects.Components as any);
export const ComputedSize = components.ComputedSize;
export const Depth = components.Depth;
export const Flip = components.Flip;

View File

@ -27,8 +27,10 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
// biome-ignore-all lint: ignore biome for this file
if (typeof window !== 'undefined' && window.Phaser) {
let prevRequire = window.require;
const prevRequire = window.require;
(window as any).require = (x: string) => {
if (prevRequire) return prevRequire(x);
else if (x === "Phaser") return window.Phaser;

View File

@ -27,29 +27,29 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { SPINE_GAME_OBJECT_TYPE } from "./keys.js";
import { SpinePlugin } from "./SpinePlugin.js";
import {
ComputedSizeMixin,
DepthMixin,
FlipMixin,
ScrollFactorMixin,
TransformMixin,
VisibleMixin,
AlphaMixin,
OriginMixin,
} from "./mixins.js";
import {
AnimationState,
AnimationStateData,
Bone,
type Bone,
MathUtils,
Physics,
Skeleton,
SkeletonClipping,
Skin,
Vector2,
type Vector2,
} from "@esotericsoftware/spine-core";
import { SPINE_GAME_OBJECT_TYPE } from "./keys.js";
import {
AlphaMixin,
ComputedSizeMixin,
DepthMixin,
FlipMixin,
OriginMixin,
ScrollFactorMixin,
TransformMixin,
VisibleMixin,
} from "./mixins.js";
import type { SpinePlugin } from "./SpinePlugin.js";
class BaseSpineGameObject extends Phaser.GameObjects.GameObject {
constructor (scene: Phaser.Scene, type: string) {
@ -99,7 +99,7 @@ export class SetupPoseBoundsProvider implements SpineGameObjectBoundsProvider {
skeleton.setupPose();
skeleton.updateWorldTransform(Physics.update);
const bounds = skeleton.getBoundsRect(this.clipping ? new SkeletonClipping() : undefined);
return bounds.width == Number.NEGATIVE_INFINITY
return bounds.width === Number.NEGATIVE_INFINITY
? { x: 0, y: 0, width: 0, height: 0 }
: bounds;
}
@ -137,7 +137,7 @@ export class SkinsAndAnimationBoundsProvider
const clipper = this.clipping ? new SkeletonClipping() : undefined;
const data = skeleton.data;
if (this.skins.length > 0) {
let customSkin = new Skin("custom-skin");
const customSkin = new Skin("custom-skin");
for (const skinName of this.skins) {
const skin = data.findSkin(skinName);
if (skin == null) continue;
@ -147,12 +147,11 @@ export class SkinsAndAnimationBoundsProvider
}
skeleton.setupPose();
const animation =
this.animation != null ? data.findAnimation(this.animation!) : null;
const animation = this.animation != null ? data.findAnimation(this.animation) : null;
if (animation == null) {
skeleton.updateWorldTransform(Physics.update);
const bounds = skeleton.getBoundsRect(clipper);
return bounds.width == Number.NEGATIVE_INFINITY
return bounds.width === Number.NEGATIVE_INFINITY
? { x: 0, y: 0, width: 0, height: 0 }
: bounds;
} else {
@ -182,7 +181,7 @@ export class SkinsAndAnimationBoundsProvider
width: maxX - minX,
height: maxY - minY,
};
return bounds.width == Number.NEGATIVE_INFINITY
return bounds.width === Number.NEGATIVE_INFINITY
? { x: 0, y: 0, width: 0, height: 0 }
: bounds;
}
@ -240,6 +239,7 @@ export class SpineGameObject extends DepthMixin(
atlasKey: string,
public boundsProvider: SpineGameObjectBoundsProvider = new SetupPoseBoundsProvider()
) {
// biome-ignore lint/suspicious/noExplicitAny: necessary for phaser
super(scene, (window as any).SPINE_GAME_OBJECT_TYPE ? (window as any).SPINE_GAME_OBJECT_TYPE : SPINE_GAME_OBJECT_TYPE);
this.setPosition(x, y);
@ -253,7 +253,7 @@ export class SpineGameObject extends DepthMixin(
updateSize () {
if (!this.skeleton) return;
let bounds = this.boundsProvider.calculateBounds(this);
const bounds = this.boundsProvider.calculateBounds(this);
this.width = bounds.width;
this.height = bounds.height;
this.setDisplayOrigin(-bounds.x, -bounds.y);
@ -263,15 +263,15 @@ export class SpineGameObject extends DepthMixin(
/** Converts a point from the skeleton coordinate system to the Phaser world coordinate system. */
skeletonToPhaserWorldCoordinates (point: { x: number; y: number }) {
let transform = this.getWorldTransformMatrix();
let a = transform.a,
const transform = this.getWorldTransformMatrix();
const a = transform.a,
b = transform.b,
c = transform.c,
d = transform.d,
tx = transform.tx,
ty = transform.ty;
let x = point.x;
let y = point.y;
const x = point.x;
const y = point.y;
point.x = x * a + y * c + tx;
point.y = x * b + y * d + ty;
}
@ -280,14 +280,14 @@ export class SpineGameObject extends DepthMixin(
phaserWorldCoordinatesToSkeleton (point: { x: number; y: number }) {
let transform = this.getWorldTransformMatrix();
transform = transform.invert();
let a = transform.a,
const a = transform.a,
b = transform.b,
c = transform.c,
d = transform.d,
tx = transform.tx,
ty = transform.ty;
let x = point.x;
let y = point.y;
const x = point.x;
const y = point.y;
point.x = x * a + y * c + tx;
point.y = x * b + y * d + ty;
}
@ -325,12 +325,12 @@ export class SpineGameObject extends DepthMixin(
}
willRender (camera: Phaser.Cameras.Scene2D.Camera) {
var GameObjectRenderMask = 0xf;
var result = !this.skeleton || !(GameObjectRenderMask !== this.renderFlags || (this.cameraFilter !== 0 && this.cameraFilter & camera.id));
const GameObjectRenderMask = 0xf;
let result = !this.skeleton || !(GameObjectRenderMask !== this.renderFlags || (this.cameraFilter !== 0 && this.cameraFilter & camera.id));
if (!this.visible) result = false;
if (!result && this.parentContainer && this.plugin.webGLRenderer) {
var sceneRenderer = this.plugin.webGLRenderer;
const sceneRenderer = this.plugin.webGLRenderer;
if (this.plugin.gl && this.plugin.phaserRenderer instanceof Phaser.Renderer.WebGL.WebGLRenderer && sceneRenderer.batcher.isDrawing) {
sceneRenderer.end();
@ -354,7 +354,7 @@ export class SpineGameObject extends DepthMixin(
if (!camera || !src.skeleton || !src.animationState || !src.plugin.webGLRenderer)
return;
let sceneRenderer = src.plugin.webGLRenderer;
const sceneRenderer = src.plugin.webGLRenderer;
// Determine object type in context.
const previousGameObject = displayList[displayListIndex - 1];
@ -376,20 +376,20 @@ export class SpineGameObject extends DepthMixin(
}
camera.addToRenderList(src);
let transform = Phaser.GameObjects.GetCalcMatrix(
const transform = Phaser.GameObjects.GetCalcMatrix(
src,
camera,
parentMatrix
).calc;
let a = transform.a,
const a = transform.a,
b = transform.b,
c = transform.c,
d = transform.d,
tx = transform.tx,
ty = transform.ty;
let offsetX = src.offsetX - src.displayOriginX;
let offsetY = src.offsetY - src.displayOriginY;
const offsetX = src.offsetX - src.displayOriginX;
const offsetY = src.offsetY - src.displayOriginY;
sceneRenderer.drawSkeleton(
src.skeleton,
@ -398,8 +398,8 @@ export class SpineGameObject extends DepthMixin(
-1,
(vertices, numVertices, stride) => {
for (let i = 0; i < numVertices; i += stride) {
let vx = vertices[i] + offsetX;
let vy = vertices[i + 1] + offsetY;
const vx = vertices[i] + offsetX;
const vy = vertices[i + 1] + offsetY;
vertices[i] = vx * a + vy * c + tx;
vertices[i + 1] = vx * b + vy * d + ty;
}
@ -424,22 +424,23 @@ export class SpineGameObject extends DepthMixin(
if (!this.skeleton || !this.animationState || !this.plugin.canvasRenderer)
return;
let context = renderer.currentContext;
let skeletonRenderer = this.plugin.canvasRenderer;
const context = renderer.currentContext;
const skeletonRenderer = this.plugin.canvasRenderer;
// biome-ignore lint/suspicious/noExplicitAny: necessary for phaser
(skeletonRenderer as any).ctx = context;
camera.addToRenderList(src);
let transform = Phaser.GameObjects.GetCalcMatrix(
const transform = Phaser.GameObjects.GetCalcMatrix(
src,
camera,
parentMatrix
).calc;
let skeleton = this.skeleton;
const skeleton = this.skeleton;
skeleton.x = transform.tx;
skeleton.y = transform.ty;
skeleton.scaleX = transform.scaleX;
skeleton.scaleY = transform.scaleY;
let root = skeleton.getRootBone()!;
const root = skeleton.getRootBone() as Bone;
root.applied.rotation = -MathUtils.radiansToDegrees * transform.rotationNormalized;
this.skeleton.updateWorldTransform(Physics.update);

View File

@ -1,14 +1,47 @@
export * from "./require-shim.js"
export * from "./SpinePlugin.js"
export * from "./SpineGameObject.js"
export * from "./mixins.js"
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated July 28, 2023. Replaces all prior versions.
*
* Copyright (c) 2013-2023, 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.
*
* THE SPINE RUNTIMES ARE 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 THE
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
export * from "@esotericsoftware/spine-core";
export * from "@esotericsoftware/spine-webgl";
import { SpineGameObjectConfig, SpinePlugin } from "./SpinePlugin.js";
export * from "./mixins.js"
export * from "./require-shim.js"
export * from "./SpineGameObject.js"
export * from "./SpinePlugin.js"
import { type SpineGameObjectConfig, SpinePlugin } from "./SpinePlugin.js";
// biome-ignore lint/suspicious/noExplicitAny: need to add spine to window
(window as any).spine = { SpinePlugin: SpinePlugin };
// biome-ignore lint/suspicious/noExplicitAny: need to add spine to window
(window as any)["spine.SpinePlugin"] = SpinePlugin;
import { SpineGameObject, SpineGameObjectBoundsProvider } from "./SpineGameObject.js";
import type { SpineGameObject, SpineGameObjectBoundsProvider } from "./SpineGameObject.js";
declare global {
namespace Phaser.Loader {

View File

@ -24,7 +24,9 @@ SOFTWARE.
// Adapted from https://github.com/agogpixel/phaser3-ts-utils/tree/main
let components = (Phaser.GameObjects.Components as any);
// biome-ignore-all lint: ignore biome for this file
const components = (Phaser.GameObjects.Components as any);
export const ComputedSize = components.ComputedSize;
export const Depth = components.Depth;
export const Flip = components.Flip;

View File

@ -27,8 +27,10 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
// biome-ignore-all lint: ignore biome for this file
if (typeof window !== 'undefined' && window.Phaser) {
let prevRequire = window.require;
const prevRequire = window.require;
(window as any).require = (x: string) => {
if (prevRequire) return prevRequire(x);
else if (x === "Phaser") return window.Phaser;

View File

@ -27,10 +27,10 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { SpineTexture } from "./SpineTexture.js";
import type { BlendMode, NumberArrayLike } from "@esotericsoftware/spine-core";
import { DarkTintMesh } from "./darkTintMesh/DarkTintMesh.js";
import type { ISlotMesh } from "./Spine.js";
import { SpineTexture } from "./SpineTexture.js";
export class DarkSlotMesh extends DarkTintMesh implements ISlotMesh {
public name: string = "";
@ -66,8 +66,8 @@ export class DarkSlotMesh extends DarkTintMesh implements ISlotMesh {
let vertIndex = 0;
let textureCoordData = textureCoord.data;
let vertexCoordData = vertexCoord.data;
const textureCoordData = textureCoord.data;
const vertexCoordData = vertexCoord.data;
for (let i = 0; i < finalVerticesLength; i += darkTint ? 12 : 8) {
let auxi = i;

View File

@ -27,11 +27,11 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { SpineTexture } from "./SpineTexture.js";
import type { BlendMode, NumberArrayLike } from "@esotericsoftware/spine-core";
import type { ISlotMesh } from "./Spine.js";
import { Mesh, MeshGeometry, MeshMaterial } from "@pixi/mesh";
import { Texture } from "@pixi/core";
import { Mesh, MeshGeometry, MeshMaterial } from "@pixi/mesh";
import type { ISlotMesh } from "./Spine.js";
import { SpineTexture } from "./SpineTexture.js";
export class SlotMesh extends Mesh implements ISlotMesh {
public name: string = "";
@ -74,8 +74,8 @@ export class SlotMesh extends Mesh implements ISlotMesh {
let vertIndex = 0;
let textureCoordData = textureCoord.data;
let vertexCoordData = vertexCoord.data;
const textureCoordData = textureCoord.data;
const vertexCoordData = vertexCoord.data;
for (let i = 0; i < finalVerticesLength; i += darkTint ? 12 : 8) {
let auxi = i;

View File

@ -40,7 +40,7 @@ import {
Skeleton,
SkeletonBinary,
SkeletonClipping,
type SkeletonData,
SkeletonData,
SkeletonJson,
Skin,
Utils,
@ -329,10 +329,12 @@ export class Spine extends Container {
private _boundsSpineID = -1;
private _boundsSpineDirty = true;
constructor (options: SpineOptions | SpineFromOptions) {
constructor (options: SkeletonData | SpineOptions | SpineFromOptions) {
super();
if ("skeleton" in options)
if (options instanceof SkeletonData)
options = { skeletonData: options };
else if ("skeleton" in options)
options = new.target.createOptions(options);
const { autoUpdate = true, boundsProvider, darkTint, skeletonData } = options;

View File

@ -27,12 +27,12 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import type { AnimationStateListener } from "@esotericsoftware/spine-core";
import { ClippingAttachment, MeshAttachment, PathAttachment, RegionAttachment, SkeletonBounds } from "@esotericsoftware/spine-core";
import { Container } from "@pixi/display";
import { Graphics } from "@pixi/graphics";
import { Text } from "@pixi/text";
import type { Spine } from "./Spine.js";
import type { AnimationStateListener } from "@esotericsoftware/spine-core";
import { ClippingAttachment, MeshAttachment, PathAttachment, RegionAttachment, SkeletonBounds } from "@esotericsoftware/spine-core";
/**
* Make a class that extends from this interface to create your own debug renderer.
@ -159,9 +159,10 @@ export class SpineDebugRenderer implements ISpineDebugRenderer {
debugDisplayObjects.parentDebugContainer.zIndex = 9999999;
// Disable screen reader and mouse input on debug objects.
// biome-ignore lint/suspicious/noExplicitAny: this prop is available in later versions
(debugDisplayObjects.parentDebugContainer as any).accessibleChildren = false;
(debugDisplayObjects.parentDebugContainer as any).eventMode = "none";
(debugDisplayObjects.parentDebugContainer as any).interactiveChildren = false;
(debugDisplayObjects.parentDebugContainer).eventMode = "none";
(debugDisplayObjects.parentDebugContainer).interactiveChildren = false;
spine.addChild(debugDisplayObjects.parentDebugContainer);

View File

@ -28,15 +28,15 @@
*****************************************************************************/
import { BlendMode, Texture, TextureFilter, TextureWrap } from "@esotericsoftware/spine-core";
import type { BaseTexture as PixiBaseTexture, BaseImageResource } from "@pixi/core";
import { Texture as PixiTexture, SCALE_MODES, MIPMAP_MODES, WRAP_MODES, BLEND_MODES } from "@pixi/core";
import type { BaseImageResource, BaseTexture as PixiBaseTexture } from "@pixi/core";
import { BLEND_MODES, MIPMAP_MODES, Texture as PixiTexture, SCALE_MODES, WRAP_MODES } from "@pixi/core";
export class SpineTexture extends Texture {
private static textureMap: Map<PixiBaseTexture, SpineTexture> = new Map<PixiBaseTexture, SpineTexture>();
public static from (texture: PixiBaseTexture): SpineTexture {
if (SpineTexture.textureMap.has(texture)) {
return SpineTexture.textureMap.get(texture)!;
return SpineTexture.textureMap.get(texture) as SpineTexture;
}
return new SpineTexture(texture);
}
@ -45,7 +45,7 @@ export class SpineTexture extends Texture {
private constructor (image: PixiBaseTexture) {
// Todo: maybe add error handling if you feed a video texture to spine?
super((image.resource as BaseImageResource).source as any);
super((image.resource as BaseImageResource).source);
this.texture = PixiTexture.from(image);
}

View File

@ -28,12 +28,11 @@
*****************************************************************************/
import { TextureAtlas } from "@esotericsoftware/spine-core";
import { SpineTexture } from "../SpineTexture.js";
import type { AssetExtension, Loader, ResolvedAsset, UnresolvedAsset } from "@pixi/assets";
import { Assets, copySearchParams } from "@pixi/assets";
import { LoaderParserPriority, checkExtension } from "@pixi/assets";
import { Assets, checkExtension, copySearchParams, LoaderParserPriority } from "@pixi/assets";
import type { Texture } from "@pixi/core";
import { ALPHA_MODES, ExtensionType, settings, utils, BaseTexture, extensions } from "@pixi/core";
import { ALPHA_MODES, BaseTexture, ExtensionType, extensions, settings, utils } from "@pixi/core";
import { SpineTexture } from "../SpineTexture.js";
type RawAtlas = string;
@ -75,7 +74,7 @@ const spineTextureAtlasLoader: AssetExtension<RawAtlas | TextureAtlas, ISpineAtl
},
testParse (asset: unknown, options: ResolvedAsset): Promise<boolean> {
const isExtensionRight = checkExtension(options.src!, ".atlas");
const isExtensionRight = checkExtension(options.src as string, ".atlas");
const isString = typeof asset === "string";
const isExplicitLoadParserSet = options.loadParser === loaderName;
@ -111,7 +110,7 @@ const spineTextureAtlasLoader: AssetExtension<RawAtlas | TextureAtlas, ISpineAtl
// setting preferCreateImageBitmap to false for loadTextures loader to allow loading PMA images
let oldPreferCreateImageBitmap = true;
for (const parser of loader.parsers) {
if (parser.name == "loadTextures") {
if (parser.name === "loadTextures") {
oldPreferCreateImageBitmap = parser.config?.preferCreateImageBitmap;
break;
}
@ -149,6 +148,7 @@ extensions.add(spineTextureAtlasLoader);
export interface ISpineAtlasMetadata {
// If you are downloading an .atlas file, this metadata will go to the Texture loader
// biome-ignore lint/suspicious/noExplicitAny: user can pass any
imageMetadata?: any;
// If you already have atlas pages loaded as pixi textures and want to use that to create the atlas, you can pass them here
images?: BaseTexture | string | Record<string, BaseTexture | string>;

View File

@ -28,18 +28,21 @@
*****************************************************************************/
import type { AssetExtension, ResolvedAsset } from "@pixi/assets";
import { LoaderParserPriority, checkExtension } from "@pixi/assets";
import { ExtensionType, settings, extensions } from "@pixi/core";
import { checkExtension, LoaderParserPriority } from "@pixi/assets";
import { ExtensionType, extensions, settings } from "@pixi/core";
// biome-ignore lint/suspicious/noExplicitAny: any until we have a schema
type SkeletonJsonAsset = any;
type SkeletonBinaryAsset = Uint8Array;
const loaderName = "spineSkeletonLoader";
// biome-ignore lint/suspicious/noExplicitAny: can receive any
function isJson (resource: any): resource is SkeletonJsonAsset {
return resource.hasOwnProperty("bones");
}
// biome-ignore lint/suspicious/noExplicitAny: can receive any
function isBuffer (resource: any): resource is SkeletonBinaryAsset {
return resource instanceof Uint8Array;
}
@ -67,8 +70,8 @@ const spineLoaderExtension: AssetExtension<SkeletonJsonAsset | SkeletonBinaryAss
return buffer;
},
testParse (asset: unknown, options: ResolvedAsset): Promise<boolean> {
const isJsonSpineModel = checkExtension(options.src!, ".json") && isJson(asset);
const isBinarySpineModel = checkExtension(options.src!, ".skel") && isBuffer(asset);
const isJsonSpineModel = checkExtension(options.src as string, ".json") && isJson(asset);
const isBinarySpineModel = checkExtension(options.src as string, ".skel") && isBuffer(asset);
const isExplicitLoadParserSet = options.loadParser === loaderName;
return Promise.resolve(isJsonSpineModel || isBinarySpineModel || isExplicitLoadParserSet);

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Geometry, Buffer, TYPES } from "@pixi/core";
import { Buffer, Geometry, TYPES } from "@pixi/core";
/**
* Geometry used to batch standard PIXI content (e.g. Mesh, Sprite, Graphics objects).

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Geometry, Buffer, TYPES } from "@pixi/core";
import { Buffer, Geometry, TYPES } from "@pixi/core";
/**
* Geometry used to batch standard PIXI content (e.g. Mesh, Sprite, Graphics objects).

View File

@ -28,7 +28,7 @@
*****************************************************************************/
import type { ColorSource } from "@pixi/core";
import { Shader, TextureMatrix, Color, Texture, Matrix, Program } from "@pixi/core";
import { Color, Matrix, Program, Shader, Texture, TextureMatrix } from "@pixi/core";
const vertex = `
attribute vec2 aVertexPosition;
@ -163,7 +163,7 @@ export class DarkTintMaterial extends Shader {
this._colorDirty = true;
}
public get tint (): ColorSource {
return this._tintColor.value!;
return this._tintColor.value as ColorSource;
}
public set darkTint (value: ColorSource) {
@ -176,7 +176,7 @@ export class DarkTintMaterial extends Shader {
this._colorDirty = true;
}
public get darkTint (): ColorSource {
return this._darkTintColor.value!;
return this._darkTintColor.value as ColorSource;
}
public get tintValue (): number {

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import type { Texture, ColorSource, Renderer, BLEND_MODES } from "@pixi/core";
import type { BLEND_MODES, ColorSource, Renderer, Texture } from "@pixi/core";
import { Mesh } from "@pixi/mesh";
import { DarkTintGeometry } from "./DarkTintGeom.js";
import { DarkTintMaterial } from "./DarkTintMaterial.js";
@ -60,11 +60,11 @@ export class DarkTintMesh extends Mesh<DarkTintMaterial> {
}
public set darkTint (value: ColorSource | null) {
(this.shader as unknown as DarkTintMaterial).darkTint = value!;
this.shader.darkTint = value as ColorSource;
}
public get darkTintValue (): number {
return (this.shader as unknown as DarkTintMaterial).darkTintValue;
return this.shader.darkTintValue;
}
// eslint-disable-next-line @typescript-eslint/naming-convention

View File

@ -27,10 +27,10 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import type { IDarkTintElement } from "./DarkTintMesh.js";
import { DarkTintBatchGeometry } from "./DarkTintBatchGeom.js";
import type { ExtensionMetadata, Renderer, ViewableBuffer } from "@pixi/core";
import { extensions, BatchRenderer, ExtensionType, BatchShaderGenerator, Color } from "@pixi/core";
import { BatchRenderer, BatchShaderGenerator, Color, ExtensionType, extensions } from "@pixi/core";
import { DarkTintBatchGeometry } from "./DarkTintBatchGeom.js";
import type { IDarkTintElement } from "./DarkTintMesh.js";
const vertex = `
precision highp float;

View File

@ -1,18 +1,48 @@
export * from './require-shim.js';
export * from './Spine.js';
export * from './SpineDebugRenderer.js';
export * from './SpineTexture.js';
export * from './SlotMesh.js';
export * from './DarkSlotMesh.js';
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated September 24, 2021. Replaces all prior versions.
*
* Copyright (c) 2013-2021, 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.
*
* THE SPINE RUNTIMES ARE 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import './require-shim.js';
import './assets/AtlasLoader.js'; // Side effects install the loaders into pixi
import './assets/SkeletonLoader.js'; // Side effects install the loaders into pixi
export * from "@esotericsoftware/spine-core";
export * from './assets/AtlasLoader.js';
export * from './assets/SkeletonLoader.js';
export * from './DarkSlotMesh.js';
export * from './darkTintMesh/DarkTintBatchGeom.js';
export * from './darkTintMesh/DarkTintGeom.js';
export * from './darkTintMesh/DarkTintMaterial.js';
export * from './darkTintMesh/DarkTintMesh.js';
export * from './darkTintMesh/DarkTintRenderer.js';
export * from "@esotericsoftware/spine-core";
export * from './SlotMesh.js';
export * from './Spine.js';
export * from './SpineDebugRenderer.js';
export * from './SpineTexture.js';
import './assets/AtlasLoader.js'; // Side effects install the loaders into pixi
import './assets/SkeletonLoader.js'; // Side effects install the loaders into pixi

View File

@ -27,8 +27,10 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
// biome-ignore-all lint: ignore biome for this file
if (typeof window !== 'undefined' && (window as any).PIXI) {
let prevRequire = window.require;
const prevRequire = window.require;
(window as any).require = (x: string) => {
if (prevRequire) return prevRequire(x);
else if (x.startsWith("@pixi/")) return (window as any).PIXI;

View File

@ -27,9 +27,9 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { AttachmentCacheData, Spine } from './Spine.js';
import type { Batch, Batcher, BLEND_MODES, DefaultBatchableMeshElement, Matrix, Texture, Topology } from 'pixi.js';
import type { AttachmentCacheData, ClippedData, Spine } from './Spine.js';
export class BatchableSpineSlot implements DefaultBatchableMeshElement {
indexOffset = 0;
@ -112,13 +112,13 @@ export class BatchableSpineSlot implements DefaultBatchableMeshElement {
this.data = data;
if (data.clipped) {
const clippedData = data.clippedData;
const clippedData = data.clippedData as ClippedData;
this.indexSize = clippedData!.indicesCount;
this.attributeSize = clippedData!.vertexCount;
this.positions = clippedData!.vertices;
this.indices = clippedData!.indices;
this.uvs = clippedData!.uvs;
this.indexSize = clippedData.indicesCount;
this.attributeSize = clippedData.vertexCount;
this.positions = clippedData.vertices;
this.indices = clippedData.indices;
this.uvs = clippedData.uvs;
}
else {
this.indexSize = data.indices.length;

View File

@ -43,7 +43,7 @@ import {
SkeletonBinary,
SkeletonBounds,
SkeletonClipping,
type SkeletonData,
SkeletonData,
SkeletonJson,
Skin,
type Slot,
@ -261,6 +261,14 @@ export interface SpineEvents {
start: [trackEntry: TrackEntry];
}
export interface ClippedData {
vertices: Float32Array;
uvs: Float32Array;
indices: Uint16Array;
vertexCount: number;
indicesCount: number;
}
export interface AttachmentCacheData {
id: string;
clipped: boolean;
@ -272,13 +280,7 @@ export interface AttachmentCacheData {
darkTint: boolean;
skipRender: boolean;
texture: Texture;
clippedData?: {
vertices: Float32Array;
uvs: Float32Array;
indices: Uint16Array;
vertexCount: number;
indicesCount: number;
};
clippedData?: ClippedData;
}
interface SlotsToClipping {
@ -385,10 +387,12 @@ export class Spine extends ViewContainer {
}
private hasNeverUpdated = true;
constructor (options: SpineOptions | SpineFromOptions) {
constructor (options: SkeletonData | SpineOptions | SpineFromOptions) {
super({});
if ("skeleton" in options)
if (options instanceof SkeletonData)
options = { skeletonData: options };
else if ("skeleton" in options)
options = new.target.createOptions(options);
this.allowChildren = true;
@ -579,7 +583,7 @@ export class Spine extends ViewContainer {
const clippingAttachment = slotClipping.pose.attachment as ClippingAttachment;
// create the pixi mask, only the first time and if the clipped slot is the first one clipped by this currentClippingSlot
let mask = currentClippingSlot.mask as Graphics;
let mask = currentClippingSlot.mask;
if (!mask) {
mask = maskPool.obtain();
currentClippingSlot.mask = mask;
@ -1030,11 +1034,11 @@ export class Spine extends ViewContainer {
Ticker.shared.remove(this.internalUpdate, this);
this.state.clearListeners();
this.debug = undefined;
this.skeleton = null as any;
this.state = null as any;
(this._slotsObject as any) = null;
(this.skeleton as unknown) = null;
(this.state as unknown) = null;
(this._slotsObject as unknown) = null;
(this.attachmentCacheData as unknown) = null;
this._lastAttachments.length = 0;
this.attachmentCacheData = null as any;
}
/** Converts a point from the skeleton coordinate system to the Pixi world coordinate system. */

View File

@ -27,8 +27,8 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Container, Graphics, Text } from 'pixi.js';
import { Spine } from './Spine.js';
import type { AnimationStateListener } from '@esotericsoftware/spine-core';
import {
ClippingAttachment,
MeshAttachment,
@ -36,8 +36,8 @@ import {
RegionAttachment,
SkeletonBounds
} from '@esotericsoftware/spine-core';
import type { AnimationStateListener } from '@esotericsoftware/spine-core';
import { Container, Graphics, Text } from 'pixi.js';
import type { Spine } from './Spine.js';
/**
* Make a class that extends from this interface to create your own debug renderer.
@ -171,12 +171,12 @@ export class SpineDebugRenderer implements ISpineDebugRenderer {
debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.pathsLine);
debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.eventText);
(debugDisplayObjects.parentDebugContainer as any).zIndex = 9999999;
debugDisplayObjects.parentDebugContainer.zIndex = 9999999;
// Disable screen reader and mouse input on debug objects.
(debugDisplayObjects.parentDebugContainer as any).accessibleChildren = false;
(debugDisplayObjects.parentDebugContainer as any).eventMode = 'none';
(debugDisplayObjects.parentDebugContainer as any).interactiveChildren = false;
debugDisplayObjects.parentDebugContainer.accessibleChildren = false;
debugDisplayObjects.parentDebugContainer.eventMode = 'none';
debugDisplayObjects.parentDebugContainer.interactiveChildren = false;
spine.addChild(debugDisplayObjects.parentDebugContainer);

View File

@ -150,6 +150,7 @@ export class SpinePipe implements RenderPipe<Spine> {
if (!skipRender) {
container.includeInBuild = true;
// See https://github.com/pixijs/pixijs/blob/b4c050a791fe65e979e467c9cba2bda0c01a1c35/src/scene/container/utils/collectAllRenderables.ts#L28
// biome-ignore lint/style/noNonNullAssertion: it was in pixi code
container.collectRenderables(instructionSet, this.renderer, null!);
}
@ -185,13 +186,13 @@ export class SpinePipe implements RenderPipe<Spine> {
}
destroyRenderable (spine: Spine) {
this.gpuSpineData[spine.uid] = null as any;
(this.gpuSpineData[spine.uid] as unknown) = null;
spine.off('destroyed', this._destroyRenderableBound);
}
destroy () {
this.gpuSpineData = null as any;
this.renderer = null as any;
(this.gpuSpineData as unknown) = null;
(this.renderer as unknown) = null;
}
private _getSpineData (spine: Spine): GpuSpineDataElement {

View File

@ -27,10 +27,9 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Texture as PixiTexture } from 'pixi.js';
import { BlendMode, Texture, TextureFilter, TextureWrap } from '@esotericsoftware/spine-core';
import type { BLEND_MODES, SCALE_MODE, TextureSource, WRAP_MODE } from 'pixi.js';
import { Texture as PixiTexture } from 'pixi.js';
export class SpineTexture extends Texture {
private static readonly textureMap: Map<TextureSource, SpineTexture> = new Map<TextureSource, SpineTexture>();

View File

@ -120,7 +120,7 @@ const spineTextureAtlasLoader: AssetExtension<RawAtlas | TextureAtlas, ISpineAtl
}
// we will wait for all promises for the textures at the same time at the end.
const textureLoadingPromises: Promise<any>[] = [];
const textureLoadingPromises: Promise<void>[] = [];
// fill the pages
for (const page of retval.pages) {
@ -161,6 +161,7 @@ extensions.add(spineTextureAtlasLoader);
export interface ISpineAtlasMetadata {
// If you are downloading an .atlas file, this metadata will go to the Texture loader
// biome-ignore lint/suspicious/noExplicitAny: user can pass any
imageMetadata?: any;
// If you already have atlas pages loaded as pixi textures
// and want to use that to create the atlas, you can pass them here

View File

@ -37,15 +37,18 @@ import {
type ResolvedAsset
} from 'pixi.js';
// biome-ignore lint/suspicious/noExplicitAny: can receive any
type SkeletonJsonAsset = any;
type SkeletonBinaryAsset = Uint8Array;
const loaderName = "spineSkeletonLoader";
// biome-ignore lint/suspicious/noExplicitAny: can receive any
function isJson (resource: any): resource is SkeletonJsonAsset {
return Object.prototype.hasOwnProperty.call(resource, 'bones');
}
// biome-ignore lint/suspicious/noExplicitAny: can receive any
function isBuffer (resource: any): resource is SkeletonBinaryAsset {
return resource instanceof Uint8Array;
}
@ -74,8 +77,8 @@ const spineLoaderExtension: AssetExtension<SkeletonJsonAsset | SkeletonBinaryAss
return buffer;
},
testParse (asset: unknown, options: ResolvedAsset): Promise<boolean> {
const isJsonSpineModel = checkExtension(options.src!, '.json') && isJson(asset);
const isBinarySpineModel = checkExtension(options.src!, '.skel') && isBuffer(asset);
const isJsonSpineModel = checkExtension(options.src as string, '.json') && isJson(asset);
const isBinarySpineModel = checkExtension(options.src as string, '.skel') && isBuffer(asset);
const isExplicitLoadParserSet = options.parser === loaderName || options.loadParser === loaderName;
return Promise.resolve(isJsonSpineModel || isBinarySpineModel || isExplicitLoadParserSet);

View File

@ -30,11 +30,11 @@
import {
Batcher,
Color,
DefaultBatchableMeshElement,
DefaultBatchableQuadElement,
extensions,
type DefaultBatchableMeshElement,
type DefaultBatchableQuadElement,
ExtensionType,
Shader
extensions,
type Shader
} from 'pixi.js';
import { DarkTintBatchGeometry } from './DarkTintBatchGeometry.js';
import { DarkTintShader } from './DarkTintShader.js';

View File

@ -33,6 +33,7 @@ import './assets/SkeletonLoader.js'; // Side effects install the loaders into pi
import './darktint/DarkTintBatcher.js'; // Side effects install the batcher into pixi
import './SpinePipe.js';
export * from '@esotericsoftware/spine-core';
export * from './assets/AtlasLoader.js';
export * from './assets/SkeletonLoader.js';
export * from './require-shim.js';
@ -40,4 +41,3 @@ export * from './Spine.js';
export * from './SpineDebugRenderer.js';
export * from './SpinePipe.js';
export * from './SpineTexture.js';
export * from '@esotericsoftware/spine-core';

View File

@ -27,6 +27,8 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
// biome-ignore-all lint: ignore biome for this file
if (typeof window !== 'undefined' && (window as any).PIXI) {
const prevRequire = window.require;
(window as any).require = (x: string) => {

View File

@ -27,22 +27,16 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Animation, AnimationState, AnimationStateData, AtlasAttachmentLoader, Bone, Color, Disposable, Downloader, MathUtils, MixBlend, MixDirection, Physics, Skeleton, SkeletonBinary, SkeletonData, SkeletonJson, StringMap, TextureAtlas, TextureFilter, TimeKeeper, TrackEntry, Vector2 } from "@esotericsoftware/spine-core"
import { AssetManager, GLTexture, Input, LoadingScreen, ManagedWebGLRenderingContext, ResizeMode, SceneRenderer, Vector3 } from "@esotericsoftware/spine-webgl"
import { type Animation, AnimationState, AnimationStateData, AtlasAttachmentLoader, type Bone, Color, type Disposable, type Downloader, MathUtils, MixBlend, MixDirection, Physics, Skeleton, SkeletonBinary, type SkeletonData, SkeletonJson, type StringMap, type TextureAtlas, TextureFilter, TimeKeeper, type TrackEntry, Vector2 } from "@esotericsoftware/spine-core"
import { AssetManager, type GLTexture, Input, LoadingScreen, ManagedWebGLRenderingContext, ResizeMode, SceneRenderer, Vector3 } from "@esotericsoftware/spine-webgl"
export interface SpinePlayerConfig {
/* The URL of the skeleton JSON (.json) or binary (.skel) file */
skeleton?: string;
/* @deprecated Use skeleton instead. The URL of the skeleton JSON file (.json). Undefined if binaryUrl is given. */
jsonUrl?: string
/* Optional: The name of a field in the JSON that holds the skeleton data. Default: none */
jsonField?: string
/* @deprecated Use skeleton instead. The URL of the skeleton binary file (.skel). Undefined if jsonUrl is given. */
binaryUrl?: string
/* The scale when loading the skeleton data. Default: 1 */
scale?: number
@ -245,12 +239,12 @@ export class SpinePlayer implements Disposable {
private input?: Input;
constructor (parent: HTMLElement | string, private config: SpinePlayerConfig) {
let parentDom = typeof parent === "string" ? document.getElementById(parent) : parent;
if (parentDom == null) throw new Error("SpinePlayer parent not found: " + parent);
const parentDom = typeof parent === "string" ? document.getElementById(parent) : parent;
if (parentDom == null) throw new Error(`SpinePlayer parent not found: ${parent}`);
this.parent = parentDom;
if (config.showControls === void 0) config.showControls = true;
let controls = config.showControls ? /*html*/`
const controls = config.showControls ? /*html*/`
<div class="spine-player-controls spine-player-popup-parent spine-player-controls-hidden">
<div class="spine-player-timeline"></div>
<div class="spine-player-buttons">
@ -287,8 +281,8 @@ export class SpinePlayer implements Disposable {
this.loadingScreen?.dispose();
this.assetManager?.dispose();
this.context?.dispose();
for (var i = 0; i < this.eventListeners.length; i++) {
var eventListener = this.eventListeners[i];
for (let i = 0; i < this.eventListeners.length; i++) {
const eventListener = this.eventListeners[i];
eventListener.target.removeEventListener(eventListener.event, eventListener.func);
}
this.input?.dispose();
@ -308,12 +302,10 @@ export class SpinePlayer implements Disposable {
private validateConfig (config: SpinePlayerConfig) {
if (!config) throw new Error("A configuration object must be passed to to new SpinePlayer().");
if ((config as any).skelUrl) config.skeleton = (config as any).skelUrl;
if (!config.skeleton && !config.jsonUrl && !config.binaryUrl) throw new Error("A URL must be specified for the skeleton JSON or binary file.");
if (!config.skeleton) throw new Error("A URL must be specified for the skeleton JSON or binary file.");
if (!config.scale) config.scale = 1;
if (!config.atlas && !config.atlasUrl) throw new Error("A URL must be specified for the atlas file.");
if (config.jsonUrl && !config.skeleton) config.skeleton = config.jsonUrl;
if (config.binaryUrl && !config.skeleton) config.skeleton = config.binaryUrl;
if (config.atlasUrl && !config.atlas) config.atlas = config.atlasUrl;
if (!config.backgroundColor) config.backgroundColor = config.alpha ? "00000000" : "000000";
@ -334,9 +326,9 @@ export class SpinePlayer implements Disposable {
regions: false
};
if (config.animations && config.animation && config.animations.indexOf(config.animation) < 0)
throw new Error("Animation '" + config.animation + "' is not in the config animation list: " + toString(config.animations));
throw new Error(`Animation '${config.animation}' is not in the config animation list: ${print(config.animations)}`);
if (config.skins && config.skin && config.skins.indexOf(config.skin) < 0)
throw new Error("Default skin '" + config.skin + "' is not in the config skins list: " + toString(config.skins));
throw new Error(`Default skin '${config.skin}' is not in the config skins list: ${print(config.skins)}`);
if (!config.viewport) config.viewport = {} as any;
if (!config.viewport!.animations) config.viewport!.animations = {};
if (config.viewport!.debugRender === void 0) config.viewport!.debugRender = false;
@ -347,12 +339,12 @@ export class SpinePlayer implements Disposable {
}
private initialize (): HTMLElement | null {
let config = this.config;
let dom = this.dom;
const config = this.config;
const dom = this.dom;
if (!config.alpha) { // Prevents a flash before the first frame is drawn.
let hex = config.backgroundColor!;
this.dom.style.backgroundColor = (hex.charAt(0) == '#' ? hex : "#" + hex).substr(0, 7);
const hex = config.backgroundColor!;
this.dom.style.backgroundColor = (hex.charAt(0) === '#' ? hex : `#${hex}`).substr(0, 7);
}
try {
@ -371,7 +363,7 @@ export class SpinePlayer implements Disposable {
// Load the assets.
this.assetManager = new AssetManager(this.context, "", config.downloader);
if (config.rawDataURIs) {
for (let path in config.rawDataURIs)
for (const path in config.rawDataURIs)
this.assetManager.setRawDataURI(path, config.rawDataURIs[path]);
}
if (config.skeleton!.endsWith(".json"))
@ -386,23 +378,23 @@ export class SpinePlayer implements Disposable {
this.bgFullscreen.setFromString(config.fullScreenBackgroundColor!);
if (config.showControls) {
this.playerControls = dom.children[1] as HTMLElement;
let controls = this.playerControls.children;
let timeline = controls[0] as HTMLElement;
let buttons = controls[1].children;
const controls = this.playerControls.children;
const timeline = controls[0] as HTMLElement;
const buttons = controls[1].children;
this.playButton = buttons[0] as HTMLElement;
let speedButton = buttons[2] as HTMLElement;
const speedButton = buttons[2] as HTMLElement;
this.animationButton = buttons[3] as HTMLElement;
this.skinButton = buttons[4] as HTMLElement;
let settingsButton = buttons[5] as HTMLElement;
let fullscreenButton = buttons[6] as HTMLElement;
let logoButton = buttons[7] as HTMLElement;
const settingsButton = buttons[5] as HTMLElement;
const fullscreenButton = buttons[6] as HTMLElement;
const logoButton = buttons[7] as HTMLElement;
this.timelineSlider = new Slider();
timeline.appendChild(this.timelineSlider.create());
this.timelineSlider.change = (percentage) => {
this.pause();
let animationDuration = this.animationState!.getCurrent(0)!.animation!.duration;
let time = animationDuration * percentage;
const animationDuration = this.animationState!.getCurrent(0)!.animation!.duration;
const time = animationDuration * percentage;
this.animationState!.update(time - this.playTime);
this.animationState!.apply(this.skeleton!);
this.skeleton!.update(time - this.playTime);
@ -420,11 +412,11 @@ export class SpinePlayer implements Disposable {
let oldStyleWidth = this.canvas.style.width, oldStyleHeight = this.canvas.style.height;
let isFullscreen = false;
fullscreenButton.onclick = () => {
let fullscreenChanged = () => {
const fullscreenChanged = () => {
isFullscreen = !isFullscreen;
if (!isFullscreen) {
this.canvas!.style.width = oldWidth + "px";
this.canvas!.style.height = oldHeight + "px";
this.canvas!.style.width = `${oldWidth}px`;
this.canvas!.style.height = `${oldHeight}px`;
this.drawFrame(false);
// Got to reset the style to whatever the user set after the next layouting.
requestAnimationFrame(() => {
@ -434,11 +426,11 @@ export class SpinePlayer implements Disposable {
}
};
let player = dom as any;
const player = dom as any;
player.onfullscreenchange = fullscreenChanged;
player.onwebkitfullscreenchange = fullscreenChanged;
let doc = document as any;
const doc = document as any;
if (doc.fullscreenElement || doc.webkitFullscreenElement || doc.mozFullScreenElement || doc.msFullscreenElement) {
if (doc.exitFullscreen) doc.exitFullscreen();
else if (doc.mozCancelFullScreen) doc.mozCancelFullScreen();
@ -465,18 +457,18 @@ export class SpinePlayer implements Disposable {
if (this.error) return;
if (this.assetManager!.hasErrors())
this.showError("Error: Assets could not be loaded.\n" + toString(this.assetManager!.getErrors()));
this.showError(`Error: Assets could not be loaded.\n${print(this.assetManager!.getErrors())}`);
let config = this.config;
const config = this.config;
// Configure filtering, don't use mipmaps in WebGL1 if the atlas page is non-POT
let atlas = this.assetManager!.require(config.atlas!) as TextureAtlas;
let gl = this.context!.gl, anisotropic = gl.getExtension("EXT_texture_filter_anisotropic");
let isWebGL1 = gl.getParameter(gl.VERSION).indexOf("WebGL 1.0") != -1;
for (let page of atlas.pages) {
const atlas = this.assetManager!.require(config.atlas!) as TextureAtlas;
const gl = this.context!.gl, anisotropic = gl.getExtension("EXT_texture_filter_anisotropic");
const isWebGL1 = gl.getParameter(gl.VERSION).indexOf("WebGL 1.0") !== -1;
for (const page of atlas.pages) {
let minFilter = page.minFilter;
var useMipMaps: boolean = config.mipmaps!;
var isPOT = MathUtils.isPowerOfTwo(page.width) && MathUtils.isPowerOfTwo(page.height);
let useMipMaps: boolean = config.mipmaps!;
const isPOT = MathUtils.isPowerOfTwo(page.width) && MathUtils.isPowerOfTwo(page.height);
if (isWebGL1 && !isPOT) useMipMaps = false;
if (useMipMaps) {
@ -487,7 +479,7 @@ export class SpinePlayer implements Disposable {
minFilter = TextureFilter.Linear; // Don't use mipmaps without anisotropic.
page.texture!.setFilters(minFilter, TextureFilter.Nearest);
}
if (minFilter != TextureFilter.Nearest && minFilter != TextureFilter.Linear) (page.texture as GLTexture).update(true);
if (minFilter !== TextureFilter.Nearest && minFilter !== TextureFilter.Linear) (page.texture as GLTexture).update(true);
}
// Load skeleton data.
@ -499,7 +491,7 @@ export class SpinePlayer implements Disposable {
if (!data) throw new Error("Empty JSON data.");
if (config.jsonField) {
data = data[config.jsonField];
if (!data) throw new Error("JSON field does not exist: " + config.jsonField);
if (!data) throw new Error(`JSON field does not exist: ${config.jsonField}`);
}
loader = new SkeletonJson(attachmentLoader);
} else {
@ -513,7 +505,7 @@ export class SpinePlayer implements Disposable {
return;
}
this.skeleton = new Skeleton(skeletonData);
let stateData = new AnimationStateData(skeletonData);
const stateData = new AnimationStateData(skeletonData);
stateData.defaultMix = config.defaultMix!;
this.animationState = new AnimationState(stateData);
@ -560,8 +552,8 @@ export class SpinePlayer implements Disposable {
if (config.showControls) {
// Hide skin and animation if there's only the default skin / no animation
if (skeletonData.skins.length == 1 || (config.skins && config.skins.length == 1)) this.skinButton!.classList.add("spine-player-hidden");
if (skeletonData.animations.length == 1 || (config.animations && config.animations.length == 1)) this.animationButton!.classList.add("spine-player-hidden");
if (skeletonData.skins.length === 1 || (config.skins && config.skins.length === 1)) this.skinButton!.classList.add("spine-player-hidden");
if (skeletonData.animations.length === 1 || (config.animations && config.animations.length === 1)) this.animationButton!.classList.add("spine-player-hidden");
}
if (config.success) config.success(this);
@ -590,30 +582,30 @@ export class SpinePlayer implements Disposable {
}
private setupInput () {
let config = this.config;
let controlBones = config.controlBones!;
const config = this.config;
const controlBones = config.controlBones!;
if (!controlBones.length && !config.showControls) return;
let selectedBones = this.selectedBones = new Array<Bone | null>(controlBones.length);
let canvas = this.canvas!;
const selectedBones = this.selectedBones = new Array<Bone | null>(controlBones.length);
const canvas = this.canvas!;
let target: Bone | null = null;
let offset = new Vector2();
let coords = new Vector3();
let mouse = new Vector3();
let position = new Vector2();
let skeleton = this.skeleton!;
let renderer = this.sceneRenderer!;
const offset = new Vector2();
const coords = new Vector3();
const mouse = new Vector3();
const position = new Vector2();
const skeleton = this.skeleton!;
const renderer = this.sceneRenderer!;
if (config.interactive) {
let closest = function (x: number, y: number): Bone | null {
const closest = (x: number, y: number): Bone | null => {
mouse.set(x, canvas.clientHeight - y, 0)
offset.x = offset.y = 0;
let bestDistance = 24, index = 0;
let best: Bone | null = null;
for (let i = 0; i < controlBones.length; i++) {
selectedBones[i] = null;
let bone = skeleton.findBone(controlBones[i]);
const bone = skeleton.findBone(controlBones[i]);
if (!bone) continue;
let distance = renderer.camera.worldToScreen(
const distance = renderer.camera.worldToScreen(
coords.set(bone.applied.worldX, bone.applied.worldY, 0),
canvas.clientWidth, canvas.clientHeight).distance(mouse);
if (distance < bestDistance) {
@ -668,27 +660,27 @@ export class SpinePlayer implements Disposable {
});
this.addEventListener(document, "touchmove", (ev: UIEvent) => {
if (ev instanceof TouchEvent) {
let touches = ev.changedTouches;
const touches = ev.changedTouches;
if (touches.length) {
let touch = touches[0];
const touch = touches[0];
handleHover(touch.clientX, touch.clientY);
}
}
});
let overlap = (mouseX: number, mouseY: number, rect: DOMRect | ClientRect): boolean => {
let x = mouseX - rect.left, y = mouseY - rect.top;
const overlap = (mouseX: number, mouseY: number, rect: DOMRect | ClientRect): boolean => {
const x = mouseX - rect.left, y = mouseY - rect.top;
return x >= 0 && x <= rect.width && y >= 0 && y <= rect.height;
}
let mouseOverControls = true, mouseOverCanvas = false;
let handleHover = (mouseX: number, mouseY: number) => {
let popup = findWithClass(this.dom, "spine-player-popup");
const handleHover = (mouseX: number, mouseY: number) => {
const popup = findWithClass(this.dom, "spine-player-popup");
mouseOverControls = overlap(mouseX, mouseY, this.playerControls!.getBoundingClientRect());
mouseOverCanvas = overlap(mouseX, mouseY, canvas.getBoundingClientRect());
clearTimeout(this.cancelId);
let hide = !popup && !mouseOverControls && !mouseOverCanvas && !this.paused;
const hide = !popup && !mouseOverControls && !mouseOverCanvas && !this.paused;
if (hide)
this.playerControls!.classList.add("spine-player-controls-hidden");
else
@ -704,7 +696,7 @@ export class SpinePlayer implements Disposable {
play () {
this.paused = false;
let config = this.config;
const config = this.config;
if (config.showControls) {
this.cancelId = setTimeout(() => {
if (!this.paused) this.playerControls!.classList.add("spine-player-controls-hidden");
@ -747,17 +739,17 @@ export class SpinePlayer implements Disposable {
/* Sets the viewport for the specified animation. */
setViewport (animation: string | Animation): Animation {
if (typeof animation == "string") {
let foundAnimation = this.skeleton!.data.findAnimation(animation);
if (!foundAnimation) throw new Error("Animation not found: " + animation);
if (typeof animation === "string") {
const foundAnimation = this.skeleton!.data.findAnimation(animation);
if (!foundAnimation) throw new Error(`Animation not found: ${animation}`);
animation = foundAnimation;
}
this.previousViewport = this.currentViewport;
// Determine the base viewport.
let globalViewport = this.config.viewport!;
let viewport = this.currentViewport = {
const globalViewport = this.config.viewport!;
const viewport = this.currentViewport = {
clip: globalViewport.clip,
padLeft: globalViewport.padLeft !== void 0 ? globalViewport.padLeft : "10%",
padRight: globalViewport.padRight !== void 0 ? globalViewport.padRight : "10%",
@ -773,7 +765,7 @@ export class SpinePlayer implements Disposable {
this.calculateAnimationViewport(animation, viewport);
// Override with the animation specific viewport for the final result.
let userAnimViewport = this.config.viewport!.animations![animation.name];
const userAnimViewport = this.config.viewport!.animations![animation.name];
if (userAnimViewport) {
if (userAnimViewport.x !== void 0 && userAnimViewport.y !== void 0 && userAnimViewport.width && userAnimViewport.height) {
viewport.x = userAnimViewport.x;
@ -809,7 +801,7 @@ export class SpinePlayer implements Disposable {
let steps = 100, stepTime = animation.duration ? animation.duration / steps : 0, time = 0;
let minX = 100000000, maxX = -100000000, minY = 100000000, maxY = -100000000;
let offset = new Vector2(), size = new Vector2();
const offset = new Vector2(), size = new Vector2();
const tempArray = new Array<number>(2);
for (let i = 0; i < steps; i++, time += stepTime) {
@ -817,13 +809,13 @@ export class SpinePlayer implements Disposable {
this.skeleton!.updateWorldTransform(Physics.update);
this.skeleton!.getBounds(offset, size, tempArray, this.sceneRenderer!.skeletonRenderer.getSkeletonClipping());
if (!isNaN(offset.x) && !isNaN(offset.y) && !isNaN(size.x) && !isNaN(size.y)) {
if (!Number.isNaN(offset.x) && !Number.isNaN(offset.y) && !Number.isNaN(size.x) && !Number.isNaN(size.y)) {
minX = Math.min(offset.x, minX);
maxX = Math.max(offset.x + size.x, maxX);
minY = Math.min(offset.y, minY);
maxY = Math.max(offset.y + size.y, maxY);
} else
this.showError("Animation bounds are invalid: " + animation.name);
this.showError(`Animation bounds are invalid: ${animation.name}`);
}
viewport.x = minX;
@ -838,20 +830,20 @@ export class SpinePlayer implements Disposable {
if (this.disposed) return;
if (requestNextFrame && !this.stopRequestAnimationFrame) requestAnimationFrame(() => this.drawFrame());
let doc = document as any;
let isFullscreen = doc.fullscreenElement || doc.webkitFullscreenElement || doc.mozFullScreenElement || doc.msFullscreenElement;
let bg = isFullscreen ? this.bgFullscreen : this.bg;
const doc = document as any;
const isFullscreen = doc.fullscreenElement || doc.webkitFullscreenElement || doc.mozFullScreenElement || doc.msFullscreenElement;
const bg = isFullscreen ? this.bgFullscreen : this.bg;
this.time.update();
let delta = this.time.delta;
const delta = this.time.delta;
// Load the skeleton if the assets are ready.
let loading = !this.assetManager!.isLoadingComplete();
const loading = !this.assetManager!.isLoadingComplete();
if (!this.skeleton && !loading) this.loadSkeleton();
let skeleton = this.skeleton!;
let config = this.config!;
const skeleton = this.skeleton!;
const config = this.config!;
if (skeleton) {
let playDelta = this.paused ? 0 : delta * this.speed;
const playDelta = this.paused ? 0 : delta * this.speed;
if (config.frame) config.frame(this, playDelta);
// Update animation time and pose the skeleton.
@ -866,10 +858,10 @@ export class SpinePlayer implements Disposable {
if (config.showControls) {
this.playTime += playDelta;
let entry = this.animationState!.getCurrent(0);
const entry = this.animationState!.getCurrent(0);
if (entry) {
let duration = entry.animation!.duration;
while (this.playTime >= duration && duration != 0)
const duration = entry.animation!.duration;
while (this.playTime >= duration && duration !== 0)
this.playTime -= duration;
this.playTime = Math.max(0, Math.min(this.playTime, duration));
this.timelineSlider!.setValue(this.playTime / duration);
@ -878,19 +870,19 @@ export class SpinePlayer implements Disposable {
}
// Determine the viewport.
let viewport = this.viewport;
const viewport = this.viewport;
viewport.x = this.currentViewport.x - (this.currentViewport.padLeft as number);
viewport.y = this.currentViewport.y - (this.currentViewport.padBottom as number);
viewport.width = this.currentViewport.width + (this.currentViewport.padLeft as number) + (this.currentViewport.padRight as number);
viewport.height = this.currentViewport.height + (this.currentViewport.padBottom as number) + (this.currentViewport.padTop as number);
if (this.previousViewport) {
let transitionAlpha = (performance.now() - this.viewportTransitionStart) / 1000 / config.viewport!.transitionTime!;
const transitionAlpha = (performance.now() - this.viewportTransitionStart) / 1000 / config.viewport!.transitionTime!;
if (transitionAlpha < 1) {
let x = this.previousViewport.x - (this.previousViewport.padLeft as number);
let y = this.previousViewport.y - (this.previousViewport.padBottom as number);
let width = this.previousViewport.width + (this.previousViewport.padLeft as number) + (this.previousViewport.padRight as number);
let height = this.previousViewport.height + (this.previousViewport.padBottom as number) + (this.previousViewport.padTop as number);
const x = this.previousViewport.x - (this.previousViewport.padLeft as number);
const y = this.previousViewport.y - (this.previousViewport.padBottom as number);
const width = this.previousViewport.width + (this.previousViewport.padLeft as number) + (this.previousViewport.padRight as number);
const height = this.previousViewport.height + (this.previousViewport.padBottom as number) + (this.previousViewport.padTop as number);
viewport.x = x + (viewport.x - x) * transitionAlpha;
viewport.y = y + (viewport.y - y) * transitionAlpha;
viewport.width = width + (viewport.width - width) * transitionAlpha;
@ -898,7 +890,7 @@ export class SpinePlayer implements Disposable {
}
}
let renderer = this.sceneRenderer!;
const renderer = this.sceneRenderer!;
renderer.camera.zoom = this.canvas!.height / this.canvas!.width > viewport.height / viewport.width
? viewport.width / this.canvas!.width : viewport.height / this.canvas!.height;
renderer.camera.position.x = viewport.x + viewport.width / 2;
@ -908,7 +900,7 @@ export class SpinePlayer implements Disposable {
renderer.resize(this.currentViewport.clip ? ResizeMode.FitClip : ResizeMode.Fit, viewport.width, viewport.height);
// Clear the screen.
let gl = this.context!.gl;
const gl = this.context!.gl;
gl.clearColor(bg.r, bg.g, bg.b, bg.a);
gl.clear(gl.COLOR_BUFFER_BIT);
@ -917,9 +909,9 @@ export class SpinePlayer implements Disposable {
renderer.begin();
// Draw the background image.
let bgImage = config.backgroundImage;
const bgImage = config.backgroundImage;
if (bgImage) {
let texture = this.assetManager!.require(bgImage.url) as GLTexture;
const texture = this.assetManager!.require(bgImage.url) as GLTexture;
if (bgImage.x !== void 0 && bgImage.y !== void 0 && bgImage.width && bgImage.height)
renderer.drawTexture(texture, bgImage.x, bgImage.y, bgImage.width, bgImage.height);
else
@ -940,15 +932,15 @@ export class SpinePlayer implements Disposable {
}
// Draw the control bones.
let controlBones = config.controlBones!;
const controlBones = config.controlBones!;
if (controlBones.length) {
let selectedBones = this.selectedBones;
const selectedBones = this.selectedBones;
gl.lineWidth(2);
for (let i = 0; i < controlBones.length; i++) {
let bone = skeleton.findBone(controlBones[i]);
const bone = skeleton.findBone(controlBones[i]);
if (!bone) continue;
let colorInner = selectedBones[i] ? BONE_INNER_OVER : BONE_INNER;
let colorOuter = selectedBones[i] ? BONE_OUTER_OVER : BONE_OUTER;
const colorInner = selectedBones[i] ? BONE_INNER_OVER : BONE_INNER;
const colorOuter = selectedBones[i] ? BONE_OUTER_OVER : BONE_OUTER;
const applied = bone.applied;
renderer.circle(true, skeleton.x + applied.worldX, skeleton.y + applied.worldY, 20, colorInner);
renderer.circle(false, skeleton.x + applied.worldX, skeleton.y + applied.worldY, 20, colorOuter);
@ -992,10 +984,10 @@ export class SpinePlayer implements Disposable {
}
private showSpeedDialog (speedButton: HTMLElement) {
let id = "speed";
const id = "speed";
if (this.hidePopup(id)) return;
let popup = new Popup(id, speedButton, this, this.playerControls!, /*html*/`
const popup = new Popup(id, speedButton, this, this.playerControls!, /*html*/`
<div class="spine-player-popup-title">Speed</div>
<hr>
<div class="spine-player-row" style="align-items:center;padding:8px">
@ -1004,7 +996,7 @@ export class SpinePlayer implements Disposable {
<div class="spine-player-row" style="justify-content:space-between"><div>0.1x</div><div>1x</div><div>2x</div></div>
</div>
</div>`);
let slider = new Slider(2, 0.1, true);
const slider = new Slider(2, 0.1, true);
findWithClass(popup.dom, "spine-player-speed-slider").appendChild(slider.create());
slider.setValue(this.speed / 2);
slider.change = (percentage) => this.speed = percentage * 2;
@ -1012,21 +1004,21 @@ export class SpinePlayer implements Disposable {
}
private showAnimationsDialog (animationsButton: HTMLElement) {
let id = "animations";
const id = "animations";
if (this.hidePopup(id)) return;
if (!this.skeleton || !this.skeleton.data.animations.length) return;
let popup = new Popup(id, animationsButton, this, this.playerControls!,
const popup = new Popup(id, animationsButton, this, this.playerControls!,
/*html*/`<div class="spine-player-popup-title">Animations</div><hr><ul class="spine-player-list"></ul>`);
let rows = findWithClass(popup.dom, "spine-player-list");
const rows = findWithClass(popup.dom, "spine-player-list");
this.skeleton.data.animations.forEach((animation) => {
// Skip animations not whitelisted if a whitelist was given.
if (this.config.animations && this.config.animations.indexOf(animation.name) < 0) return;
let row = createElement(
const row = createElement(
/*html*/`<li class="spine-player-list-item selectable"><div class="selectable-circle"></div><div class="selectable-text"></div></li>`);
if (animation.name == this.config.animation) row.classList.add("selected");
if (animation.name === this.config.animation) row.classList.add("selected");
findWithClass(row, "selectable-text").innerText = animation.name;
rows.appendChild(row);
row.onclick = () => {
@ -1042,20 +1034,20 @@ export class SpinePlayer implements Disposable {
}
private showSkinsDialog (skinButton: HTMLElement) {
let id = "skins";
const id = "skins";
if (this.hidePopup(id)) return;
if (!this.skeleton || !this.skeleton.data.animations.length) return;
let popup = new Popup(id, skinButton, this, this.playerControls!,
const popup = new Popup(id, skinButton, this, this.playerControls!,
/*html*/`<div class="spine-player-popup-title">Skins</div><hr><ul class="spine-player-list"></ul>`);
let rows = findWithClass(popup.dom, "spine-player-list");
const rows = findWithClass(popup.dom, "spine-player-list");
this.skeleton.data.skins.forEach((skin) => {
// Skip skins not whitelisted if a whitelist was given.
if (this.config.skins && this.config.skins.indexOf(skin.name) < 0) return;
let row = createElement(/*html*/`<li class="spine-player-list-item selectable"><div class="selectable-circle"></div><div class="selectable-text"></div></li>`);
if (skin.name == this.config.skin) row.classList.add("selected");
const row = createElement(/*html*/`<li class="spine-player-list-item selectable"><div class="selectable-circle"></div><div class="selectable-text"></div></li>`);
if (skin.name === this.config.skin) row.classList.add("selected");
findWithClass(row, "selectable-text").innerText = skin.name;
rows.appendChild(row);
row.onclick = () => {
@ -1070,18 +1062,18 @@ export class SpinePlayer implements Disposable {
}
private showSettingsDialog (settingsButton: HTMLElement) {
let id = "settings";
const id = "settings";
if (this.hidePopup(id)) return;
if (!this.skeleton || !this.skeleton.data.animations.length) return;
let popup = new Popup(id, settingsButton, this, this.playerControls!, /*html*/`<div class="spine-player-popup-title">Debug</div><hr><ul class="spine-player-list"></li>`);
const popup = new Popup(id, settingsButton, this, this.playerControls!, /*html*/`<div class="spine-player-popup-title">Debug</div><hr><ul class="spine-player-list"></li>`);
let rows = findWithClass(popup.dom, "spine-player-list");
let makeItem = (label: string, name: string) => {
let row = createElement(/*html*/`<li class="spine-player-list-item"></li>`);
let s = new Switch(label);
const rows = findWithClass(popup.dom, "spine-player-list");
const makeItem = (label: string, name: string) => {
const row = createElement(/*html*/`<li class="spine-player-list-item"></li>`);
const s = new Switch(label);
row.appendChild(s.create());
let debug = this.config.debug as any;
const debug = this.config.debug as any;
s.setEnabled(debug[name]);
s.change = (value) => debug[name] = value;
rows.appendChild(row);
@ -1107,7 +1099,6 @@ export class SpinePlayer implements Disposable {
+ message.replace("\n", "<br><br>") + `</div>`));
if (this.config.error) this.config.error(this, message);
throw (error ? error : new Error(message));
console.log(error);
}
}
}
@ -1121,7 +1112,7 @@ class Popup {
this.dom = createElement(/*html*/`<div class="spine-player-popup spine-player-hidden"></div>`);
this.dom.innerHTML = htmlContent;
parent.appendChild(this.dom);
this.className = "spine-player-button-icon-" + id + "-selected";
this.className = `spine-player-button-icon-${id}-selected`;
}
dispose () {
@ -1131,7 +1122,7 @@ class Popup {
hide (id: string): boolean {
this.dom.remove();
this.button.classList.remove(this.className);
if (this.id == id) {
if (this.id === id) {
this.player.popup = null;
return true;
}
@ -1145,19 +1136,19 @@ class Popup {
// Make sure the popup isn't bigger than the player.
let dismissed = false;
let resize = () => {
const resize = () => {
if (!dismissed) requestAnimationFrame(resize);
let playerDom = this.player.dom;
let bottomOffset = Math.abs(playerDom.getBoundingClientRect().bottom - playerDom.getBoundingClientRect().bottom);
let rightOffset = Math.abs(playerDom.getBoundingClientRect().right - playerDom.getBoundingClientRect().right);
this.dom.style.maxHeight = (playerDom.clientHeight - bottomOffset - rightOffset) + "px";
const playerDom = this.player.dom;
const bottomOffset = Math.abs(playerDom.getBoundingClientRect().bottom - playerDom.getBoundingClientRect().bottom);
const rightOffset = Math.abs(playerDom.getBoundingClientRect().right - playerDom.getBoundingClientRect().right);
this.dom.style.maxHeight = `${playerDom.clientHeight - bottomOffset - rightOffset}px`;
}
requestAnimationFrame(resize);
// Dismiss when clicking somewhere outside the popup.
let justClicked = true;
let windowClickListener = (event: any) => {
if (justClicked || this.player.popup != this) {
const windowClickListener = (event: any) => {
if (justClicked || this.player.popup !== this) {
justClicked = false;
return;
}
@ -1250,8 +1241,8 @@ class Slider {
setValue (percentage: number): number {
percentage = Math.max(0, Math.min(1, percentage));
if (this.snaps) {
let snap = 1 / this.snaps;
let modulo = percentage % snap;
const snap = 1 / this.snaps;
const modulo = percentage % snap;
// floor
if (modulo < snap * this.snapPercentage)
percentage = percentage - modulo;
@ -1259,7 +1250,7 @@ class Slider {
percentage = percentage - modulo + snap;
percentage = Math.max(0, Math.min(1, percentage));
}
this.value!.style.width = "" + (percentage * 100) + "%";
this.value!.style.width = `${percentage * 100}%`;
// this.knob.style.left = "" + (-8 + percentage * this.slider.clientWidth) + "px";
return percentage;
}
@ -1270,7 +1261,7 @@ function findWithClass (element: HTMLElement, className: string): HTMLElement {
}
function createElement (html: string): HTMLElement {
let div = document.createElement("div");
const div = document.createElement("div");
div.innerHTML = html;
return div.children[0] as HTMLElement;
}
@ -1280,7 +1271,7 @@ function removeClass (elements: HTMLCollection, clazz: string) {
elements[i].classList.remove(clazz);
}
function toString (object: any) {
function print (object: any) {
return JSON.stringify(object)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")

View File

@ -88,14 +88,14 @@ body { margin: 0px; }
}
private render (parent: HTMLElement) {
let dom = /*html*/`
const dom = /*html*/`
<div style="display: flex; flex-direction: column; width: 100%; height: 100%;">
<div style="width: 100%; height: 50%"></div>
<iframe style="width: 100%; height: 50%; outline: none; border: none;"></iframe>
</div>
`;
parent.innerHTML = dom;
let codeElement = parent.children[0].children[0];
const codeElement = parent.children[0].children[0];
this.player = parent.children[0].children[1] as HTMLIFrameElement;
requestAnimationFrame(() => {

View File

@ -1,4 +1,4 @@
export * from './Player.js';
export * from './PlayerEditor.js';
export * from "@esotericsoftware/spine-core";
export * from "@esotericsoftware/spine-webgl";
export * from "@esotericsoftware/spine-webgl";
export * from './Player.js';
export * from './PlayerEditor.js';

View File

@ -27,9 +27,9 @@
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { AssetCache, AssetManager, Color, Disposable, Input, LoadingScreen, ManagedWebGLRenderingContext, Physics, SceneRenderer, TimeKeeper, Vector2, Vector3 } from "@esotericsoftware/spine-webgl"
import { SpineWebComponentSkeleton } from "./SpineWebComponentSkeleton.js"
import { AttributeTypes, castValue, Point, Rectangle } from "./wcUtils.js"
import { AssetCache, AssetManager, type Bone, Color, type Disposable, Input, LoadingScreen, ManagedWebGLRenderingContext, Physics, SceneRenderer, type Skeleton, TimeKeeper, Vector2, Vector3 } from "@esotericsoftware/spine-webgl"
import type { SpineWebComponentSkeleton } from "./SpineWebComponentSkeleton.js"
import { type AttributeTypes, castValue, type Point, type Rectangle } from "./wcUtils.js"
interface OverlayAttributes {
overlayId?: string
@ -41,6 +41,8 @@ interface OverlayAttributes {
}
export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttributes, Disposable {
declare parentElement: HTMLElement;
private static OVERLAY_ID = "spine-overlay-default-identifier";
private static OVERLAY_LIST = new Map<string, SpineWebComponentOverlay>();
@ -66,7 +68,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
/**
* A list holding the widgets added to this overlay.
*/
public widgets = new Array<SpineWebComponentSkeleton>();
public widgets = [] as SpineWebComponentSkeleton[];
/**
* The {@link SceneRenderer} used by this overlay.
@ -256,7 +258,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
const { target, intersectionRatio } = elem;
let { isIntersecting } = elem;
for (const widget of this.widgets) {
if (widget.getHostElement() != target) continue;
if (widget.getHostElement() !== target) continue;
// old browsers do not have isIntersecting
if (isIntersecting === undefined) {
@ -277,13 +279,13 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
if (this.hasCssTweakOff()) {
this.hasParentTransform = false;
} else {
this.parentElement!.style.transform = `translateZ(0)`;
this.parentElement.style.transform = `translateZ(0)`;
}
} else {
window.addEventListener("resize", this.windowResizeCallback);
}
this.resizeObserver = new ResizeObserver(() => this.resizedCallback());
this.resizeObserver.observe(this.parentElement!);
this.resizeObserver.observe(this.parentElement);
for (const widget of this.widgets) {
this.intersectionObserver?.observe(widget.getHostElement());
@ -308,7 +310,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
this.input?.dispose();
}
static attributesDescription: Record<string, { propertyName: keyof OverlayAttributes, type: AttributeTypes, defaultValue?: any }> = {
static attributesDescription: Record<string, { propertyName: keyof OverlayAttributes, type: AttributeTypes, defaultValue?: OverlayAttributes[keyof OverlayAttributes] }> = {
"overlay-id": { propertyName: "overlayId", type: "string" },
"no-auto-parent-transform": { propertyName: "noAutoParentTransform", type: "boolean" },
"overflow-top": { propertyName: "overflowTop", type: "number" },
@ -324,7 +326,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
attributeChangedCallback (name: string, oldValue: string | null, newValue: string | null): void {
const { type, propertyName, defaultValue } = SpineWebComponentOverlay.attributesDescription[name];
const val = castValue(type, newValue, defaultValue);
(this as any)[propertyName] = val;
(this[propertyName] as OverlayAttributes[typeof propertyName]) = val as OverlayAttributes[typeof propertyName];
return;
}
@ -360,12 +362,12 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
this.scrolledCallback();
if (!this.loaded) {
this.loaded = true;
this.parentElement!.appendChild(this);
this.parentElement.appendChild(this);
}
}
private hasCssTweakOff () {
return this.noAutoParentTransform && getComputedStyle(this.parentElement!).transform === "none";
return this.noAutoParentTransform && getComputedStyle(this.parentElement).transform === "none";
}
/**
@ -393,7 +395,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
const comparison = this.compareDocumentPosition(widget);
// DOCUMENT_POSITION_DISCONNECTED is needed when a widget is inside the overlay (due to followBone)
if ((comparison & Node.DOCUMENT_POSITION_FOLLOWING) && !(comparison & Node.DOCUMENT_POSITION_DISCONNECTED)) {
this.parentElement!.appendChild(this);
this.parentElement.appendChild(this);
}
}
@ -448,7 +450,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
this.div.appendChild(this.fps);
this.fpsAppended = true;
}
this.fps.innerText = this.time.framesPerSecond.toFixed(2) + " fps";
this.fps.innerText = `${this.time.framesPerSecond.toFixed(2)} fps`;
} else {
if (this.fpsAppended) {
this.div.removeChild(this.fps);
@ -482,15 +484,15 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
const renderWidgets = () => {
clear(0, 0, 0, 0);
let renderer = this.renderer;
const renderer = this.renderer;
renderer.begin();
let ref: DOMRect;
let offsetLeftForOevrlay = 0;
let offsetTopForOverlay = 0;
if (!this.appendedToBody) {
ref = this.parentElement!.getBoundingClientRect();
const computedStyle = getComputedStyle(this.parentElement!);
ref = this.parentElement.getBoundingClientRect();
const computedStyle = getComputedStyle(this.parentElement);
offsetLeftForOevrlay = ref.left + parseFloat(computedStyle.borderLeftWidth);
offsetTopForOverlay = ref.top + parseFloat(computedStyle.borderTopWidth);
}
@ -532,7 +534,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
if (loading) {
if (spinner) {
if (!widget.loadingScreen) widget.loadingScreen = new LoadingScreen(renderer);
widget.loadingScreen!.drawInCoordinates(divOriginX, divOriginY);
widget.loadingScreen?.drawInCoordinates(divOriginX, divOriginY);
}
if (clip) endScissor();
continue;
@ -540,7 +542,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
if (skeleton) {
if (fit !== "origin") {
let { x: ax, y: ay, width: aw, height: ah } = bounds;
const { x: ax, y: ay, width: aw, height: ah } = bounds;
if (aw <= 0 || ah <= 0) continue;
// scale ratio
@ -622,7 +624,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
// drawing debug stuff
if (debug) {
// if (true) {
let { x: ax, y: ay, width: aw, height: ah } = bounds;
const { x: ax, y: ay, width: aw, height: ah } = bounds;
// show bounds and its center
if (drag) {
@ -645,7 +647,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
renderer.circle(true, bbCenterX, bbCenterY, 10, blue);
// show skeleton root
const root = skeleton.getRootBone()!;
const root = skeleton.getRootBone() as Bone;
renderer.circle(true, root.applied.x + worldOffsetX, root.applied.y + worldOffsetY, 10, red);
// show shifted origin
@ -720,7 +722,6 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
const red = new Color(1, 0, 0, 1);
const green = new Color(0, 1, 0, 1);
const blue = new Color(0, 0, 1, 1);
const transparentWhite = new Color(1, 1, 1, .3);
const transparentRed = new Color(1, 0, 0, .3);
}
@ -735,12 +736,12 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
this.pointerCanvasY = input.y - window.scrollY;
if (!this.appendedToBody) {
const ref = this.parentElement!.getBoundingClientRect();
const ref = this.parentElement.getBoundingClientRect();
this.pointerCanvasX -= ref.left;
this.pointerCanvasY -= ref.top;
}
let tempVector = this.tempVector;
const tempVector = this.tempVector;
tempVector.set(this.pointerCanvasX, this.pointerCanvasY, 0);
this.renderer.camera.screenToWorld(tempVector, this.canvas.clientWidth, this.canvas.clientHeight);
@ -763,8 +764,8 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
const inputManager = new Input(document.body, false)
const inputPointTemp: Point = new Vector2();
const getInput = (ev?: MouseEvent | TouchEvent): Point => {
const originalEvent = ev instanceof MouseEvent ? ev : ev!.changedTouches[0];
const getInput = (ev: MouseEvent | TouchEvent): Point => {
const originalEvent = ev instanceof MouseEvent ? ev : ev.changedTouches[0];
inputPointTemp.x = originalEvent.pageX + this.overflowLeftSize;
inputPointTemp.y = originalEvent.pageY + this.overflowTopSize;
return inputPointTemp;
@ -774,7 +775,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
let lastY = 0;
inputManager.addListener({
// moved is used to pass pointer position wrt to canvas and widget position and currently is EXPERIMENTAL
moved: (x, y, ev) => {
moved: (x, y, ev: MouseEvent | TouchEvent) => {
const input = getInput(ev);
this.updatePointer(input);
@ -784,7 +785,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
widget.pointerEventUpdate("move", ev);
}
},
down: (x, y, ev) => {
down: (x, y, ev: MouseEvent | TouchEvent) => {
const input = getInput(ev);
this.updatePointer(input);
@ -805,11 +806,11 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
lastX = input.x;
lastY = input.y;
},
dragged: (x, y, ev) => {
dragged: (x, y, ev: MouseEvent | TouchEvent) => {
const input = getInput(ev);
let dragX = input.x - lastX;
let dragY = input.y - lastY;
const dragX = input.x - lastX;
const dragY = input.y - lastY;
this.updatePointer(input);
@ -820,7 +821,7 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
if (!widget.dragging) continue;
const skeleton = widget.skeleton!;
const skeleton = widget.skeleton as Skeleton;
widget.dragX += this.screenToWorldLength(dragX);
widget.dragY -= this.screenToWorldLength(dragY);
skeleton.physicsTranslate(dragX, -dragY);
@ -861,8 +862,8 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
const totalWidth = width * (1 + (this.overflowLeft + this.overflowRight));
const totalHeight = height * (1 + (this.overflowTop + this.overflowBottom));
this.canvas.style.width = totalWidth + "px";
this.canvas.style.height = totalHeight + "px";
this.canvas.style.width = `${totalWidth}px`;
this.canvas.style.height = `${totalHeight}px`;
this.resize(totalWidth, totalHeight);
}
@ -872,28 +873,28 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
// this.div?.remove(); is it better width/height to zero?
// this.div!.style.width = 0 + "px";
// this.div!.style.height = 0 + "px";
this.div!.style.display = "none";
this.div.style.display = "none";
if (this.appendedToBody) {
const { width, height } = this.getPageSize();
this.div!.style.width = width + "px";
this.div!.style.height = height + "px";
this.div.style.width = `${width}px`;
this.div.style.height = `${height}px`;
} else {
if (this.hasCssTweakOff()) {
// this case lags if scrolls or position fixed. Users should never use tweak off
this.div!.style.width = this.parentElement!.clientWidth + "px";
this.div!.style.height = this.parentElement!.clientHeight + "px";
this.div.style.width = `${this.parentElement.clientWidth}px`;
this.div.style.height = `${this.parentElement.clientHeight}px`;
this.canvas.style.transform = `translate(${-this.overflowLeftSize}px,${-this.overflowTopSize}px)`;
} else {
this.div!.style.width = this.parentElement!.scrollWidth + "px";
this.div!.style.height = this.parentElement!.scrollHeight + "px";
this.div.style.width = `${this.parentElement.scrollWidth}px`;
this.div.style.height = `${this.parentElement.scrollHeight}px`;
}
}
this.div!.style.display = "";
this.div.style.display = "";
// this.root.appendChild(this.div!);
}
private resize (width: number, height: number) {
let canvas = this.canvas;
const canvas = this.canvas;
canvas.width = Math.round(this.screenToWorldLength(width));
canvas.height = Math.round(this.screenToWorldLength(height));
this.renderer.context.gl.viewport(0, 0, canvas.width, canvas.height);
@ -921,13 +922,13 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
private getViewportSize (): { width: number, height: number } {
if (!this.appendedToBody) {
return {
width: this.parentElement!.clientWidth,
height: this.parentElement!.clientHeight,
width: this.parentElement.clientWidth,
height: this.parentElement.clientHeight,
}
}
let width = window.innerWidth;
let height = window.innerHeight;
const width = window.innerWidth;
const height = window.innerHeight;
const dpr = this.getDevicePixelRatio();
if (dpr !== this.lastDPR) {
@ -993,10 +994,10 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
// Ideally this should be the only appendedToBody case (no-auto-parent-transform not enabled or at least an ancestor has transform)
// I'd like to get rid of the else case
if (this.hasParentTransform) {
scrollPositionX += this.parentElement!.scrollLeft;
scrollPositionY += this.parentElement!.scrollTop;
scrollPositionX += this.parentElement.scrollLeft;
scrollPositionY += this.parentElement.scrollTop;
} else {
const { left, top } = this.parentElement!.getBoundingClientRect();
const { left, top } = this.parentElement.getBoundingClientRect();
scrollPositionX += left + window.scrollX;
scrollPositionY += top + window.scrollY;
@ -1027,10 +1028,10 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
let parent: HTMLElement | null = element;
let zIndex: undefined | number;
do {
let currentZIndex = parseInt(getComputedStyle(parent).zIndex);
const currentZIndex = parseInt(getComputedStyle(parent).zIndex);
// searching the shallowest z-index
if (!isNaN(currentZIndex)) zIndex = currentZIndex;
if (!Number.isNaN(currentZIndex)) zIndex = currentZIndex;
parent = parent.parentElement;
} while (parent && parent !== document.body)

View File

@ -28,31 +28,32 @@
*****************************************************************************/
import {
Animation,
type Animation,
AnimationState,
AnimationStateData,
AtlasAttachmentLoader,
Bone,
Disposable,
LoadingScreen,
type Bone,
type Disposable,
type LoadingScreen,
MeshAttachment,
MixBlend,
MixDirection,
NumberArrayLike,
type NumberArrayLike,
Physics,
RegionAttachment,
Skeleton,
SkeletonBinary,
SkeletonData,
type SkeletonData,
SkeletonJson,
Skin,
Slot,
TextureAtlas,
type Slot,
type TextureAtlas,
type TrackEntry,
Utils,
Vector2,
} from "@esotericsoftware/spine-webgl";
import { SpineWebComponentOverlay } from "./SpineWebComponentOverlay.js";
import { AttributeTypes, castValue, isBase64, Rectangle } from "./wcUtils.js";
import { type AttributeTypes, castValue, isBase64, type Rectangle } from "./wcUtils.js";
type UpdateSpineWidgetFunction = (delta: number, skeleton: Skeleton, state: AnimationState) => void;
@ -456,10 +457,10 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
}
private getSlotFromRef (slotRef: number | string | Slot): Slot {
let slot: Slot | null;
let slot: Slot | null | undefined;
if (typeof slotRef === 'number') slot = this.skeleton!.slots[slotRef];
else if (typeof slotRef === 'string') slot = this.skeleton!.findSlot(slotRef);
if (typeof slotRef === 'number') slot = this.skeleton?.slots[slotRef];
else if (typeof slotRef === 'string') slot = this.skeleton?.findSlot(slotRef);
else slot = slotRef;
if (!slot) throw new Error(`No slot found with the given slot reference: ${slotRef}`);
@ -686,7 +687,7 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
// - remove appendTo that is just to avoid the user to use the overlayAssignedPromise when the widget is created using js
private overlayAssignedPromise: Promise<void>;
static attributesDescription: Record<string, { propertyName: keyof WidgetAttributes, type: AttributeTypes, defaultValue?: any }> = {
static attributesDescription: Record<string, { propertyName: keyof WidgetAttributes, type: AttributeTypes, defaultValue?: WidgetAttributes[keyof WidgetAttributes] }> = {
atlas: { propertyName: "atlasPath", type: "string" },
skeleton: { propertyName: "skeletonPath", type: "string" },
"raw-data": { propertyName: "rawData", type: "object" },
@ -776,7 +777,7 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
window.removeEventListener("DOMContentLoaded", this.DOMContentLoadedCallback);
const index = this.overlay?.widgets.indexOf(this);
if (index > 0) {
this.overlay!.widgets.splice(index, 1);
this.overlay?.widgets.splice(index, 1);
}
}
@ -797,7 +798,7 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
attributeChangedCallback (name: string, oldValue: string | null, newValue: string | null): void {
const { type, propertyName, defaultValue } = SpineWebComponentSkeleton.attributesDescription[name];
const val = castValue(type, newValue, defaultValue);
(this as any)[propertyName] = val;
(this[propertyName] as WidgetAttributes[typeof propertyName]) = val as WidgetAttributes[typeof propertyName];
return;
}
@ -830,12 +831,12 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
* @param atlas the `TextureAtlas` from which to get the `TextureAtlasPage`s
* @returns The list of loaded assets
*/
public async loadTexturesInPagesAttribute (): Promise<Array<any>> {
const atlas = this.overlay.assetManager.require(this.atlasPath!) as TextureAtlas;
public async loadTexturesInPagesAttribute (): Promise<Array<string>> {
const atlas = this.overlay.assetManager.require(this.atlasPath as string) as TextureAtlas;
const pagesIndexToLoad = this.pages ?? atlas.pages.map((_, i) => i); // if no pages provided, loads all
const atlasPath = this.atlasPath?.includes("/") ? this.atlasPath.substring(0, this.atlasPath.lastIndexOf("/") + 1) : "";
const promisePageList: Array<Promise<any>> = [];
const texturePaths = [];
const promisePageList: Array<Promise<string>> = [];
const texturePaths = [] as string[];
for (const index of pagesIndexToLoad) {
const page = atlas.pages[index];
@ -866,7 +867,7 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
*/
public getHostElement (): HTMLElement {
return (this.width <= 0 || this.width <= 0) && !this.getAttribute("style") && !this.getAttribute("class")
? this.parentElement!
? this.parentElement as HTMLElement
: this;
}
@ -934,7 +935,7 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
const isBinary = skeletonPath.endsWith(".skel");
if (rawData) {
for (let [key, value] of Object.entries(rawData)) {
for (const [key, value] of Object.entries(rawData)) {
this.overlay.assetManager.setRawDataURI(key, isBase64(value) ? `data:application/octet-stream;base64,${value}` : value);
}
}
@ -1032,7 +1033,7 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
const cycleFn = () => {
const trackIndex = Number(trackIndexString);
for (const [index, { animationName, delay, loop, mixDuration }] of animations.entries()) {
let track;
let track: TrackEntry;
if (index === 0) {
if (animationName === "#EMPTY#") {
track = state.setEmptyAnimation(trackIndex, mixDuration);
@ -1076,7 +1077,7 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
}
private render (): void {
let noSize = (!this.getAttribute("style") && !this.getAttribute("class"));
const noSize = (!this.getAttribute("style") && !this.getAttribute("class"));
this.root.innerHTML = `
<style>
:host {
@ -1155,24 +1156,24 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
}
private checkSlotInteraction (type: PointerEventTypesInput, originalEvent?: UIEvent) {
for (let [slot, interactionState] of this.pointerSlotEventCallbacks) {
for (const [slot, interactionState] of this.pointerSlotEventCallbacks) {
if (!slot.bone.active) continue;
let attachment = slot.applied.attachment;
const attachment = slot.applied.attachment;
if (!(attachment instanceof RegionAttachment || attachment instanceof MeshAttachment)) continue;
const { slotFunction, inside } = interactionState
let vertices = this.verticesTemp;
const vertices = this.verticesTemp;
let hullLength = 8;
// we could probably cache the vertices from rendering if interaction with this slot is enabled
if (attachment instanceof RegionAttachment) {
let regionAttachment = <RegionAttachment>attachment;
const regionAttachment = <RegionAttachment>attachment;
regionAttachment.computeWorldVertices(slot, vertices, 0, 2);
} else if (attachment instanceof MeshAttachment) {
let mesh = <MeshAttachment>attachment;
mesh.computeWorldVertices(this.skeleton!, slot, 0, mesh.worldVerticesLength, vertices, 0, 2);
const mesh = <MeshAttachment>attachment;
mesh.computeWorldVertices(this.skeleton as Skeleton, slot, 0, mesh.worldVerticesLength, vertices, 0, 2);
hullLength = mesh.hullLength;
}
@ -1274,7 +1275,7 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
if (!skeleton) return { x: 0, y: 0, width: 0, height: 0 };
skeleton.setupPose();
let offset = new Vector2(), size = new Vector2();
const offset = new Vector2(), size = new Vector2();
const tempArray = new Array<number>(2);
if (!animation) {
skeleton.updateWorldTransform(Physics.update);
@ -1294,8 +1295,8 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
skeleton.updateWorldTransform(Physics.update);
skeleton.getBounds(offset, size, tempArray, renderer.skeletonRenderer.getSkeletonClipping());
if (!isNaN(offset.x) && !isNaN(offset.y) && !isNaN(size.x) && !isNaN(size.y) &&
!isNaN(minX) && !isNaN(minY) && !isNaN(maxX) && !isNaN(maxY)) {
if (!Number.isNaN(offset.x) && !Number.isNaN(offset.y) && !Number.isNaN(size.x) && !Number.isNaN(size.y) &&
!Number.isNaN(minX) && !Number.isNaN(minY) && !Number.isNaN(maxX) && !Number.isNaN(maxY)) {
minX = Math.min(offset.x, minX);
maxX = Math.max(offset.x + size.x, maxX);
minY = Math.min(offset.y, minY);
@ -1345,7 +1346,7 @@ export function createSkeleton (parameters: WidgetAttributes): SpineWebComponent
Object.entries(SpineWebComponentSkeleton.attributesDescription).forEach(entry => {
const [key, { propertyName }] = entry;
const value = parameters[propertyName];
if (value) widget.setAttribute(key, value as any);
if (value) widget.setAttribute(key, value as string);
});
return widget;

View File

@ -1,4 +1,4 @@
export * from './SpineWebComponentSkeleton.js';
export * from './SpineWebComponentOverlay.js';
export * from "@esotericsoftware/spine-core";
export * from "@esotericsoftware/spine-webgl";
export * from './SpineWebComponentOverlay.js';
export * from './SpineWebComponentSkeleton.js';

View File

@ -28,8 +28,8 @@
*****************************************************************************/
import { AssetManagerBase, Downloader } from "@esotericsoftware/spine-core"
import { ManagedWebGLRenderingContext } from "./WebGL.js";
import { GLTexture } from "./GLTexture.js";
import type { ManagedWebGLRenderingContext } from "./WebGL.js";
export class AssetManager extends AssetManagerBase {
constructor (context: ManagedWebGLRenderingContext | WebGLRenderingContext, pathPrefix: string = "", downloader: Downloader = new Downloader()) {

View File

@ -51,11 +51,11 @@ export class OrthoCamera {
}
update () {
let projection = this.projection;
let view = this.view;
let projectionView = this.projectionView;
let inverseProjectionView = this.inverseProjectionView;
let zoom = this.zoom, viewportWidth = this.viewportWidth, viewportHeight = this.viewportHeight;
const projection = this.projection;
const view = this.view;
const projectionView = this.projectionView;
const inverseProjectionView = this.inverseProjectionView;
const zoom = this.zoom, viewportWidth = this.viewportWidth, viewportHeight = this.viewportHeight;
projection.ortho(zoom * (-viewportWidth / 2), zoom * (viewportWidth / 2),
zoom * (-viewportHeight / 2), zoom * (viewportHeight / 2),
this.near, this.far);
@ -66,7 +66,7 @@ export class OrthoCamera {
}
screenToWorld (screenCoords: Vector3, screenWidth: number, screenHeight: number) {
let x = screenCoords.x, y = screenHeight - screenCoords.y - 1;
const x = screenCoords.x, y = screenHeight - screenCoords.y - 1;
screenCoords.x = (2 * x) / screenWidth - 1;
screenCoords.y = (2 * y) / screenHeight - 1;
screenCoords.z = (2 * screenCoords.z) - 1;

View File

@ -27,13 +27,13 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import type { OrthoCamera } from "./Camera.js";
import { Input } from "./Input.js";
import { OrthoCamera } from "./Camera.js";
import { Vector3 } from "./Vector3.js";
export class CameraController {
constructor (public canvas: HTMLElement, public camera: OrthoCamera) {
let cameraX = 0, cameraY = 0, cameraZoom = 0;
let cameraX = 0, cameraY = 0;
let mouseX = 0, mouseY = 0;
let lastX = 0, lastY = 0;
let initialZoom = 0;
@ -47,39 +47,39 @@ export class CameraController {
initialZoom = camera.zoom;
},
dragged: (x: number, y: number) => {
let deltaX = x - mouseX;
let deltaY = y - mouseY;
let originWorld = camera.screenToWorld(new Vector3(0, 0), canvas.clientWidth, canvas.clientHeight);
let deltaWorld = camera.screenToWorld(new Vector3(deltaX, deltaY), canvas.clientWidth, canvas.clientHeight).sub(originWorld);
const deltaX = x - mouseX;
const deltaY = y - mouseY;
const originWorld = camera.screenToWorld(new Vector3(0, 0), canvas.clientWidth, canvas.clientHeight);
const deltaWorld = camera.screenToWorld(new Vector3(deltaX, deltaY), canvas.clientWidth, canvas.clientHeight).sub(originWorld);
camera.position.set(cameraX - deltaWorld.x, cameraY - deltaWorld.y, 0);
camera.update();
lastX = x;
lastY = y;
},
wheel: (delta: number) => {
let zoomAmount = delta / 200 * camera.zoom;
let newZoom = camera.zoom + zoomAmount;
const zoomAmount = delta / 200 * camera.zoom;
const newZoom = camera.zoom + zoomAmount;
if (newZoom > 0) {
let x = 0, y = 0;
if (delta < 0) {
x = lastX; y = lastY;
} else {
let viewCenter = new Vector3(canvas.clientWidth / 2 + 15, canvas.clientHeight / 2);
let mouseToCenterX = lastX - viewCenter.x;
let mouseToCenterY = canvas.clientHeight - 1 - lastY - viewCenter.y;
const viewCenter = new Vector3(canvas.clientWidth / 2 + 15, canvas.clientHeight / 2);
const mouseToCenterX = lastX - viewCenter.x;
const mouseToCenterY = canvas.clientHeight - 1 - lastY - viewCenter.y;
x = viewCenter.x - mouseToCenterX;
y = canvas.clientHeight - 1 - viewCenter.y + mouseToCenterY;
}
let oldDistance = camera.screenToWorld(new Vector3(x, y), canvas.clientWidth, canvas.clientHeight);
const oldDistance = camera.screenToWorld(new Vector3(x, y), canvas.clientWidth, canvas.clientHeight);
camera.zoom = newZoom;
camera.update();
let newDistance = camera.screenToWorld(new Vector3(x, y), canvas.clientWidth, canvas.clientHeight);
const newDistance = camera.screenToWorld(new Vector3(x, y), canvas.clientWidth, canvas.clientHeight);
camera.position.add(oldDistance.sub(newDistance));
camera.update();
}
},
zoom: (initialDistance, distance) => {
let newZoom = initialDistance / distance;
const newZoom = initialDistance / distance;
camera.zoom = initialZoom * newZoom;
},
up: (x: number, y: number) => {

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Texture, Disposable, Restorable, TextureFilter, TextureWrap } from "@esotericsoftware/spine-core";
import { type Disposable, type Restorable, Texture, TextureFilter, type TextureWrap } from "@esotericsoftware/spine-core";
import { ManagedWebGLRenderingContext } from "./WebGL.js";
export class GLTexture extends Texture implements Disposable, Restorable {
@ -47,7 +47,7 @@ export class GLTexture extends Texture implements Disposable, Restorable {
}
setFilters (minFilter: TextureFilter, magFilter: TextureFilter) {
let gl = this.context.gl;
const gl = this.context.gl;
this.bind();
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, GLTexture.validateMagFilter(magFilter));
@ -80,14 +80,14 @@ export class GLTexture extends Texture implements Disposable, Restorable {
}
setWraps (uWrap: TextureWrap, vWrap: TextureWrap) {
let gl = this.context.gl;
const gl = this.context.gl;
this.bind();
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, uWrap);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, vWrap);
}
update (useMipMaps: boolean) {
let gl = this.context.gl;
const gl = this.context.gl;
if (!this.texture) this.texture = this.context.gl.createTexture();
this.bind();
if (GLTexture.DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL) gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
@ -105,21 +105,21 @@ export class GLTexture extends Texture implements Disposable, Restorable {
}
bind (unit: number = 0) {
let gl = this.context.gl;
const gl = this.context.gl;
this.boundUnit = unit;
gl.activeTexture(gl.TEXTURE0 + unit);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
}
unbind () {
let gl = this.context.gl;
const gl = this.context.gl;
gl.activeTexture(gl.TEXTURE0 + this.boundUnit);
gl.bindTexture(gl.TEXTURE_2D, null);
}
dispose () {
this.context.removeRestorable(this);
let gl = this.context.gl;
const gl = this.context.gl;
gl.deleteTexture(this.texture);
}
}

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Disposable } from "./index.js"
import type { Disposable } from "./index.js"
export class Input implements Disposable {
element: HTMLElement;
mouseX = 0;
@ -36,7 +36,7 @@ export class Input implements Disposable {
touch0: Touch | null = null;
touch1: Touch | null = null;
initialPinchDistance = 0;
private listeners = new Array<InputListener>();
private listeners = [] as InputListener[];
private autoPreventDefault: boolean;
// this is needed because browsers sends mousedown-mousemove-mousesup after a touch sequence, unless touch end preventDefault
@ -62,7 +62,7 @@ export class Input implements Disposable {
private setupCallbacks (element: HTMLElement) {
const mouseDown = (ev: UIEvent) => {
if (ev instanceof MouseEvent && !this.isTouch) {
let rect = element.getBoundingClientRect();
const rect = element.getBoundingClientRect();
this.mouseX = ev.clientX - rect.left;
this.mouseY = ev.clientY - rect.top;
this.buttonDown = true;
@ -72,7 +72,7 @@ export class Input implements Disposable {
const mouseMove = (ev: UIEvent) => {
if (ev instanceof MouseEvent && !this.isTouch) {
let rect = element.getBoundingClientRect();
const rect = element.getBoundingClientRect();
this.mouseX = ev.clientX - rect.left;
this.mouseY = ev.clientY - rect.top;
@ -88,7 +88,7 @@ export class Input implements Disposable {
const mouseUp = (ev: UIEvent) => {
if (ev instanceof MouseEvent && !this.isTouch) {
let rect = element.getBoundingClientRect();
const rect = element.getBoundingClientRect();
this.mouseX = ev.clientX - rect.left;;
this.mouseY = ev.clientY - rect.top;
this.buttonDown = false;
@ -99,21 +99,21 @@ export class Input implements Disposable {
const mouseWheel = (ev: WheelEvent) => {
if (this.autoPreventDefault) ev.preventDefault();
let deltaY = ev.deltaY;
if (ev.deltaMode == WheelEvent.DOM_DELTA_LINE) deltaY *= 8;
if (ev.deltaMode == WheelEvent.DOM_DELTA_PAGE) deltaY *= 24;
this.listeners.map((listener) => { if (listener.wheel) listener.wheel(ev.deltaY, ev); });
if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) deltaY *= 8;
if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) deltaY *= 24;
this.listeners.map((listener) => { if (listener.wheel) listener.wheel(deltaY, ev); });
};
const touchStart = (ev: TouchEvent) => {
this.isTouch = true;
if (!this.touch0 || !this.touch1) {
var touches = ev.changedTouches;
let nativeTouch = touches.item(0);
const touches = ev.changedTouches;
const nativeTouch = touches.item(0);
if (!nativeTouch) return;
let rect = element.getBoundingClientRect();
let x = nativeTouch.clientX - rect.left;
let y = nativeTouch.clientY - rect.top;
let touch = new Touch(nativeTouch.identifier, x, y);
const rect = element.getBoundingClientRect();
const x = nativeTouch.clientX - rect.left;
const y = nativeTouch.clientY - rect.top;
const touch = new Touch(nativeTouch.identifier, x, y);
this.mouseX = x;
this.mouseY = y;
this.buttonDown = true;
@ -123,8 +123,8 @@ export class Input implements Disposable {
this.listeners.map((listener) => { if (listener.down) listener.down(touch.x, touch.y, ev) })
} else if (!this.touch1) {
this.touch1 = touch;
let dx = this.touch1.x - this.touch0.x;
let dy = this.touch1.x - this.touch0.x;
const dx = this.touch1.x - this.touch0.x;
const dy = this.touch1.x - this.touch0.x;
this.initialPinchDistance = Math.sqrt(dx * dx + dy * dy);
this.listeners.map((listener) => { if (listener.zoom) listener.zoom(this.initialPinchDistance, this.initialPinchDistance, ev) });
}
@ -135,12 +135,12 @@ export class Input implements Disposable {
const touchMove = (ev: TouchEvent) => {
this.isTouch = true;
if (this.touch0) {
var touches = ev.changedTouches;
let rect = element.getBoundingClientRect();
for (var i = 0; i < touches.length; i++) {
var nativeTouch = touches[i];
let x = nativeTouch.clientX - rect.left;
let y = nativeTouch.clientY - rect.top;
const touches = ev.changedTouches;
const rect = element.getBoundingClientRect();
for (let i = 0; i < touches.length; i++) {
const nativeTouch = touches[i];
const x = nativeTouch.clientX - rect.left;
const y = nativeTouch.clientY - rect.top;
if (this.touch0.identifier === nativeTouch.identifier) {
this.touch0.x = this.mouseX = x;
@ -153,9 +153,9 @@ export class Input implements Disposable {
}
}
if (this.touch0 && this.touch1) {
let dx = this.touch1.x - this.touch0.x;
let dy = this.touch1.x - this.touch0.x;
let distance = Math.sqrt(dx * dx + dy * dy);
const dx = this.touch1.x - this.touch0.x;
const dy = this.touch1.x - this.touch0.x;
const distance = Math.sqrt(dx * dx + dy * dy);
this.listeners.map((listener) => { if (listener.zoom) listener.zoom(this.initialPinchDistance, distance, ev) });
}
}
@ -164,16 +164,17 @@ export class Input implements Disposable {
const touchEnd = (ev: TouchEvent) => {
this.isTouch = true;
if (this.touch0) {
var touches = ev.changedTouches;
let rect = element.getBoundingClientRect();
const touch0 = this.touch0 as Touch;
if (touch0) {
const touches = ev.changedTouches;
const rect = element.getBoundingClientRect();
for (var i = 0; i < touches.length; i++) {
var nativeTouch = touches[i];
let x = nativeTouch.clientX - rect.left;
let y = nativeTouch.clientY - rect.top;
for (let i = 0; i < touches.length; i++) {
const nativeTouch = touches[i];
const x = nativeTouch.clientX - rect.left;
const y = nativeTouch.clientY - rect.top;
if (this.touch0.identifier === nativeTouch.identifier) {
if (touch0.identifier === nativeTouch.identifier) {
this.touch0 = null;
this.mouseX = x;
this.mouseY = y;
@ -183,16 +184,16 @@ export class Input implements Disposable {
this.buttonDown = false;
break;
} else {
this.touch0 = this.touch1;
const touch0 = this.touch0 = this.touch1;
this.touch1 = null;
this.mouseX = this.touch0.x;
this.mouseX = this.touch0.x;
this.mouseX = touch0.x;
this.mouseY = touch0.y;
this.buttonDown = true;
this.listeners.map((listener) => { if (listener.down) listener.down(this.touch0!.x, this.touch0!.y, ev) });
this.listeners.map((listener) => { if (listener.down) listener.down(touch0.x, touch0.y, ev) });
}
}
if (this.touch1 && this.touch1.identifier) {
if (this.touch1?.identifier) {
this.touch1 = null;
}
}
@ -238,7 +239,7 @@ export class Input implements Disposable {
}
removeListener (listener: InputListener) {
let idx = this.listeners.indexOf(listener);
const idx = this.listeners.indexOf(listener);
if (idx > -1) {
this.listeners.splice(idx, 1);
}

File diff suppressed because one or more lines are too long

View File

@ -56,7 +56,7 @@ export class Matrix4 {
private static tmpMatrix = new Matrix4();
constructor () {
let v = this.values;
const v = this.values;
v[M00] = 1;
v[M11] = 1;
v[M22] = 1;
@ -69,8 +69,8 @@ export class Matrix4 {
}
transpose (): Matrix4 {
let t = this.temp;
let v = this.values;
const t = this.temp;
const v = this.values;
t[M00] = v[M00];
t[M01] = v[M10];
t[M02] = v[M20];
@ -91,7 +91,7 @@ export class Matrix4 {
}
identity (): Matrix4 {
let v = this.values;
const v = this.values;
v[M00] = 1;
v[M01] = 0;
v[M02] = 0;
@ -112,9 +112,9 @@ export class Matrix4 {
}
invert (): Matrix4 {
let v = this.values;
let t = this.temp;
let l_det = v[M30] * v[M21] * v[M12] * v[M03] - v[M20] * v[M31] * v[M12] * v[M03] - v[M30] * v[M11] * v[M22] * v[M03]
const v = this.values;
const t = this.temp;
const l_det = v[M30] * v[M21] * v[M12] * v[M03] - v[M20] * v[M31] * v[M12] * v[M03] - v[M30] * v[M11] * v[M22] * v[M03]
+ v[M10] * v[M31] * v[M22] * v[M03] + v[M20] * v[M11] * v[M32] * v[M03] - v[M10] * v[M21] * v[M32] * v[M03]
- v[M30] * v[M21] * v[M02] * v[M13] + v[M20] * v[M31] * v[M02] * v[M13] + v[M30] * v[M01] * v[M22] * v[M13]
- v[M00] * v[M31] * v[M22] * v[M13] - v[M20] * v[M01] * v[M32] * v[M13] + v[M00] * v[M21] * v[M32] * v[M13]
@ -122,8 +122,8 @@ export class Matrix4 {
+ v[M00] * v[M31] * v[M12] * v[M23] + v[M10] * v[M01] * v[M32] * v[M23] - v[M00] * v[M11] * v[M32] * v[M23]
- v[M20] * v[M11] * v[M02] * v[M33] + v[M10] * v[M21] * v[M02] * v[M33] + v[M20] * v[M01] * v[M12] * v[M33]
- v[M00] * v[M21] * v[M12] * v[M33] - v[M10] * v[M01] * v[M22] * v[M33] + v[M00] * v[M11] * v[M22] * v[M33];
if (l_det == 0) throw new Error("non-invertible matrix");
let inv_det = 1.0 / l_det;
if (l_det === 0) throw new Error("non-invertible matrix");
const inv_det = 1.0 / l_det;
t[M00] = v[M12] * v[M23] * v[M31] - v[M13] * v[M22] * v[M31] + v[M13] * v[M21] * v[M32]
- v[M11] * v[M23] * v[M32] - v[M12] * v[M21] * v[M33] + v[M11] * v[M22] * v[M33];
t[M01] = v[M03] * v[M22] * v[M31] - v[M02] * v[M23] * v[M31] - v[M03] * v[M21] * v[M32]
@ -176,7 +176,7 @@ export class Matrix4 {
}
determinant (): number {
let v = this.values;
const v = this.values;
return v[M30] * v[M21] * v[M12] * v[M03] - v[M20] * v[M31] * v[M12] * v[M03] - v[M30] * v[M11] * v[M22] * v[M03]
+ v[M10] * v[M31] * v[M22] * v[M03] + v[M20] * v[M11] * v[M32] * v[M03] - v[M10] * v[M21] * v[M32] * v[M03]
- v[M30] * v[M21] * v[M02] * v[M13] + v[M20] * v[M31] * v[M02] * v[M13] + v[M30] * v[M01] * v[M22] * v[M13]
@ -188,7 +188,7 @@ export class Matrix4 {
}
translate (x: number, y: number, z: number): Matrix4 {
let v = this.values;
const v = this.values;
v[M03] += x;
v[M13] += y;
v[M23] += z;
@ -201,10 +201,10 @@ export class Matrix4 {
projection (near: number, far: number, fovy: number, aspectRatio: number): Matrix4 {
this.identity();
let l_fd = (1.0 / Math.tan((fovy * (Math.PI / 180)) / 2.0));
let l_a1 = (far + near) / (near - far);
let l_a2 = (2 * far * near) / (near - far);
let v = this.values;
const l_fd = (1.0 / Math.tan((fovy * (Math.PI / 180)) / 2.0));
const l_a1 = (far + near) / (near - far);
const l_a2 = (2 * far * near) / (near - far);
const v = this.values;
v[M00] = l_fd / aspectRatio;
v[M10] = 0;
v[M20] = 0;
@ -230,15 +230,15 @@ export class Matrix4 {
ortho (left: number, right: number, bottom: number, top: number, near: number, far: number): Matrix4 {
this.identity();
let x_orth = 2 / (right - left);
let y_orth = 2 / (top - bottom);
let z_orth = -2 / (far - near);
const x_orth = 2 / (right - left);
const y_orth = 2 / (top - bottom);
const z_orth = -2 / (far - near);
let tx = -(right + left) / (right - left);
let ty = -(top + bottom) / (top - bottom);
let tz = -(far + near) / (far - near);
const tx = -(right + left) / (right - left);
const ty = -(top + bottom) / (top - bottom);
const tz = -(far + near) / (far - near);
let v = this.values;
const v = this.values;
v[M00] = x_orth;
v[M10] = 0;
v[M20] = 0;
@ -259,9 +259,9 @@ export class Matrix4 {
}
multiply (matrix: Matrix4): Matrix4 {
let t = this.temp;
let v = this.values;
let m = matrix.values;
const t = this.temp;
const v = this.values;
const m = matrix.values;
t[M00] = v[M00] * m[M00] + v[M01] * m[M10] + v[M02] * m[M20] + v[M03] * m[M30];
t[M01] = v[M00] * m[M01] + v[M01] * m[M11] + v[M02] * m[M21] + v[M03] * m[M31];
t[M02] = v[M00] * m[M02] + v[M01] * m[M12] + v[M02] * m[M22] + v[M03] * m[M32];
@ -282,9 +282,9 @@ export class Matrix4 {
}
multiplyLeft (matrix: Matrix4): Matrix4 {
let t = this.temp;
let v = this.values;
let m = matrix.values;
const t = this.temp;
const v = this.values;
const m = matrix.values;
t[M00] = m[M00] * v[M00] + m[M01] * v[M10] + m[M02] * v[M20] + m[M03] * v[M30];
t[M01] = m[M00] * v[M01] + m[M01] * v[M11] + m[M02] * v[M21] + m[M03] * v[M31];
t[M02] = m[M00] * v[M02] + m[M01] * v[M12] + m[M02] * v[M22] + m[M03] * v[M32];
@ -305,13 +305,13 @@ export class Matrix4 {
}
lookAt (position: Vector3, direction: Vector3, up: Vector3) {
let xAxis = Matrix4.xAxis, yAxis = Matrix4.yAxis, zAxis = Matrix4.zAxis;
const xAxis = Matrix4.xAxis, yAxis = Matrix4.yAxis, zAxis = Matrix4.zAxis;
zAxis.setFrom(direction).normalize();
xAxis.setFrom(direction).normalize();
xAxis.cross(up).normalize();
yAxis.setFrom(xAxis).cross(zAxis).normalize();
this.identity();
let val = this.values;
const val = this.values;
val[M00] = xAxis.x;
val[M01] = xAxis.y;
val[M02] = xAxis.z;

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Disposable, Restorable } from "@esotericsoftware/spine-core";
import type { Disposable, Restorable } from "@esotericsoftware/spine-core";
import { Shader } from "./Shader.js";
import { ManagedWebGLRenderingContext } from "./WebGL.js";
@ -64,8 +64,8 @@ export class Mesh implements Disposable, Restorable {
getVertexSizeInFloats (): number {
let size = 0;
for (var i = 0; i < this.attributes.length; i++) {
let attribute = this.attributes[i];
for (let i = 0; i < this.attributes.length; i++) {
const attribute = this.attributes[i];
size += attribute.numElements;
}
return size;
@ -84,14 +84,14 @@ export class Mesh implements Disposable, Restorable {
setVertices (vertices: Array<number>) {
this.dirtyVertices = true;
if (vertices.length > this.vertices.length) throw Error("Mesh can't store more than " + this.maxVertices() + " vertices");
if (vertices.length > this.vertices.length) throw Error(`Mesh can't store more than ${this.maxVertices()} vertices`);
this.vertices.set(vertices, 0);
this.verticesLength = vertices.length;
}
setIndices (indices: Array<number>) {
this.dirtyIndices = true;
if (indices.length > this.indices.length) throw Error("Mesh can't store more than " + this.maxIndices() + " indices");
if (indices.length > this.indices.length) throw Error(`Mesh can't store more than ${this.maxIndices()} indices`);
this.indices.set(indices, 0);
this.indicesLength = indices.length;
}
@ -101,7 +101,7 @@ export class Mesh implements Disposable, Restorable {
}
drawWithOffset (shader: Shader, primitiveType: number, offset: number, count: number) {
let gl = this.context.gl;
const gl = this.context.gl;
if (this.dirtyVertices || this.dirtyIndices) this.update();
this.bind(shader);
if (this.indicesLength > 0) {
@ -113,12 +113,12 @@ export class Mesh implements Disposable, Restorable {
}
bind (shader: Shader) {
let gl = this.context.gl;
const gl = this.context.gl;
gl.bindBuffer(gl.ARRAY_BUFFER, this.verticesBuffer);
let offset = 0;
for (let i = 0; i < this.attributes.length; i++) {
let attrib = this.attributes[i];
let location = shader.getAttributeLocation(attrib.name);
const attrib = this.attributes[i];
const location = shader.getAttributeLocation(attrib.name);
gl.enableVertexAttribArray(location);
gl.vertexAttribPointer(location, attrib.numElements, gl.FLOAT, false, this.elementsPerVertex * 4, offset * 4);
offset += attrib.numElements;
@ -127,10 +127,10 @@ export class Mesh implements Disposable, Restorable {
}
unbind (shader: Shader) {
let gl = this.context.gl;
const gl = this.context.gl;
for (let i = 0; i < this.attributes.length; i++) {
let attrib = this.attributes[i];
let location = shader.getAttributeLocation(attrib.name);
const attrib = this.attributes[i];
const location = shader.getAttributeLocation(attrib.name);
gl.disableVertexAttribArray(location);
}
gl.bindBuffer(gl.ARRAY_BUFFER, null);
@ -138,7 +138,7 @@ export class Mesh implements Disposable, Restorable {
}
private update () {
let gl = this.context.gl;
const gl = this.context.gl;
if (this.dirtyVertices) {
if (!this.verticesBuffer) {
this.verticesBuffer = gl.createBuffer();
@ -166,7 +166,7 @@ export class Mesh implements Disposable, Restorable {
dispose () {
this.context.removeRestorable(this);
let gl = this.context.gl;
const gl = this.context.gl;
gl.deleteBuffer(this.verticesBuffer);
gl.deleteBuffer(this.indicesBuffer);
}
@ -190,7 +190,7 @@ export class Position3Attribute extends VertexAttribute {
export class TexCoordAttribute extends VertexAttribute {
constructor (unit: number = 0) {
super(Shader.TEXCOORDS + (unit == 0 ? "" : unit), VertexAttributeType.Float, 2);
super(Shader.TEXCOORDS + (unit === 0 ? "" : unit), VertexAttributeType.Float, 2);
}
}

View File

@ -27,16 +27,17 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { BlendMode, Disposable } from "@esotericsoftware/spine-core";
import { GLTexture } from "./GLTexture.js";
import { Mesh, Position2Attribute, ColorAttribute, TexCoordAttribute, Color2Attribute } from "./Mesh.js";
import { Shader } from "./Shader.js";
import type { BlendMode, Disposable } from "@esotericsoftware/spine-core";
import type { GLTexture } from "./GLTexture.js";
import { Color2Attribute, ColorAttribute, Mesh, Position2Attribute, TexCoordAttribute } from "./Mesh.js";
import type { Shader } from "./Shader.js";
import { ManagedWebGLRenderingContext } from "./WebGL.js";
const GL_ONE = 1;
const GL_ONE_MINUS_SRC_COLOR = 0x0301;
const GL_SRC_ALPHA = 0x0302;
const GL_ONE_MINUS_SRC_ALPHA = 0x0303;
// biome-ignore lint/correctness/noUnusedVariables: intentional
const GL_ONE_MINUS_DST_ALPHA = 0x0305;
const GL_DST_COLOR = 0x0306;
@ -58,13 +59,13 @@ export class PolygonBatcher implements Disposable {
private cullWasEnabled = false;
constructor (context: ManagedWebGLRenderingContext | WebGLRenderingContext, twoColorTint: boolean = true, maxVertices: number = 10920) {
if (maxVertices > 10920) throw new Error("Can't have more than 10920 triangles per batch: " + maxVertices);
if (maxVertices > 10920) throw new Error(`Can't have more than 10920 triangles per batch: ${maxVertices}`);
this.context = context instanceof ManagedWebGLRenderingContext ? context : new ManagedWebGLRenderingContext(context);
let attributes = twoColorTint ?
const attributes = twoColorTint ?
[new Position2Attribute(), new ColorAttribute(), new TexCoordAttribute(), new Color2Attribute()] :
[new Position2Attribute(), new ColorAttribute(), new TexCoordAttribute()];
this.mesh = new Mesh(context, attributes, maxVertices, maxVertices * 3);
let gl = this.context.gl;
const gl = this.context.gl;
this.srcColorBlend = gl.SRC_ALPHA;
this.srcAlphaBlend = gl.ONE;
this.dstBlend = gl.ONE_MINUS_SRC_ALPHA;
@ -77,7 +78,7 @@ export class PolygonBatcher implements Disposable {
this.lastTexture = null;
this.isDrawing = true;
let gl = this.context.gl;
const gl = this.context.gl;
gl.enable(gl.BLEND);
gl.blendFuncSeparate(this.srcColorBlend, this.dstBlend, this.srcAlphaBlend, this.dstBlend);
@ -100,19 +101,19 @@ export class PolygonBatcher implements Disposable {
const srcAlphaBlend = blendModeGL.srcAlpha;
const dstBlend = blendModeGL.dstRgb;
if (this.srcColorBlend == srcColorBlend && this.srcAlphaBlend == srcAlphaBlend && this.dstBlend == dstBlend) return;
if (this.srcColorBlend === srcColorBlend && this.srcAlphaBlend === srcAlphaBlend && this.dstBlend === dstBlend) return;
this.srcColorBlend = srcColorBlend;
this.srcAlphaBlend = srcAlphaBlend;
this.dstBlend = dstBlend;
if (this.isDrawing) {
this.flush();
}
let gl = this.context.gl;
const gl = this.context.gl;
gl.blendFuncSeparate(srcColorBlend, dstBlend, srcAlphaBlend, dstBlend);
}
draw (texture: GLTexture, vertices: ArrayLike<number>, indices: Array<number>) {
if (texture != this.lastTexture) {
if (texture !== this.lastTexture) {
this.flush();
this.lastTexture = texture;
} else if (this.verticesLength + vertices.length > this.mesh.getVertices().length ||
@ -120,12 +121,12 @@ export class PolygonBatcher implements Disposable {
this.flush();
}
let indexStart = this.mesh.numVertices();
const indexStart = this.mesh.numVertices();
this.mesh.getVertices().set(vertices, this.verticesLength);
this.verticesLength += vertices.length;
this.mesh.setVerticesLength(this.verticesLength)
let indicesArray = this.mesh.getIndices();
const indicesArray = this.mesh.getIndices();
for (let i = this.indicesLength, j = 0; j < indices.length; i++, j++)
indicesArray[i] = indices[j] + indexStart;
this.indicesLength += indices.length;
@ -133,7 +134,7 @@ export class PolygonBatcher implements Disposable {
}
flush () {
if (this.verticesLength == 0) return;
if (this.verticesLength === 0) return;
if (!this.lastTexture) throw new Error("No texture set.");
if (!this.shader) throw new Error("No shader set.");
this.lastTexture.bind();
@ -154,7 +155,7 @@ export class PolygonBatcher implements Disposable {
this.lastTexture = null;
this.isDrawing = false;
let gl = this.context.gl;
const gl = this.context.gl;
gl.disable(gl.BLEND);
if (PolygonBatcher.disableCulling) {
if (this.cullWasEnabled) gl.enable(gl.CULL_FACE);
@ -166,7 +167,7 @@ export class PolygonBatcher implements Disposable {
}
static getAndResetGlobalDrawCalls () {
let result = PolygonBatcher.globalDrawCalls;
const result = PolygonBatcher.globalDrawCalls;
PolygonBatcher.globalDrawCalls = 0;
return result;
}

View File

@ -27,15 +27,16 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Color, Disposable, Skeleton, MathUtils, TextureAtlasRegion } from "@esotericsoftware/spine-core";
import { Color, type Disposable, MathUtils, type Skeleton, type TextureAtlasRegion } from "@esotericsoftware/spine-core";
import { OrthoCamera } from "./Camera.js";
import { GLTexture } from "./GLTexture.js";
import type { GLTexture } from "./GLTexture.js";
import { PolygonBatcher } from "./PolygonBatcher.js";
import { Shader } from "./Shader.js";
import { ShapeRenderer } from "./ShapeRenderer.js";
import { SkeletonDebugRenderer } from "./SkeletonDebugRenderer.js";
import { SkeletonRenderer, VertexTransformer } from "./SkeletonRenderer.js";
import { SkeletonRenderer, type VertexTransformer } from "./SkeletonRenderer.js";
import { ManagedWebGLRenderingContext } from "./WebGL.js";
;
const quad = [
@ -229,22 +230,22 @@ export class SceneRenderer implements Disposable {
if (!color) color = WHITE;
// bottom left and top right corner points relative to origin
let worldOriginX = x + pivotX;
let worldOriginY = y + pivotY;
let fx = -pivotX;
let fy = -pivotY;
let fx2 = width - pivotX;
let fy2 = height - pivotY;
const worldOriginX = x + pivotX;
const worldOriginY = y + pivotY;
const fx = -pivotX;
const fy = -pivotY;
const fx2 = width - pivotX;
const fy2 = height - pivotY;
// construct corner points, start from top left and go counter clockwise
let p1x = fx;
let p1y = fy;
let p2x = fx;
let p2y = fy2;
let p3x = fx2;
let p3y = fy2;
let p4x = fx2;
let p4y = fy;
const p1x = fx;
const p1y = fy;
const p2x = fx;
const p2y = fy2;
const p3x = fx2;
const p3y = fy2;
const p4x = fx2;
const p4y = fy;
let x1 = 0;
let y1 = 0;
@ -256,9 +257,9 @@ export class SceneRenderer implements Disposable {
let y4 = 0;
// rotate
if (angle != 0) {
let cos = MathUtils.cosDeg(angle);
let sin = MathUtils.sinDeg(angle);
if (angle !== 0) {
const cos = MathUtils.cosDeg(angle);
const sin = MathUtils.sinDeg(angle);
x1 = cos * p1x - sin * p1y;
y1 = sin * p1x + cos * p1y;
@ -464,27 +465,27 @@ export class SceneRenderer implements Disposable {
}
resize (resizeMode: ResizeMode, worldWidth?: number, worldHeight?: number) {
let canvas = this.canvas;
const canvas = this.canvas;
var dpr = window.devicePixelRatio || 1;
var w = Math.round(canvas.clientWidth * dpr);
var h = Math.round(canvas.clientHeight * dpr);
if (canvas.width != w || canvas.height != h) {
if (canvas.width !== w || canvas.height !== h) {
canvas.width = w;
canvas.height = h;
}
if (resizeMode === ResizeMode.FitClip && worldWidth !== undefined && worldHeight !== undefined) {
let targetRatio = h / w, sourceRatio = worldHeight / worldWidth;
let scale = targetRatio > sourceRatio ? w / worldWidth : h / worldHeight;
const targetRatio = h / w, sourceRatio = worldHeight / worldWidth;
const scale = targetRatio > sourceRatio ? w / worldWidth : h / worldHeight;
worldWidth *= scale;
worldHeight *= scale;
this.camera.setViewport(worldWidth, worldHeight);
this.context.gl.viewport((w - worldWidth) / 2, (h - worldHeight) / 2, worldWidth, worldHeight);
} else {
if (resizeMode === ResizeMode.Fit) {
let targetWidth = this.camera.viewportWidth, targetHeight = this.camera.viewportHeight;
let targetRatio = targetHeight / targetWidth, sourceRatio = h / w;
let scale = targetRatio < sourceRatio ? targetWidth / w : targetHeight / h;
const targetWidth = this.camera.viewportWidth, targetHeight = this.camera.viewportHeight;
const targetRatio = targetHeight / targetWidth, sourceRatio = h / w;
const scale = targetRatio < sourceRatio ? targetWidth / w : targetHeight / h;
this.camera.setViewport(w * scale, h * scale);
} else if (resizeMode === ResizeMode.Expand)
this.camera.setViewport(w, h);

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Disposable, Restorable } from "@esotericsoftware/spine-core";
import type { Disposable, Restorable } from "@esotericsoftware/spine-core";
import { ManagedWebGLRenderingContext } from "./WebGL.js";
export class Shader implements Disposable, Restorable {
@ -63,7 +63,7 @@ export class Shader implements Disposable, Restorable {
}
private compile () {
let gl = this.context.gl;
const gl = this.context.gl;
try {
this.vs = this.compileShader(gl.VERTEX_SHADER, this.vertexShader);
if (!this.vs) throw new Error("Couldn't compile vertex shader.");
@ -77,13 +77,13 @@ export class Shader implements Disposable, Restorable {
}
private compileShader (type: number, source: string) {
let gl = this.context.gl;
let shader = gl.createShader(type);
const gl = this.context.gl;
const shader = gl.createShader(type);
if (!shader) throw new Error("Couldn't create shader.");
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
let error = "Couldn't compile shader: " + gl.getShaderInfoLog(shader);
const error = `Couldn't compile shader: ${gl.getShaderInfoLog(shader)}`;
gl.deleteShader(shader);
if (!gl.isContextLost()) throw new Error(error);
}
@ -91,15 +91,15 @@ export class Shader implements Disposable, Restorable {
}
private compileProgram (vs: WebGLShader, fs: WebGLShader) {
let gl = this.context.gl;
let program = gl.createProgram();
const gl = this.context.gl;
const program = gl.createProgram();
if (!program) throw new Error("Couldn't compile program.");
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
let error = "Couldn't compile shader program: " + gl.getProgramInfoLog(program);
const error = `Couldn't compile shader program: ${gl.getProgramInfoLog(program)}`;
gl.deleteProgram(program);
if (!gl.isContextLost()) throw new Error(error);
}
@ -139,43 +139,43 @@ export class Shader implements Disposable, Restorable {
}
public setUniform2x2f (uniform: string, value: ArrayLike<number>) {
let gl = this.context.gl;
const gl = this.context.gl;
this.tmp2x2.set(value);
gl.uniformMatrix2fv(this.getUniformLocation(uniform), false, this.tmp2x2);
}
public setUniform3x3f (uniform: string, value: ArrayLike<number>) {
let gl = this.context.gl;
const gl = this.context.gl;
this.tmp3x3.set(value);
gl.uniformMatrix3fv(this.getUniformLocation(uniform), false, this.tmp3x3);
}
public setUniform4x4f (uniform: string, value: ArrayLike<number>) {
let gl = this.context.gl;
const gl = this.context.gl;
this.tmp4x4.set(value);
gl.uniformMatrix4fv(this.getUniformLocation(uniform), false, this.tmp4x4);
}
public getUniformLocation (uniform: string): WebGLUniformLocation | null {
let gl = this.context.gl;
const gl = this.context.gl;
if (!this.program) throw new Error("Shader not compiled.");
let location = gl.getUniformLocation(this.program, uniform);
const location = gl.getUniformLocation(this.program, uniform);
if (!location && !gl.isContextLost()) throw new Error(`Couldn't find location for uniform ${uniform}`);
return location;
}
public getAttributeLocation (attribute: string): number {
let gl = this.context.gl;
const gl = this.context.gl;
if (!this.program) throw new Error("Shader not compiled.");
let location = gl.getAttribLocation(this.program, attribute);
if (location == -1 && !gl.isContextLost()) throw new Error(`Couldn't find location for attribute ${attribute}`);
const location = gl.getAttribLocation(this.program, attribute);
if (location === -1 && !gl.isContextLost()) throw new Error(`Couldn't find location for attribute ${attribute}`);
return location;
}
public dispose () {
this.context.removeRestorable(this);
let gl = this.context.gl;
const gl = this.context.gl;
if (this.vs) {
gl.deleteShader(this.vs);
this.vs = null;
@ -193,7 +193,7 @@ export class Shader implements Disposable, Restorable {
}
public static newColoredTextured (context: ManagedWebGLRenderingContext | WebGLRenderingContext): Shader {
let vs = `
const vs = `
attribute vec4 ${Shader.POSITION};
attribute vec4 ${Shader.COLOR};
attribute vec2 ${Shader.TEXCOORDS};
@ -208,7 +208,7 @@ void main () {
}
`;
let fs = `
const fs = `
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
@ -228,7 +228,7 @@ void main () {
}
public static newTwoColoredTextured (context: ManagedWebGLRenderingContext | WebGLRenderingContext): Shader {
let vs = `
const vs = `
attribute vec4 ${Shader.POSITION};
attribute vec4 ${Shader.COLOR};
attribute vec4 ${Shader.COLOR2};
@ -246,7 +246,7 @@ void main () {
}
`;
let fs = `
const fs = `
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
@ -269,7 +269,7 @@ void main () {
}
public static newColored (context: ManagedWebGLRenderingContext | WebGLRenderingContext): Shader {
let vs = `
const vs = `
attribute vec4 ${Shader.POSITION};
attribute vec4 ${Shader.COLOR};
uniform mat4 ${Shader.MVP_MATRIX};
@ -281,7 +281,7 @@ void main () {
}
`;
let fs = `
const fs = `
#ifdef GL_ES
#define LOWP lowp
precision mediump float;

View File

@ -27,9 +27,9 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Disposable, Color, Vector2, MathUtils } from "@esotericsoftware/spine-core";
import { Mesh, Position2Attribute, ColorAttribute } from "./Mesh.js";
import { Shader } from "./Shader.js";
import { Color, type Disposable, MathUtils, Vector2 } from "@esotericsoftware/spine-core";
import { ColorAttribute, Mesh, Position2Attribute } from "./Mesh.js";
import type { Shader } from "./Shader.js";
import { ManagedWebGLRenderingContext } from "./WebGL.js";
export class ShapeRenderer implements Disposable {
@ -46,10 +46,10 @@ export class ShapeRenderer implements Disposable {
private dstBlend: number;
constructor (context: ManagedWebGLRenderingContext | WebGLRenderingContext, maxVertices: number = 10920) {
if (maxVertices > 10920) throw new Error("Can't have more than 10920 triangles per batch: " + maxVertices);
if (maxVertices > 10920) throw new Error(`Can't have more than 10920 triangles per batch: ${maxVertices}`);
this.context = context instanceof ManagedWebGLRenderingContext ? context : new ManagedWebGLRenderingContext(context);
this.mesh = new Mesh(context, [new Position2Attribute(), new ColorAttribute()], maxVertices, 0);
let gl = this.context.gl;
const gl = this.context.gl;
this.srcColorBlend = gl.SRC_ALPHA;
this.srcAlphaBlend = gl.ONE;
this.dstBlend = gl.ONE_MINUS_SRC_ALPHA;
@ -61,7 +61,7 @@ export class ShapeRenderer implements Disposable {
this.vertexIndex = 0;
this.isDrawing = true;
let gl = this.context.gl;
const gl = this.context.gl;
gl.enable(gl.BLEND);
gl.blendFuncSeparate(this.srcColorBlend, this.dstBlend, this.srcAlphaBlend, this.dstBlend);
}
@ -72,7 +72,7 @@ export class ShapeRenderer implements Disposable {
this.dstBlend = dstBlend;
if (this.isDrawing) {
this.flush();
let gl = this.context.gl;
const gl = this.context.gl;
gl.blendFuncSeparate(srcColorBlend, dstBlend, srcAlphaBlend, dstBlend);
}
}
@ -93,8 +93,6 @@ export class ShapeRenderer implements Disposable {
line (x: number, y: number, x2: number, y2: number, color?: Color) {
this.check(ShapeType.Line, 2);
let vertices = this.mesh.getVertices();
let idx = this.vertexIndex;
if (!color) color = this.color;
this.vertex(x, y, color);
this.vertex(x2, y2, color);
@ -102,8 +100,6 @@ export class ShapeRenderer implements Disposable {
triangle (filled: boolean, x: number, y: number, x2: number, y2: number, x3: number, y3: number, color?: Color, color2?: Color, color3?: Color) {
this.check(filled ? ShapeType.Filled : ShapeType.Line, 3);
let vertices = this.mesh.getVertices();
let idx = this.vertexIndex;
if (!color) color = this.color;
if (!color2) color2 = this.color;
if (!color3) color3 = this.color;
@ -125,8 +121,6 @@ export class ShapeRenderer implements Disposable {
quad (filled: boolean, x: number, y: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number, color?: Color, color2?: Color, color3?: Color, color4?: Color) {
this.check(filled ? ShapeType.Filled : ShapeType.Line, 3);
let vertices = this.mesh.getVertices();
let idx = this.vertexIndex;
if (!color) color = this.color;
if (!color2) color2 = this.color;
if (!color3) color3 = this.color;
@ -149,11 +143,11 @@ export class ShapeRenderer implements Disposable {
rectLine (filled: boolean, x1: number, y1: number, x2: number, y2: number, width: number, color?: Color) {
this.check(filled ? ShapeType.Filled : ShapeType.Line, 8);
if (!color) color = this.color;
let t = this.tmp.set(y2 - y1, x1 - x2);
const t = this.tmp.set(y2 - y1, x1 - x2);
t.normalize();
width *= 0.5;
let tx = t.x * width;
let ty = t.y * width;
const tx = t.x * width;
const ty = t.y * width;
if (!filled) {
this.vertex(x1 + tx, y1 + ty, color);
this.vertex(x1 - tx, y1 - ty, color);
@ -185,19 +179,17 @@ export class ShapeRenderer implements Disposable {
if (count < 3) throw new Error("Polygon must contain at least 3 vertices");
this.check(ShapeType.Line, count * 2);
if (!color) color = this.color;
let vertices = this.mesh.getVertices();
let idx = this.vertexIndex;
offset <<= 1;
count <<= 1;
let firstX = polygonVertices[offset];
let firstY = polygonVertices[offset + 1];
let last = offset + count;
const firstX = polygonVertices[offset];
const firstY = polygonVertices[offset + 1];
const last = offset + count;
for (let i = offset, n = offset + count - 2; i < n; i += 2) {
let x1 = polygonVertices[i];
let y1 = polygonVertices[i + 1];
const x1 = polygonVertices[i];
const y1 = polygonVertices[i + 1];
let x2 = 0;
let y2 = 0;
@ -210,24 +202,24 @@ export class ShapeRenderer implements Disposable {
y2 = polygonVertices[i + 3];
}
this.vertex(x1, y1, color!);
this.vertex(x2, y2, color!);
this.vertex(x1, y1, color);
this.vertex(x2, y2, color);
}
}
circle (filled: boolean, x: number, y: number, radius: number, color?: Color, segments: number = 0) {
if (segments == 0) segments = Math.max(1, (6 * MathUtils.cbrt(radius)) | 0);
if (segments === 0) segments = Math.max(1, (6 * MathUtils.cbrt(radius)) | 0);
if (segments <= 0) throw new Error("segments must be > 0.");
if (!color) color = this.color;
let angle = 2 * MathUtils.PI / segments;
let cos = Math.cos(angle);
let sin = Math.sin(angle);
const angle = 2 * MathUtils.PI / segments;
const cos = Math.cos(angle);
const sin = Math.sin(angle);
let cx = radius, cy = 0;
if (!filled) {
this.check(ShapeType.Line, segments * 2 + 2);
for (let i = 0; i < segments; i++) {
this.vertex(x + cx, y + cy, color);
let temp = cx;
const temp = cx;
cx = cos * cx - sin * cy;
cy = sin * temp + cos * cy;
this.vertex(x + cx, y + cy, color);
@ -240,7 +232,7 @@ export class ShapeRenderer implements Disposable {
for (let i = 0; i < segments; i++) {
this.vertex(x, y, color);
this.vertex(x + cx, y + cy, color);
let temp = cx;
const temp = cx;
cx = cos * cx - sin * cy;
cy = sin * temp + cos * cy;
this.vertex(x + cx, y + cy, color);
@ -250,7 +242,6 @@ export class ShapeRenderer implements Disposable {
this.vertex(x + cx, y + cy, color);
}
let temp = cx;
cx = radius;
cy = 0;
this.vertex(x + cx, y + cy, color);
@ -261,20 +252,20 @@ export class ShapeRenderer implements Disposable {
if (!color) color = this.color;
// Algorithm from: http://www.antigrain.com/research/bezier_interpolation/index.html#PAGE_BEZIER_INTERPOLATION
let subdiv_step = 1 / segments;
let subdiv_step2 = subdiv_step * subdiv_step;
let subdiv_step3 = subdiv_step * subdiv_step * subdiv_step;
const subdiv_step = 1 / segments;
const subdiv_step2 = subdiv_step * subdiv_step;
const subdiv_step3 = subdiv_step * subdiv_step * subdiv_step;
let pre1 = 3 * subdiv_step;
let pre2 = 3 * subdiv_step2;
let pre4 = 6 * subdiv_step2;
let pre5 = 6 * subdiv_step3;
const pre1 = 3 * subdiv_step;
const pre2 = 3 * subdiv_step2;
const pre4 = 6 * subdiv_step2;
const pre5 = 6 * subdiv_step3;
let tmp1x = x1 - cx1 * 2 + cx2;
let tmp1y = y1 - cy1 * 2 + cy2;
const tmp1x = x1 - cx1 * 2 + cx2;
const tmp1y = y1 - cy1 * 2 + cy2;
let tmp2x = (cx1 - cx2) * 3 - x1 + x2;
let tmp2y = (cy1 - cy2) * 3 - y1 + y2;
const tmp2x = (cx1 - cx2) * 3 - x1 + x2;
const tmp2y = (cy1 - cy2) * 3 - y1 + y2;
let fx = x1;
let fy = y1;
@ -285,26 +276,26 @@ export class ShapeRenderer implements Disposable {
let ddfx = tmp1x * pre4 + tmp2x * pre5;
let ddfy = tmp1y * pre4 + tmp2y * pre5;
let dddfx = tmp2x * pre5;
let dddfy = tmp2y * pre5;
const dddfx = tmp2x * pre5;
const dddfy = tmp2y * pre5;
while (segments-- > 0) {
this.vertex(fx, fy, color!);
this.vertex(fx, fy, color);
fx += dfx;
fy += dfy;
dfx += ddfx;
dfy += ddfy;
ddfx += dddfx;
ddfy += dddfy;
this.vertex(fx, fy, color!);
this.vertex(fx, fy, color);
}
this.vertex(fx, fy, color!);
this.vertex(x2, y2, color!);
this.vertex(fx, fy, color);
this.vertex(x2, y2, color);
}
private vertex (x: number, y: number, color: Color) {
let idx = this.vertexIndex;
let vertices = this.mesh.getVertices();
const vertices = this.mesh.getVertices();
vertices[idx++] = x;
vertices[idx++] = y;
vertices[idx++] = color.r;
@ -317,13 +308,13 @@ export class ShapeRenderer implements Disposable {
end () {
if (!this.isDrawing) throw new Error("ShapeRenderer.begin() has not been called");
this.flush();
let gl = this.context.gl;
const gl = this.context.gl;
gl.disable(gl.BLEND);
this.isDrawing = false;
}
private flush () {
if (this.vertexIndex == 0) return;
if (this.vertexIndex === 0) return;
if (!this.shader) throw new Error("No shader set.");
this.mesh.setVerticesLength(this.vertexIndex);
this.mesh.draw(this.shader, this.shapeType);
@ -332,7 +323,7 @@ export class ShapeRenderer implements Disposable {
private check (shapeType: ShapeType, numVertices: number) {
if (!this.isDrawing) throw new Error("ShapeRenderer.begin() has not been called");
if (this.shapeType == shapeType) {
if (this.shapeType === shapeType) {
if (this.mesh.maxVertices() - this.mesh.numVertices() < numVertices) this.flush();
else return;
} else {

View File

@ -27,8 +27,8 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Disposable, Color, SkeletonBounds, Utils, Skeleton, RegionAttachment, MeshAttachment, PathAttachment, ClippingAttachment } from "@esotericsoftware/spine-core";
import { ShapeRenderer } from "./ShapeRenderer.js";
import { ClippingAttachment, Color, type Disposable, MeshAttachment, PathAttachment, RegionAttachment, type Skeleton, SkeletonBounds, Utils } from "@esotericsoftware/spine-core";
import type { ShapeRenderer } from "./ShapeRenderer.js";
import { ManagedWebGLRenderingContext } from "./WebGL.js";
export class SkeletonDebugRenderer implements Disposable {
@ -53,7 +53,7 @@ export class SkeletonDebugRenderer implements Disposable {
private context: ManagedWebGLRenderingContext;
private bounds = new SkeletonBounds();
private temp = new Array<number>();
private temp = [] as number[];
private vertices = Utils.newFloatArray(2 * 1024);
private static LIGHT_GRAY = new Color(192 / 255, 192 / 255, 192 / 255, 1);
private static GREEN = new Color(0, 1, 0, 1);
@ -63,22 +63,22 @@ export class SkeletonDebugRenderer implements Disposable {
}
draw (shapes: ShapeRenderer, skeleton: Skeleton, ignoredBones?: Array<string>) {
let skeletonX = skeleton.x;
let skeletonY = skeleton.y;
let gl = this.context.gl;
let srcFunc = this.premultipliedAlpha ? gl.ONE : gl.SRC_ALPHA;
const skeletonX = skeleton.x;
const skeletonY = skeleton.y;
const gl = this.context.gl;
const srcFunc = this.premultipliedAlpha ? gl.ONE : gl.SRC_ALPHA;
shapes.setBlendMode(srcFunc, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
let bones = skeleton.bones;
const bones = skeleton.bones;
if (this.drawBones) {
shapes.setColor(this.boneLineColor);
for (let i = 0, n = bones.length; i < n; i++) {
let bone = bones[i];
const bone = bones[i];
if (ignoredBones && ignoredBones.indexOf(bone.data.name) > -1) continue;
if (!bone.parent) continue;
const boneApplied = bone.applied;
let x = bone.data.length * boneApplied.a + boneApplied.worldX;
let y = bone.data.length * boneApplied.c + boneApplied.worldY;
const x = bone.data.length * boneApplied.a + boneApplied.worldX;
const y = bone.data.length * boneApplied.c + boneApplied.worldY;
shapes.rectLine(true, boneApplied.worldX, boneApplied.worldY, x, y, this.boneWidth * this.scale);
}
if (this.drawSkeletonXY) shapes.x(skeletonX, skeletonY, 4 * this.scale);
@ -86,13 +86,13 @@ export class SkeletonDebugRenderer implements Disposable {
if (this.drawRegionAttachments) {
shapes.setColor(this.attachmentLineColor);
let slots = skeleton.slots;
const slots = skeleton.slots;
for (let i = 0, n = slots.length; i < n; i++) {
let slot = slots[i];
const slot = slots[i];
if (!slot.bone.active) continue;
let attachment = slot.applied.attachment;
const attachment = slot.applied.attachment;
if (attachment instanceof RegionAttachment) {
let vertices = this.vertices;
const vertices = this.vertices;
attachment.computeWorldVertices(slot, vertices, 0, 2);
shapes.line(vertices[0], vertices[1], vertices[2], vertices[3]);
shapes.line(vertices[2], vertices[3], vertices[4], vertices[5]);
@ -103,20 +103,20 @@ export class SkeletonDebugRenderer implements Disposable {
}
if (this.drawMeshHull || this.drawMeshTriangles) {
let slots = skeleton.slots;
const slots = skeleton.slots;
for (let i = 0, n = slots.length; i < n; i++) {
let slot = slots[i];
const slot = slots[i];
if (!slot.bone.active) continue;
let attachment = slot.applied.attachment;
const attachment = slot.applied.attachment;
if (!(attachment instanceof MeshAttachment)) continue;
let vertices = this.vertices;
const vertices = this.vertices;
attachment.computeWorldVertices(skeleton, slot, 0, attachment.worldVerticesLength, vertices, 0, 2);
let triangles = attachment.triangles;
const triangles = attachment.triangles;
let hullLength = attachment.hullLength;
if (this.drawMeshTriangles) {
shapes.setColor(this.triangleLineColor);
for (let ii = 0, nn = triangles.length; ii < nn; ii += 3) {
let v1 = triangles[ii] * 2, v2 = triangles[ii + 1] * 2, v3 = triangles[ii + 2] * 2;
const v1 = triangles[ii] * 2, v2 = triangles[ii + 1] * 2, v3 = triangles[ii + 2] * 2;
shapes.triangle(false, vertices[v1], vertices[v1 + 1], //
vertices[v2], vertices[v2 + 1], //
vertices[v3], vertices[v3 + 1] //
@ -128,7 +128,7 @@ export class SkeletonDebugRenderer implements Disposable {
hullLength = (hullLength >> 1) * 2;
let lastX = vertices[hullLength - 2], lastY = vertices[hullLength - 1];
for (let ii = 0, nn = hullLength; ii < nn; ii += 2) {
let x = vertices[ii], y = vertices[ii + 1];
const x = vertices[ii], y = vertices[ii + 1];
shapes.line(x, y, lastX, lastY);
lastX = x;
lastY = y;
@ -138,34 +138,34 @@ export class SkeletonDebugRenderer implements Disposable {
}
if (this.drawBoundingBoxes) {
let bounds = this.bounds;
const bounds = this.bounds;
bounds.update(skeleton, true);
shapes.setColor(this.aabbColor);
shapes.rect(false, bounds.minX, bounds.minY, bounds.getWidth(), bounds.getHeight());
let polygons = bounds.polygons;
let boxes = bounds.boundingBoxes;
const polygons = bounds.polygons;
const boxes = bounds.boundingBoxes;
for (let i = 0, n = polygons.length; i < n; i++) {
let polygon = polygons[i];
const polygon = polygons[i];
shapes.setColor(boxes[i].color);
shapes.polygon(polygon, 0, polygon.length);
}
}
if (this.drawPaths) {
let slots = skeleton.slots;
const slots = skeleton.slots;
for (let i = 0, n = slots.length; i < n; i++) {
let slot = slots[i];
const slot = slots[i];
if (!slot.bone.active) continue;
let attachment = slot.applied.attachment;
const attachment = slot.applied.attachment;
if (!(attachment instanceof PathAttachment)) continue;
let nn = attachment.worldVerticesLength;
let world = this.temp = Utils.setArraySize(this.temp, nn, 0);
const world = this.temp = Utils.setArraySize(this.temp, nn, 0);
attachment.computeWorldVertices(skeleton, slot, 0, nn, world, 0, 2);
let color = this.pathColor;
const color = this.pathColor;
let x1 = world[2], y1 = world[3], x2 = 0, y2 = 0;
if (attachment.closed) {
shapes.setColor(color);
let cx1 = world[0], cy1 = world[1], cx2 = world[nn - 2], cy2 = world[nn - 1];
const cx1 = world[0], cy1 = world[1], cx2 = world[nn - 2], cy2 = world[nn - 1];
x2 = world[nn - 4];
y2 = world[nn - 3];
shapes.curve(x1, y1, cx1, cy1, cx2, cy2, x2, y2, 32);
@ -175,7 +175,7 @@ export class SkeletonDebugRenderer implements Disposable {
}
nn -= 4;
for (let ii = 4; ii < nn; ii += 6) {
let cx1 = world[ii], cy1 = world[ii + 1], cx2 = world[ii + 2], cy2 = world[ii + 3];
const cx1 = world[ii], cy1 = world[ii + 1], cx2 = world[ii + 2], cy2 = world[ii + 3];
x2 = world[ii + 4];
y2 = world[ii + 5];
shapes.setColor(color);
@ -192,29 +192,29 @@ export class SkeletonDebugRenderer implements Disposable {
if (this.drawBones) {
shapes.setColor(this.boneOriginColor);
for (let i = 0, n = bones.length; i < n; i++) {
let bone = bones[i];
const bone = bones[i];
if (ignoredBones && ignoredBones.indexOf(bone.data.name) > -1) continue;
let boneApplied = bone.applied;
const boneApplied = bone.applied;
shapes.circle(true, boneApplied.worldX, boneApplied.worldY, 3 * this.scale, this.boneOriginColor, 8);
}
}
if (this.drawClipping) {
let slots = skeleton.slots;
const slots = skeleton.slots;
shapes.setColor(this.clipColor)
for (let i = 0, n = slots.length; i < n; i++) {
let slot = slots[i];
const slot = slots[i];
if (!slot.bone.active) continue;
let attachment = slot.applied.attachment;
const attachment = slot.applied.attachment;
if (!(attachment instanceof ClippingAttachment)) continue;
let nn = attachment.worldVerticesLength;
let world = this.temp = Utils.setArraySize(this.temp, nn, 0);
const nn = attachment.worldVerticesLength;
const world = this.temp = Utils.setArraySize(this.temp, nn, 0);
attachment.computeWorldVertices(skeleton, slot, 0, nn, world, 0, 2);
for (let i = 0, n = world.length; i < n; i += 2) {
let x = world[i];
let y = world[i + 1];
let x2 = world[(i + 2) % world.length];
let y2 = world[(i + 3) % world.length];
const x = world[i];
const y = world[i + 1];
const x2 = world[(i + 2) % world.length];
const y2 = world[(i + 3) % world.length];
shapes.line(x, y, x2, y2);
}
}

View File

@ -27,10 +27,10 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { NumberArrayLike, Color, SkeletonClipping, Vector2, Utils, Skeleton, BlendMode, RegionAttachment, TextureAtlasRegion, MeshAttachment, ClippingAttachment } from "@esotericsoftware/spine-core";
import { GLTexture } from "./GLTexture.js";
import { PolygonBatcher } from "./PolygonBatcher.js";
import { ManagedWebGLRenderingContext } from "./WebGL.js";
import { type BlendMode, ClippingAttachment, Color, MeshAttachment, type NumberArrayLike, RegionAttachment, type Skeleton, SkeletonClipping, type TextureRegion, Utils, Vector2 } from "@esotericsoftware/spine-core";
import type { GLTexture } from "./GLTexture.js";
import type { PolygonBatcher } from "./PolygonBatcher.js";
import type { ManagedWebGLRenderingContext } from "./WebGL.js";
class Renderable {
@ -63,28 +63,28 @@ export class SkeletonRenderer {
}
draw (batcher: PolygonBatcher, skeleton: Skeleton, slotRangeStart: number = -1, slotRangeEnd: number = -1, transformer: VertexTransformer | null = null) {
let clipper = this.clipper;
let premultipliedAlpha = this.premultipliedAlpha;
let twoColorTint = this.twoColorTint;
const clipper = this.clipper;
const premultipliedAlpha = this.premultipliedAlpha;
const twoColorTint = this.twoColorTint;
let blendMode: BlendMode | null = null;
let renderable: Renderable = this.renderable;
const renderable: Renderable = this.renderable;
let uvs: NumberArrayLike;
let triangles: Array<number>;
let drawOrder = skeleton.drawOrder;
const drawOrder = skeleton.drawOrder;
let attachmentColor: Color;
let skeletonColor = skeleton.color;
let vertexSize = twoColorTint ? 12 : 8;
const skeletonColor = skeleton.color;
const vertexSize = twoColorTint ? 12 : 8;
let inRange = false;
if (slotRangeStart == -1) inRange = true;
if (slotRangeStart === -1) inRange = true;
for (let i = 0, n = drawOrder.length; i < n; i++) {
let slot = drawOrder[i];
const slot = drawOrder[i];
if (!slot.bone.active) {
clipper.clipEnd(slot);
continue;
}
if (slotRangeStart >= 0 && slotRangeStart == slot.data.index) {
if (slotRangeStart >= 0 && slotRangeStart === slot.data.index) {
inRange = true;
}
@ -93,7 +93,7 @@ export class SkeletonRenderer {
continue;
}
if (slotRangeEnd >= 0 && slotRangeEnd == slot.data.index) {
if (slotRangeEnd >= 0 && slotRangeEnd === slot.data.index) {
inRange = false;
}
@ -107,7 +107,7 @@ export class SkeletonRenderer {
attachment.computeWorldVertices(slot, renderable.vertices, 0, vertexSize);
triangles = SkeletonRenderer.QUAD_TRIANGLES;
uvs = attachment.uvs;
texture = <GLTexture>attachment.region!.texture;
texture = (attachment.region as TextureRegion).texture as GLTexture;
attachmentColor = attachment.color;
} else if (attachment instanceof MeshAttachment) {
renderable.vertices = this.vertices;
@ -119,7 +119,7 @@ export class SkeletonRenderer {
}
attachment.computeWorldVertices(skeleton, slot, 0, attachment.worldVerticesLength, renderable.vertices, 0, vertexSize);
triangles = attachment.triangles;
texture = <GLTexture>attachment.region!.texture;
texture = (attachment.region as TextureRegion).texture as GLTexture;
uvs = attachment.uvs;
attachmentColor = attachment.color;
} else if (attachment instanceof ClippingAttachment) {
@ -132,8 +132,8 @@ export class SkeletonRenderer {
}
if (texture) {
let slotColor = pose.color;
let finalColor = this.tempColor;
const slotColor = pose.color;
const finalColor = this.tempColor;
finalColor.r = skeletonColor.r * slotColor.r * attachmentColor.r;
finalColor.g = skeletonColor.g * slotColor.g * attachmentColor.g;
finalColor.b = skeletonColor.b * slotColor.b * attachmentColor.b;
@ -143,7 +143,7 @@ export class SkeletonRenderer {
finalColor.g *= finalColor.a;
finalColor.b *= finalColor.a;
}
let darkColor = this.tempColor2;
const darkColor = this.tempColor2;
if (!pose.darkColor)
darkColor.set(0, 0, 0, 1.0);
else {
@ -157,19 +157,19 @@ export class SkeletonRenderer {
darkColor.a = premultipliedAlpha ? 1.0 : 0.0;
}
let slotBlendMode = slot.data.blendMode;
if (slotBlendMode != blendMode) {
const slotBlendMode = slot.data.blendMode;
if (slotBlendMode !== blendMode) {
blendMode = slotBlendMode;
batcher.setBlendMode(blendMode, premultipliedAlpha);
}
if (clipper.isClipping() && clipper.clipTriangles(renderable.vertices, triangles, triangles.length, uvs, finalColor, darkColor, twoColorTint, vertexSize)) {
let clippedVertices = new Float32Array(clipper.clippedVertices);
let clippedTriangles = clipper.clippedTriangles;
const clippedVertices = new Float32Array(clipper.clippedVertices);
const clippedTriangles = clipper.clippedTriangles;
if (transformer) transformer(clippedVertices, clippedVertices.length, vertexSize);
batcher.draw(texture, clippedVertices, clippedTriangles);
} else {
let verts = renderable.vertices;
const verts = renderable.vertices;
if (!twoColorTint) {
for (let v = 2, u = 0, n = renderable.numFloats; v < n; v += vertexSize, u += 2) {
verts[v] = finalColor.r;
@ -193,7 +193,7 @@ export class SkeletonRenderer {
verts[v + 9] = darkColor.a;
}
}
let view = (renderable.vertices as Float32Array).subarray(0, renderable.numFloats);
const view = (renderable.vertices as Float32Array).subarray(0, renderable.numFloats);
if (transformer) transformer(renderable.vertices, renderable.numFloats, vertexSize);
batcher.draw(texture, view, triangles);
}

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { TimeKeeper, AssetManager, ManagedWebGLRenderingContext, SceneRenderer, Input, StringMap } from "./index.js";
import { AssetManager, Input, ManagedWebGLRenderingContext, SceneRenderer, type StringMap, TimeKeeper } from "./index.js";
/** An app running inside a {@link SpineCanvas}. The app life-cycle
* is as follows:
@ -55,7 +55,7 @@ export interface SpineCanvasConfig {
/* The path prefix to be used by the {@link AssetManager}. */
pathPrefix?: string;
/* The WebGL context configuration */
webglConfig?: any;
webglConfig?: WebGLContextAttributes;
}
/** Manages the life-cycle and WebGL context of a {@link SpineCanvasApp}. The app loads
@ -100,7 +100,7 @@ export class SpineCanvas {
if (config.app.loadAssets) config.app.loadAssets(this);
let loop = () => {
const loop = () => {
if (this.disposed) return;
requestAnimationFrame(loop);
this.time.update();
@ -108,7 +108,7 @@ export class SpineCanvas {
if (config.app.render) config.app.render(this);
}
let waitForAssets = () => {
const waitForAssets = () => {
if (this.disposed) return;
if (this.assetManager.isLoadingComplete()) {
if (this.assetManager.hasErrors()) {

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Matrix4, M00, M01, M02, M03, M10, M11, M12, M13, M20, M21, M22, M23, M30, M31, M32, M33 } from "./Matrix4.js";
import { M00, M01, M02, M03, M10, M11, M12, M13, M20, M21, M22, M23, M30, M31, M32, M33, type Matrix4 } from "./Matrix4.js";
export class Vector3 {
x = 0;
@ -77,7 +77,7 @@ export class Vector3 {
normalize (): Vector3 {
let len = this.length();
if (len == 0) return this;
if (len === 0) return this;
len = 1 / len;
this.x *= len;
this.y *= len;
@ -90,15 +90,15 @@ export class Vector3 {
}
multiply (matrix: Matrix4): Vector3 {
let l_mat = matrix.values;
const l_mat = matrix.values;
return this.set(this.x * l_mat[M00] + this.y * l_mat[M01] + this.z * l_mat[M02] + l_mat[M03],
this.x * l_mat[M10] + this.y * l_mat[M11] + this.z * l_mat[M12] + l_mat[M13],
this.x * l_mat[M20] + this.y * l_mat[M21] + this.z * l_mat[M22] + l_mat[M23]);
}
project (matrix: Matrix4): Vector3 {
let l_mat = matrix.values;
let l_w = 1 / (this.x * l_mat[M30] + this.y * l_mat[M31] + this.z * l_mat[M32] + l_mat[M33]);
const l_mat = matrix.values;
const l_w = 1 / (this.x * l_mat[M30] + this.y * l_mat[M31] + this.z * l_mat[M32] + l_mat[M33]);
return this.set((this.x * l_mat[M00] + this.y * l_mat[M01] + this.z * l_mat[M02] + l_mat[M03]) * l_w,
(this.x * l_mat[M10] + this.y * l_mat[M11] + this.z * l_mat[M12] + l_mat[M13]) * l_w,
(this.x * l_mat[M20] + this.y * l_mat[M21] + this.z * l_mat[M22] + l_mat[M23]) * l_w);
@ -113,9 +113,9 @@ export class Vector3 {
}
distance (v: Vector3): number {
let a = v.x - this.x;
let b = v.y - this.y;
let c = v.z - this.z;
const a = v.x - this.x;
const b = v.y - this.y;
const c = v.z - this.z;
return Math.sqrt(a * a + b * b + c * c);
}
}

View File

@ -1,3 +1,4 @@
export * from "@esotericsoftware/spine-core";
export * from "./AssetManager.js";
export * from "./Camera.js";
export * from "./CameraController.js";
@ -14,5 +15,4 @@ export * from "./SkeletonDebugRenderer.js";
export * from "./SkeletonRenderer.js";
export * from "./SpineCanvas.js";
export * from "./Vector3.js";
export * from "./WebGL.js";
export * from "@esotericsoftware/spine-core";
export * from "./WebGL.js";