@melonjs/spine-plugin 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,11 @@
1
1
  /*!
2
- * melonJS Spine plugin - v1.3.0
2
+ * melonJS Spine plugin - v1.5.0
3
3
  * http://www.melonjs.org
4
4
  * @melonjs/spine-plugin is licensed under the MIT License.
5
5
  * http://www.opensource.org/licenses/mit-license
6
6
  * @copyright (C) 2011 - 2023 AltByte Pte Ltd
7
7
  */
8
- import { event, video, Color as Color$1, Polygon, Math as Math$1, Renderable as Renderable$1, Vector2d } from 'melonjs';
8
+ import { Color as Color$1, Polygon, Math as Math$1, loader, utils, plugin, Renderable as Renderable$1, Vector2d } from 'melonjs';
9
9
 
10
10
  /******************************************************************************
11
11
  * Spine Runtimes License Agreement
@@ -11276,41 +11276,6 @@ class ManagedWebGLRenderingContext {
11276
11276
  this.restorables.splice(index, 1);
11277
11277
  }
11278
11278
  }
11279
- const ONE = 1;
11280
- const ONE_MINUS_SRC_COLOR = 0x0301;
11281
- const SRC_ALPHA = 0x0302;
11282
- const ONE_MINUS_SRC_ALPHA = 0x0303;
11283
- const ONE_MINUS_DST_ALPHA = 0x0305;
11284
- const DST_COLOR = 0x0306;
11285
- class WebGLBlendModeConverter {
11286
- static getDestGLBlendMode(blendMode) {
11287
- switch (blendMode) {
11288
- case BlendMode.Normal: return ONE_MINUS_SRC_ALPHA;
11289
- case BlendMode.Additive: return ONE;
11290
- case BlendMode.Multiply: return ONE_MINUS_SRC_ALPHA;
11291
- case BlendMode.Screen: return ONE_MINUS_SRC_ALPHA;
11292
- default: throw new Error("Unknown blend mode: " + blendMode);
11293
- }
11294
- }
11295
- static getSourceColorGLBlendMode(blendMode, premultipliedAlpha = false) {
11296
- switch (blendMode) {
11297
- case BlendMode.Normal: return premultipliedAlpha ? ONE : SRC_ALPHA;
11298
- case BlendMode.Additive: return premultipliedAlpha ? ONE : SRC_ALPHA;
11299
- case BlendMode.Multiply: return DST_COLOR;
11300
- case BlendMode.Screen: return ONE;
11301
- default: throw new Error("Unknown blend mode: " + blendMode);
11302
- }
11303
- }
11304
- static getSourceAlphaGLBlendMode(blendMode) {
11305
- switch (blendMode) {
11306
- case BlendMode.Normal: return ONE;
11307
- case BlendMode.Additive: return ONE;
11308
- case BlendMode.Multiply: return ONE_MINUS_SRC_ALPHA;
11309
- case BlendMode.Screen: return ONE_MINUS_SRC_COLOR;
11310
- default: throw new Error("Unknown blend mode: " + blendMode);
11311
- }
11312
- }
11313
- }
11314
11279
 
11315
11280
  /******************************************************************************
11316
11281
  * Spine Runtimes License Agreement
@@ -12760,6 +12725,12 @@ var VertexAttributeType;
12760
12725
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
12761
12726
  * SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
12762
12727
  *****************************************************************************/
12728
+ const GL_ONE = 1;
12729
+ const GL_ONE_MINUS_SRC_COLOR = 0x0301;
12730
+ const GL_SRC_ALPHA = 0x0302;
12731
+ const GL_ONE_MINUS_SRC_ALPHA = 0x0303;
12732
+ const GL_ONE_MINUS_DST_ALPHA = 0x0305;
12733
+ const GL_DST_COLOR = 0x0306;
12763
12734
  class PolygonBatcher {
12764
12735
  constructor(context, twoColorTint = true, maxVertices = 10920) {
12765
12736
  this.drawCalls = 0;
@@ -12797,7 +12768,11 @@ class PolygonBatcher {
12797
12768
  gl.disable(gl.CULL_FACE);
12798
12769
  }
12799
12770
  }
12800
- setBlendMode(srcColorBlend, srcAlphaBlend, dstBlend) {
12771
+ setBlendMode(blendMode, premultipliedAlpha) {
12772
+ const blendModeGL = PolygonBatcher.blendModesGL[blendMode];
12773
+ const srcColorBlend = premultipliedAlpha ? blendModeGL.srcRgbPma : blendModeGL.srcRgb;
12774
+ const srcAlphaBlend = blendModeGL.srcAlpha;
12775
+ const dstBlend = blendModeGL.dstRgb;
12801
12776
  if (this.srcColorBlend == srcColorBlend && this.srcAlphaBlend == srcAlphaBlend && this.dstBlend == dstBlend)
12802
12777
  return;
12803
12778
  this.srcColorBlend = srcColorBlend;
@@ -12805,9 +12780,9 @@ class PolygonBatcher {
12805
12780
  this.dstBlend = dstBlend;
12806
12781
  if (this.isDrawing) {
12807
12782
  this.flush();
12808
- let gl = this.context.gl;
12809
- gl.blendFuncSeparate(srcColorBlend, dstBlend, srcAlphaBlend, dstBlend);
12810
12783
  }
12784
+ let gl = this.context.gl;
12785
+ gl.blendFuncSeparate(srcColorBlend, dstBlend, srcAlphaBlend, dstBlend);
12811
12786
  }
12812
12787
  draw(texture, vertices, indices) {
12813
12788
  if (texture != this.lastTexture) {
@@ -12873,6 +12848,12 @@ class PolygonBatcher {
12873
12848
  }
12874
12849
  PolygonBatcher.disableCulling = false;
12875
12850
  PolygonBatcher.globalDrawCalls = 0;
12851
+ PolygonBatcher.blendModesGL = [
12852
+ { srcRgb: GL_SRC_ALPHA, srcRgbPma: GL_ONE, dstRgb: GL_ONE_MINUS_SRC_ALPHA, srcAlpha: GL_ONE },
12853
+ { srcRgb: GL_SRC_ALPHA, srcRgbPma: GL_ONE, dstRgb: GL_ONE, srcAlpha: GL_ONE },
12854
+ { srcRgb: GL_DST_COLOR, srcRgbPma: GL_DST_COLOR, dstRgb: GL_ONE_MINUS_SRC_ALPHA, srcAlpha: GL_ONE },
12855
+ { srcRgb: GL_ONE, srcRgbPma: GL_ONE, dstRgb: GL_ONE_MINUS_SRC_COLOR, srcAlpha: GL_ONE }
12856
+ ];
12876
12857
 
12877
12858
  /******************************************************************************
12878
12859
  * Spine Runtimes License Agreement
@@ -13493,10 +13474,6 @@ let SkeletonRenderer$2 = class SkeletonRenderer {
13493
13474
  let premultipliedAlpha = this.premultipliedAlpha;
13494
13475
  let twoColorTint = this.twoColorTint;
13495
13476
  let blendMode = null;
13496
- let tempPos = this.temp;
13497
- let tempUv = this.temp2;
13498
- let tempLight = this.temp3;
13499
- let tempDark = this.temp4;
13500
13477
  let renderable = this.renderable;
13501
13478
  let uvs;
13502
13479
  let triangles;
@@ -13589,7 +13566,7 @@ let SkeletonRenderer$2 = class SkeletonRenderer {
13589
13566
  let slotBlendMode = slot.data.blendMode;
13590
13567
  if (slotBlendMode != blendMode) {
13591
13568
  blendMode = slotBlendMode;
13592
- batcher.setBlendMode(WebGLBlendModeConverter.getSourceColorGLBlendMode(blendMode, premultipliedAlpha), WebGLBlendModeConverter.getSourceAlphaGLBlendMode(blendMode), WebGLBlendModeConverter.getDestGLBlendMode(blendMode));
13569
+ batcher.setBlendMode(blendMode, premultipliedAlpha);
13593
13570
  }
13594
13571
  if (clipper.isClipping()) {
13595
13572
  clipper.clipTriangles(renderable.vertices, renderable.numFloats, triangles, triangles.length, uvs, finalColor, darkColor, twoColorTint);
@@ -14185,7 +14162,7 @@ class LoadingScreen {
14185
14162
  let gl = renderer.context.gl;
14186
14163
  renderer.resize(ResizeMode.Expand);
14187
14164
  renderer.camera.position.set(canvas.width / 2, canvas.height / 2, 0);
14188
- renderer.batcher.setBlendMode(gl.ONE, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
14165
+ renderer.batcher.setBlendMode(BlendMode.Normal, true);
14189
14166
  if (complete) {
14190
14167
  this.fadeOut += this.timeKeeper.delta * (this.timeKeeper.totalTime < 1 ? 2 : 1);
14191
14168
  if (this.fadeOut > FADE_OUT)
@@ -14259,8 +14236,10 @@ let SPINE_LOGO_DATA = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKUAAABsCAY
14259
14236
  class SpineCanvas {
14260
14237
  /** Constructs a new spine canvas, rendering to the provided HTML canvas. */
14261
14238
  constructor(canvas, config) {
14239
+ this.config = config;
14262
14240
  /** Tracks the current time, delta, and other time related statistics. */
14263
14241
  this.time = new TimeKeeper();
14242
+ this.disposed = false;
14264
14243
  if (!config.pathPrefix)
14265
14244
  config.pathPrefix = "";
14266
14245
  if (!config.app)
@@ -14270,6 +14249,7 @@ class SpineCanvas {
14270
14249
  update: () => { },
14271
14250
  render: () => { },
14272
14251
  error: () => { },
14252
+ dispose: () => { },
14273
14253
  };
14274
14254
  if (!config.webglConfig)
14275
14255
  config.webglConfig = { alpha: true };
@@ -14282,6 +14262,8 @@ class SpineCanvas {
14282
14262
  if (config.app.loadAssets)
14283
14263
  config.app.loadAssets(this);
14284
14264
  let loop = () => {
14265
+ if (this.disposed)
14266
+ return;
14285
14267
  requestAnimationFrame(loop);
14286
14268
  this.time.update();
14287
14269
  if (config.app.update)
@@ -14290,6 +14272,8 @@ class SpineCanvas {
14290
14272
  config.app.render(this);
14291
14273
  };
14292
14274
  let waitForAssets = () => {
14275
+ if (this.disposed)
14276
+ return;
14293
14277
  if (this.assetManager.isLoadingComplete()) {
14294
14278
  if (this.assetManager.hasErrors()) {
14295
14279
  if (config.app.error)
@@ -14311,6 +14295,12 @@ class SpineCanvas {
14311
14295
  this.gl.clearColor(r, g, b, a);
14312
14296
  this.gl.clear(this.gl.COLOR_BUFFER_BIT);
14313
14297
  }
14298
+ /** Disposes the app, so the update() and render() functions are no longer called. Calls the dispose() callback.*/
14299
+ dispose() {
14300
+ if (this.config.app.dispose)
14301
+ this.config.app.dispose(this);
14302
+ this.disposed = true;
14303
+ }
14314
14304
  }
14315
14305
 
14316
14306
  var spineWebGL = {
@@ -14462,7 +14452,6 @@ var spineWebGL = {
14462
14452
  VertexAttachment: VertexAttachment,
14463
14453
  VertexAttribute: VertexAttribute,
14464
14454
  get VertexAttributeType () { return VertexAttributeType; },
14465
- WebGLBlendModeConverter: WebGLBlendModeConverter,
14466
14455
  WindowedMean: WindowedMean
14467
14456
  };
14468
14457
 
@@ -14890,45 +14879,6 @@ var spineCanvas = {
14890
14879
  WindowedMean: WindowedMean
14891
14880
  };
14892
14881
 
14893
- class AssetManager {
14894
- asset_manager;
14895
- pathPrefix;
14896
-
14897
- constructor(pathPrefix = "") {
14898
- event.once(event.VIDEO_INIT, this.initAssetManager.bind(this));
14899
- this.pathPrefix = pathPrefix;
14900
- }
14901
-
14902
- initAssetManager() {
14903
- if (video.renderer.WebGLVersion >= 1) {
14904
- this.asset_manager = new AssetManager$2(video.renderer.getContext(), this.pathPrefix);
14905
- } else {
14906
- // canvas renderer
14907
- this.asset_manager = new AssetManager$1(this.pathPrefix);
14908
- }
14909
- }
14910
-
14911
- setPrefix(pathPrefix) {
14912
- this.asset_manager.pathPrefix = pathPrefix;
14913
- }
14914
-
14915
- loadAsset(atlas, skel) {
14916
- if (atlas) {
14917
- this.asset_manager.loadTextureAtlas(atlas);
14918
- }
14919
-
14920
- if (skel.endsWith(".skel")) {
14921
- this.asset_manager.loadBinary(skel);
14922
- } else {
14923
- this.asset_manager.loadText(skel);
14924
- }
14925
- }
14926
-
14927
- loadAll() {
14928
- return this.asset_manager.loadAll();
14929
- }
14930
- }
14931
-
14932
14882
  const vertexSize = 2 + 2 + 4;
14933
14883
  const blendModeLUT = ["normal", "additive", "multiply", "screen"];
14934
14884
  const regionDebugColor = "green";
@@ -15147,28 +15097,347 @@ class SkeletonRenderer {
15147
15097
  }
15148
15098
  }
15149
15099
 
15150
- let assetManager = new AssetManager();
15100
+ var name = "@melonjs/spine-plugin";
15101
+ var version = "1.5.0";
15102
+ var description = "melonJS Spine plugin";
15103
+ var homepage = "https://github.com/melonjs/spine-plugin#readme";
15104
+ var type = "module";
15105
+ var keywords = [
15106
+ "2D",
15107
+ "HTML5",
15108
+ "javascript",
15109
+ "TypeScript",
15110
+ "ES6",
15111
+ "Canvas",
15112
+ "WebGL",
15113
+ "WebGL2",
15114
+ "melonjs",
15115
+ "plugin",
15116
+ "spine",
15117
+ "spine-runtimes",
15118
+ "spine-animation",
15119
+ "esotericsoftware"
15120
+ ];
15121
+ var repository = {
15122
+ type: "git",
15123
+ url: "git+https://github.com/melonjs/spine-plugin.git"
15124
+ };
15125
+ var bugs = {
15126
+ url: "https://github.com/melonjs/spine-plugin/issues"
15127
+ };
15128
+ var license = "MIT";
15129
+ var author = "AltByte Pte Ltd";
15130
+ var funding = "https://github.com/sponsors/melonjs";
15131
+ var engines = {
15132
+ node: ">= 19"
15133
+ };
15134
+ var main = "dist/@melonjs/spine-plugin.js";
15135
+ var module = "dist/@melonjs/spine-plugin.js";
15136
+ var types = "dist/@melonjs/spine-plugin.d.ts";
15137
+ var sideEffects = false;
15138
+ var files = [
15139
+ "dist/",
15140
+ "src/",
15141
+ "package.json",
15142
+ "README.md",
15143
+ "LICENSE",
15144
+ "CHANGELOG.md"
15145
+ ];
15146
+ var peerDependencies = {
15147
+ melonjs: "^15.12.0"
15148
+ };
15149
+ var dependencies = {
15150
+ "@esotericsoftware/spine-canvas": "^4.2.23",
15151
+ "@esotericsoftware/spine-core": "^4.2.23",
15152
+ "@esotericsoftware/spine-webgl": "^4.2.23"
15153
+ };
15154
+ var devDependencies = {
15155
+ "@babel/eslint-parser": "^7.22.15",
15156
+ "@babel/plugin-syntax-import-assertions": "^7.22.5",
15157
+ "@rollup/plugin-commonjs": "^25.0.4",
15158
+ "@rollup/plugin-json": "^6.0.0",
15159
+ "@rollup/plugin-node-resolve": "^15.2.1",
15160
+ "@rollup/plugin-replace": "^5.0.2",
15161
+ "del-cli": "^5.1.0",
15162
+ eslint: "^8.50.0",
15163
+ "eslint-plugin-jsdoc": "^46.8.2",
15164
+ rollup: "^3.29.2",
15165
+ "rollup-plugin-bundle-size": "^1.0.3",
15166
+ typescript: "^5.2.2"
15167
+ };
15168
+ var scripts = {
15169
+ build: "npm run clean && npm run lint && rollup -c --silent && npm run types",
15170
+ lint: "eslint src/**.js rollup.config.mjs",
15171
+ serve: "python3 -m http.server",
15172
+ test: "npm run serve",
15173
+ prepublishOnly: "npm run build",
15174
+ clean: "del-cli --force dist/*",
15175
+ types: "tsc"
15176
+ };
15177
+ var _package = {
15178
+ name: name,
15179
+ version: version,
15180
+ description: description,
15181
+ homepage: homepage,
15182
+ type: type,
15183
+ keywords: keywords,
15184
+ repository: repository,
15185
+ bugs: bugs,
15186
+ license: license,
15187
+ author: author,
15188
+ funding: funding,
15189
+ engines: engines,
15190
+ main: main,
15191
+ module: module,
15192
+ types: types,
15193
+ sideEffects: sideEffects,
15194
+ files: files,
15195
+ peerDependencies: peerDependencies,
15196
+ dependencies: dependencies,
15197
+ devDependencies: devDependencies,
15198
+ scripts: scripts
15199
+ };
15151
15200
 
15201
+ /**
15202
+ * @classdesc
15203
+ * An Asset Manager class to load spine assets
15204
+ */
15205
+ class AssetManager {
15206
+ asset_manager;
15207
+ pathPrefix;
15208
+
15209
+ /**
15210
+ * @param {CanvasRenderer|WebGLRenderer} renderer - a melonJS renderer instance
15211
+ * @param {string} [pathPrefix=""] - a default path prefix for assets location
15212
+ */
15213
+ constructor(renderer, pathPrefix = "") {
15214
+ this.pathPrefix = pathPrefix;
15215
+ if (renderer.WebGLVersion >= 1) {
15216
+ this.asset_manager = new AssetManager$2(renderer.getContext(), this.pathPrefix);
15217
+ } else {
15218
+ // canvas renderer
15219
+ this.asset_manager = new AssetManager$1(this.pathPrefix);
15220
+ }
15221
+
15222
+ // set the spine custom parser
15223
+ loader.setParser("spine", (data, onload, onerror) => {
15224
+ // decompose data.src for the spine loader
15225
+ const ext = utils.file.getExtension(data.src);
15226
+ const basename = utils.file.getBasename(data.src);
15227
+ const path = utils.file.getPath(data.src);
15228
+ const filename = basename + "." + ext;
15229
+
15230
+ this.setPrefix(path);
15231
+
15232
+ // load asset
15233
+ switch (ext) {
15234
+ case "atlas":
15235
+ this.loadTextureAtlas(filename, onload, onerror);
15236
+ break;
15237
+ case "json":
15238
+ this.loadText(filename, onload, onerror);
15239
+ break;
15240
+ case "skel":
15241
+ this.loadBinary(filename, onload, onerror);
15242
+ break;
15243
+ default:
15244
+ throw "Spine plugin: unknown extension when preloading spine assets";
15245
+ }
15246
+
15247
+ return 1;
15248
+ });
15249
+
15250
+ }
15251
+
15252
+ /**
15253
+ * set a default path prefix for assets location
15254
+ * @see loadAsset
15255
+ * @param {string} pathPrefix
15256
+ */
15257
+ setPrefix(pathPrefix) {
15258
+ this.asset_manager.pathPrefix = this.pathPrefix = pathPrefix;
15259
+ }
15260
+
15261
+ /**
15262
+ * define all spine assets to be loaded
15263
+ * @see setPrefix
15264
+ * @see loadAll
15265
+ * @param {string} atlas
15266
+ * @param {string} skel
15267
+ * @example
15268
+ * // "manually" load spine assets
15269
+ * Spine.assetManager.setPrefix("data/spine/");
15270
+ * Spine.assetManager.loadAsset("alien.atlas", "alien-ess.json");
15271
+ * await Spine.assetManager.loadAll();
15272
+ */
15273
+ loadAsset(atlas, skel) {
15274
+ if (atlas) {
15275
+ this.loadTextureAtlas(atlas);
15276
+ }
15277
+
15278
+ if (skel.endsWith(".skel")) {
15279
+ this.loadBinary(skel);
15280
+ } else {
15281
+ this.loadText(skel);
15282
+ }
15283
+ }
15284
+
15285
+ /**
15286
+ * load the given texture atlas
15287
+ * @param {string} atlas
15288
+ */
15289
+ loadTextureAtlas(atlas, onload, onerror) {
15290
+ return this.asset_manager.loadTextureAtlas(atlas, onload, onerror);
15291
+ }
15292
+
15293
+
15294
+ /**
15295
+ * load the given skeleton .skel file
15296
+ * @param {string} skel
15297
+ */
15298
+ loadBinary(skel, onload, onerror) {
15299
+ return this.asset_manager.loadBinary(skel, onload, onerror);
15300
+ }
15301
+
15302
+ /**
15303
+ * load the given skeleton binary file
15304
+ * @param {string} skel
15305
+ */
15306
+ loadText(skel, onload, onerror) {
15307
+ return this.asset_manager.loadText(skel, onload, onerror);
15308
+ }
15309
+
15310
+ /**
15311
+ * load all defined spine assets
15312
+ * @see loadAsset
15313
+ */
15314
+ loadAll() {
15315
+ return this.asset_manager.loadAll();
15316
+ }
15317
+
15318
+ /**
15319
+ * get the loaded skeleton data
15320
+ * @param {string} path
15321
+ */
15322
+ require(path) {
15323
+ return this.asset_manager.require(path);
15324
+ }
15325
+ }
15326
+
15327
+ /**
15328
+ * @classdesc
15329
+ * a Spine 4.x plugin implementation for melonJS
15330
+ * @augments plugin.BasePlugin
15331
+ */
15332
+ class SpinePlugin extends plugin.BasePlugin {
15333
+ constructor() {
15334
+ // call the super constructor
15335
+ super();
15336
+
15337
+ // minimum melonJS version expected to run this plugin
15338
+ this.version = peerDependencies["melonjs"];
15339
+
15340
+ // hello world
15341
+ console.log(`${name} ${version} - spine runtime ${dependencies["@esotericsoftware/spine-core"]} | ${homepage}`);
15342
+
15343
+ // instantiate the asset manager
15344
+ this.assetManager = new AssetManager(this.app.renderer);
15345
+ }
15346
+ }
15347
+
15348
+ // a temporary array used for skeleton.getBounds();
15349
+ let tempArray = [];
15350
+
15351
+ /**
15352
+ * @classdesc
15353
+ * An renderable object to render Spine animated skeleton.
15354
+ * @augments Renderable
15355
+ */
15152
15356
  class Spine extends Renderable$1 {
15153
15357
  runtime;
15154
15358
  skeleton;
15359
+ plugin;
15360
+ renderer;
15155
15361
  animationState;
15156
15362
  skeletonRenderer;
15157
- assetManager;
15158
15363
  root;
15159
15364
  boneOffset;
15160
15365
  boneSize;
15366
+ isSpineFlipped = {
15367
+ x : false,
15368
+ y : false
15369
+ };
15161
15370
 
15371
+ /**
15372
+ * Stores settings and other state for the playback of the current animation (if any).
15373
+ * @type {TrackEntry}
15374
+ * @see http://en.esotericsoftware.com/spine-api-reference#TrackEntry
15375
+ * @see setAnimation
15376
+ * @default undefined
15377
+ * @example
15378
+ * // set a default animation to "run"
15379
+ * this.setAnimation(0, "run", true);
15380
+ * ...
15381
+ * ...
15382
+ * // pause the animation
15383
+ * this.currentTrack.timeScale = 0;
15384
+ * ...
15385
+ * ...
15386
+ * // resume the animation
15387
+ * this.currentTrack.timeScale = 1;
15388
+ */
15389
+ currentTrack;
15390
+
15391
+ /**
15392
+ * @param {number} x - the x coordinates of the Spine object
15393
+ * @param {number} y - the y coordinates of the Spine object
15394
+ * @param {object} settings - Configuration parameters for the Spine object
15395
+ * @param {number} [settings.atlasFile] - the name of the atlasFile to be used to create this spine animation
15396
+ * @param {number} [settings.jsonFile] - the name of the atlasFile to be used to create this spine animation
15397
+ * @param {number} [settings.mixTime = 0.2] - the default mix duration to use when no mix duration has been defined between two animations.
15398
+ * @example
15399
+ * import * as Spine from '@melonjs/spine-plugin';
15400
+ * import * as me from 'melonjs';
15401
+ *
15402
+ * // prepare/declare assets for the preloader
15403
+ * const DataManifest = [
15404
+ * {
15405
+ * "name": "alien-ess.json",
15406
+ * "type": "spine",
15407
+ * "src": "data/spine/alien-ess.json"
15408
+ * },
15409
+ * {
15410
+ * "name": "alien.atlas",
15411
+ * "type": "spine",
15412
+ * "src": "data/spine/alien.atlas"
15413
+ * },
15414
+ * ]
15415
+ *
15416
+ * // create a new Spine Renderable
15417
+ * let spineAlien = new Spine(100, 100, {atlasFile: "alien.atlas", jsonFile: "alien-ess.json"});
15418
+ *
15419
+ * // set default animation
15420
+ * spineAlien.setAnimation(0, "death", true);
15421
+ *
15422
+ * // add it to the game world
15423
+ * me.game.world.addChild(spineAlien);
15424
+ */
15162
15425
  constructor(x, y, settings) {
15163
15426
  super(x, y, settings.width, settings.height);
15164
15427
 
15165
- if (video.renderer.WebGLVersion >= 1) {
15428
+ // ensure plugin was properly registered
15429
+ this.plugin = plugin.get(SpinePlugin);
15430
+ if (typeof this.plugin === "undefined") {
15431
+ throw "Spine plugin: plugin needs to be registered first using plugin.register";
15432
+ }
15433
+ this.renderer = this.plugin.app.renderer;
15434
+
15435
+ if (this.renderer.WebGLVersion >= 1) {
15166
15436
  this.runtime = spineWebGL;
15167
15437
  } else {
15168
15438
  this.runtime = spineCanvas;
15169
15439
  }
15170
15440
 
15171
- this.assetManager = assetManager.asset_manager;
15172
15441
  this.skeletonRenderer = new SkeletonRenderer(this.runtime);
15173
15442
 
15174
15443
  // force anchorPoint to 0,0
@@ -15179,20 +15448,26 @@ class Spine extends Renderable$1 {
15179
15448
  this.pos.z = settings.z;
15180
15449
  }
15181
15450
 
15182
- this.scaleValue = {x: 1, y: 1};
15451
+ // use internally when calulcating bounds
15183
15452
  this.boneOffset = new Vector2();
15184
15453
  this.boneSize = new Vector2();
15185
15454
 
15186
- this.mixTime = settings.mixTime || 0.2;
15455
+ // default mixTime
15456
+ this.mixTime = typeof settings.mixTime !== "undefined" ? settings.mixTime : 0.2;
15457
+
15187
15458
 
15188
15459
  if (settings.jsonFile) {
15189
15460
  this.jsonFile = settings.jsonFile;
15190
15461
  this.atlasFile = settings.atlasFile;
15191
-
15192
15462
  this.setSkeleton(this.atlasFile, this.jsonFile);
15193
15463
  }
15194
15464
  }
15195
15465
 
15466
+ /**
15467
+ * Whether to enabler the debug mode when rendering the spine object
15468
+ * @default false
15469
+ * @type {boolean}
15470
+ */
15196
15471
  get debugRendering() {
15197
15472
  return this.skeletonRenderer.debugRendering;
15198
15473
  }
@@ -15201,43 +15476,107 @@ class Spine extends Renderable$1 {
15201
15476
  this.skeletonRenderer.debugRendering = value;
15202
15477
  }
15203
15478
 
15479
+ /**
15480
+ * set and load the given skeleton atlas and json definition files
15481
+ * (use this if you did not specify any json or atlas through the constructor)
15482
+ * @param {number} [atlasFile] - the name of the atlasFile to be used to create this spine animation
15483
+ * @param {number} [jsonFile] - the name of the atlasFile to be used to create this spine animation
15484
+ * @example
15485
+ * // create a new Spine Renderable
15486
+ * let spineAlien = new Spine(100, 100);
15487
+ *
15488
+ * // set the skeleton
15489
+ * spineAlien.setSkeleton("alien.atlas", "alien-ess.json");
15490
+ *
15491
+ * // set default animation
15492
+ * spineAlien.setAnimation(0, "death", true);
15493
+ *
15494
+ * // add it to the game world
15495
+ * me.game.world.addChild(spineAlien);
15496
+ */
15204
15497
  setSkeleton(atlasFile, jsonFile) {
15205
- this.loadSpineAssets(atlasFile, jsonFile);
15206
- this.root = this.skeleton.getRootBone();
15207
- // Spine uses Y-up, melonJS uses Y-down
15208
- this.root.scaleY *= -1;
15209
- }
15210
-
15211
- loadSpineAssets(atlasFile, jsonFile) {
15212
15498
  // Create the texture atlas and skeleton data.
15213
- let atlas = this.assetManager.require(atlasFile);
15499
+ let atlas = this.plugin.assetManager.require(atlasFile);
15214
15500
  let atlasLoader = new this.runtime.AtlasAttachmentLoader(atlas);
15215
15501
  let skeletonJson = new this.runtime.SkeletonJson(atlasLoader);
15216
- let skeletonData = skeletonJson.readSkeletonData(this.assetManager.require(jsonFile));
15502
+ let skeletonData = skeletonJson.readSkeletonData(this.plugin.assetManager.require(jsonFile));
15217
15503
 
15218
15504
  // Instantiate a new skeleton based on the atlas and skeleton data.
15219
15505
  this.skeleton = new this.runtime.Skeleton(skeletonData);
15220
- this.skeleton.setToSetupPose();
15221
- this.skeleton.updateWorldTransform();
15506
+
15507
+ this.setToSetupPose();
15222
15508
 
15223
15509
  // Setup an animation state with a default mix of 0.2 seconds.
15224
15510
  var animationStateData = new this.runtime.AnimationStateData(this.skeleton.data);
15225
15511
  animationStateData.defaultMix = this.mixTime;
15226
15512
  this.animationState = new this.runtime.AnimationState(animationStateData);
15513
+
15514
+ // get a reference to the root bone
15515
+ this.root = this.skeleton.getRootBone();
15516
+ }
15517
+
15518
+ /**
15519
+ * flip the Spine skeleton on the horizontal axis (around its center)
15520
+ * @param {boolean} [flip=true] - `true` to flip this Spine object.
15521
+ * @returns {Spine} Reference to this object for method chaining
15522
+ */
15523
+ flipX(flip = true) {
15524
+ if (this.isSpineFlipped.x !== flip) {
15525
+ this.isSpineFlipped.x = flip;
15526
+ this.root.scaleX *= -1;
15527
+ this.isDirty = true;
15528
+ }
15529
+ return this;
15530
+ }
15531
+
15532
+ /**
15533
+ * flip the Spine skeleton on the vertical axis (around its center)
15534
+ * @param {boolean} [flip=true] - `true` to flip this Spine object.
15535
+ * @returns {Spine} Reference to this object for method chaining
15536
+ */
15537
+ flipY(flip = true) {
15538
+ if (this.isSpineFlipped.y !== flip) {
15539
+ this.isSpineFlipped.y = flip;
15540
+ this.root.scaleY *= -1;
15541
+ this.isDirty = true;
15542
+ }
15543
+ return this;
15227
15544
  }
15228
15545
 
15546
+ /**
15547
+ * Rotate this Spine object by the specified angle (in radians).
15548
+ * @param {number} angle - The angle to rotate (in radians)
15549
+ * @param {Vector2d|ObservableVector2d} [v] - an optional point to rotate around
15550
+ * @returns {Spine} Reference to this object for method chaining
15551
+ */
15229
15552
  rotate(angle, v) {
15230
15553
  // rotation for rootBone is in degrees (anti-clockwise)
15231
15554
  this.skeleton.getRootBone().rotation -= Math$1.radToDeg(angle) + 90;
15232
15555
  // melonJS rotate method takes radians
15233
- super.rotate(angle, v);
15556
+ return super.rotate(angle, v);
15234
15557
  }
15235
15558
 
15559
+ /**
15560
+ * scale the Spine object around his anchor point. Scaling actually applies changes
15561
+ * to the currentTransform member wich is used by the renderer to scale the object
15562
+ * when rendering. It does not scale the object itself. For example if the renderable
15563
+ * is an image, the image.width and image.height properties are unaltered but the currentTransform
15564
+ * member will be changed.
15565
+ * @param {number} x - a number representing the abscissa of the scaling vector.
15566
+ * @param {number} [y=x] - a number representing the ordinate of the scaling vector.
15567
+ * @returns {Spine} Reference to this object for method chaining
15568
+ */
15236
15569
  scale(x, y = x) {
15237
- this.scaleValue = {x, y};
15238
- super.scale(x, y);
15570
+ // untested
15571
+ return super.scale(x, y);
15239
15572
  }
15240
15573
 
15574
+ /**
15575
+ * update the bounding box for this spine object.
15576
+ * (this will automatically update the bounds of the entire skeleton animation)
15577
+ * @param {boolean} [absolute=true] - update the bounds size and position in (world) absolute coordinates
15578
+ * @returns {Bounds} this shape bounding box Rectangle object
15579
+ */
15241
15580
  updateBounds(absolute = true) {
15242
15581
  if (this.isRenderable) {
15243
15582
  let bounds = this.getBounds();
@@ -15250,13 +15589,16 @@ class Spine extends Renderable$1 {
15250
15589
  let boneOffset = this.boneOffset;
15251
15590
  let boneSize = this.boneSize;
15252
15591
 
15253
- this.skeleton.getBounds(boneOffset, boneSize);
15592
+ this.skeleton.getBounds(boneOffset, boneSize, tempArray);
15593
+
15594
+ let minX = boneOffset.x - rootBone.x,
15595
+ minY = boneOffset.y - rootBone.y;
15254
15596
 
15255
15597
  bounds.addFrame(
15256
- boneOffset.x - rootBone.x,
15257
- boneOffset.y - rootBone.y,
15258
- boneSize.x + boneOffset.x - rootBone.x,
15259
- boneSize.y + boneOffset.y - rootBone.y,
15598
+ minX,
15599
+ minY,
15600
+ minX + boneSize.x,
15601
+ minY + boneSize.y,
15260
15602
  !isIdentity ? this.currentTransform : undefined
15261
15603
  );
15262
15604
  } else {
@@ -15271,6 +15613,7 @@ class Spine extends Renderable$1 {
15271
15613
 
15272
15614
  if (absolute === true) {
15273
15615
  var absPos = this.getAbsolutePosition();
15616
+ //bounds.translate(absPos.x, absPos.y);
15274
15617
  bounds.centerOn(absPos.x + bounds.centerX, absPos.y + bounds.centerY);
15275
15618
  }
15276
15619
  return bounds;
@@ -15290,22 +15633,24 @@ class Spine extends Renderable$1 {
15290
15633
  update(dt) {
15291
15634
  if (typeof this.skeleton !== "undefined") {
15292
15635
  let rootBone = this.skeleton.getRootBone();
15293
-
15294
- // update the root bone position
15295
- if (rootBone.x !== this.pos.x) {
15296
- rootBone.x = this.pos.x;
15297
- }
15298
- if (rootBone.y !== this.pos.y) {
15299
- rootBone.y = this.pos.y;
15300
- }
15636
+ //let height = this.renderer.getHeight();
15301
15637
 
15302
15638
  // Update and apply the animation state, update the skeleton's
15303
- // world transforms and render the skeleton.
15304
15639
  this.animationState.update(dt / 1000);
15305
15640
  this.animationState.apply(this.skeleton);
15641
+
15642
+ // update the root bone position
15643
+ rootBone.x = this.pos.x;
15644
+ rootBone.y = this.pos.y;
15645
+
15646
+ // world transforms
15306
15647
  this.skeleton.updateWorldTransform();
15307
15648
 
15649
+ // update Bounds
15308
15650
  this.updateBounds();
15651
+
15652
+ // world transforms
15653
+ //this.skeleton.updateWorldTransform();
15309
15654
  }
15310
15655
  return true;
15311
15656
  }
@@ -15313,9 +15658,6 @@ class Spine extends Renderable$1 {
15313
15658
 
15314
15659
  /**
15315
15660
  * draw this spine object
15316
- * @name draw
15317
- * @memberof Spine
15318
- * @protected
15319
15661
  * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance
15320
15662
  * @param {Camera2d} [viewport] - the viewport to (re)draw
15321
15663
  */
@@ -15323,23 +15665,60 @@ class Spine extends Renderable$1 {
15323
15665
  this.skeletonRenderer.draw(renderer, this.skeleton);
15324
15666
  }
15325
15667
 
15668
+ /**
15669
+ * Sets the current animation for a track, discarding any queued animations.
15670
+ * @param {number} [track_index] - If the formerly current track entry was never applied to a skeleton, it is replaced (not mixed from). In either case trackEnd determines when the track is cleared.
15671
+ * @param {number} [index] - the animation index
15672
+ * @param {boolean} [loop= false] - If true, the animation will repeat. If false it will not, instead its last frame is applied if played beyond its duration.
15673
+ * @returns {TrackEntry} A track entry to allow further customization of animation playback. References to the track entry must not be kept after the dispose event occurs.
15674
+ */
15326
15675
  setAnimationByIndex(track_index, index, loop = false) {
15327
15676
  if (index < 0 || index >= this.skeleton.data.animations.length) {
15328
15677
  return (console.log("Animation Index not found"));
15329
15678
  } else {
15330
- this.animationState.setAnimation(track_index, this.skeleton.data.animations[index].name, loop);
15679
+ return this.setAnimation(track_index, this.skeleton.data.animations[index].name, loop);
15331
15680
  }
15332
15681
  }
15333
15682
 
15683
+ /**
15684
+ * Sets the current animation for a track, discarding any queued animations.
15685
+ * @param {number} [track_index] - If the formerly current track entry was never applied to a skeleton, it is replaced (not mixed from). In either case trackEnd determines when the track is cleared.
15686
+ * @param {string} [name] - the animation name
15687
+ * @param {boolean} [loop= false] - If true, the animation will repeat. If false it will not, instead its last frame is applied if played beyond its duration.
15688
+ * @returns {TrackEntry} A track entry to allow further customization of animation playback. References to the track entry must not be kept after the dispose event occurs.
15689
+ * @example
15690
+ * // set the current animation
15691
+ * spineAlien.setAnimation(0, "death", true);
15692
+ */
15334
15693
  setAnimation(track_index, name, loop = false) {
15335
- this.animationState.setAnimation(track_index, name, loop);
15694
+ this.currentTrack = this.animationState.setAnimation(track_index, name, loop);
15695
+ return this.currentTrack;
15336
15696
  }
15337
15697
 
15698
+ /**
15699
+ * return true if the given animation name is the current running animation for the current track.
15700
+ * @name isCurrentAnimation
15701
+ * @param {string} name - animation name
15702
+ * @returns {boolean}
15703
+ * @example
15704
+ * if (!this.isCurrentAnimation("death")) {
15705
+ * // do something funny...
15706
+ * }
15707
+ */
15708
+ isCurrentAnimation(name) {
15709
+ return typeof this.currentTrack !== "undefined" && this.currentTrack.animation.name === name;
15710
+ }
15711
+
15712
+ /**
15713
+ * Adds an animation to be played after the current or last queued animation for a track, and sets the track entry's mixDuration.
15714
+ * @param {number} [delay=0] - If > 0, sets delay. If <= 0, the delay set is the duration of the previous track entry minus any mix duration plus the specified `delay` (ie the mix ends at (`delay` = 0) or before (`delay` < 0) the previous track entry duration). If the previous entry is looping, its next loop completion is used instead of its duration.
15715
+ * @return {TrackEntry} A track entry to allow further customization of animation playback. References to the track entry must not be kept after the dispose} event occurs.
15716
+ */
15338
15717
  addAnimationByIndex(track_index, index, loop = false, delay = 0) {
15339
15718
  if (index < 0 || index >= this.skeleton.data.animations.length) {
15340
15719
  return (console.log("Animation Index not found"));
15341
15720
  } else {
15342
- this.animationState.addAnimation(track_index, this.skeleton.data.animations[index].name, loop, delay);
15721
+ return this.addAnimation(track_index, this.skeleton.data.animations[index].name, loop, delay);
15343
15722
  }
15344
15723
  }
15345
15724
 
@@ -15363,21 +15742,57 @@ class Spine extends Renderable$1 {
15363
15742
  };
15364
15743
  }
15365
15744
 
15745
+ /**
15746
+ * Set the default mix duration to use when no mix duration has been defined between two animations.
15747
+ * @param {number} mixTime
15748
+ */
15366
15749
  setDefaultMixTime(mixTime) {
15367
- this.animationState.data.defaultMix = mixTime;
15750
+ this.animationState.data.defaultMix = this.mixTime = mixTime;
15368
15751
  }
15369
15752
 
15753
+ /**
15754
+ * Sets a mix duration by animation name.
15755
+ */
15370
15756
  setTransitionMixTime(firstAnimation, secondAnimation, mixTime) {
15371
15757
  this.animationState.setMix(firstAnimation, secondAnimation, mixTime);
15372
15758
  }
15373
15759
 
15760
+ /**
15761
+ * Sets a skin by name.
15762
+ * @param {string} skinName
15763
+ * @example
15764
+ * // create a new Spine Renderable
15765
+ * let spineAlien = new Spine(100, 100, {atlasFile: "mix-and-match-pma.atlas", jsonFile: "mix-and-match-pro.json"});
15766
+ *
15767
+ * // set default animation
15768
+ * spineAlien.setAnimation(0, "dance", true);
15769
+ *
15770
+ * // set default skin
15771
+ * spineAlien.setSkinByName("full-skins/girl");
15772
+ *
15773
+ * // add it to the game world
15774
+ * me.game.world.addChild(spineAlien);
15775
+ */
15374
15776
  setSkinByName(skinName) {
15375
15777
  this.skeleton.setSkinByName(skinName);
15376
15778
  }
15377
15779
 
15780
+ /**
15781
+ * Reset this slot to the setup pose.
15782
+ */
15378
15783
  setToSetupPose() {
15379
15784
  this.skeleton.setToSetupPose();
15785
+ // Spine uses Y-up, melonJS uses Y-down
15786
+ this.skeleton.getRootBone().scaleY *= -1;
15787
+ this.skeleton.updateWorldTransform();
15788
+ // reset flip flags
15789
+ this.isSpineFlipped.y = false;
15790
+ this.isSpineFlipped.x = false;
15791
+ // reset reference to current track entry
15792
+ this.currentTrack = undefined;
15793
+ // mark the object as dirty
15794
+ this.isDirty = true;
15380
15795
  }
15381
15796
  }
15382
15797
 
15383
- export { assetManager, Spine as default };
15798
+ export { SpinePlugin, Spine as default };