Add PixiJS v8 support to spine (#2641)
* add v8 support to spine! * Renamed examples folder for consistency. * Gitignore dist. * Tsconfig. * Package json. * Modification due to strictNullChecks=true. * Run tsfmt. * Use clipper.clipTriangles not deprecated version. * Aligned example to spine-pixi (v7). * Fix clipping dark tint wrong param. * Removed useless clipper. * Push texture issue repro example * fix attachment.uvs by copying them * SlotObject alpha connected to skeleton and slot alpha. * add topology for future v8 release * Dark tint rendered is enabled if at least one slot has dark tint, or by configuration. Fixed clipping while using dark tint. * Optimized clipping by using clipTrianglesUnpacked. * Repro example for clipping issue. * Aligned constructor and from signature of spine-pixi(-v7) to v8. Deprecated old signatures. * Removed useless function. * Fixed clipping issue flagging attachment as dirty if indices change. * Clipping attachments clip slot object through Pixi Graphics masks. * Add autoUpdate in SpineFromOptions * Added javadoc to pixiv8 * Updated pixi7 examples to use SpineFromOptions interface * Aligned atlas loader to use texturePreference for bundles. * Add pool to manage slot objects masks * Fixed minor issues with SpineDebugRenderer * Aligned spine-pixi-v8 with latest spine-core * Updated build and publish script --------- Co-authored-by: Davide Tantillo <iamdjj@gmail.com>
1
.gitignore
vendored
@ -146,6 +146,7 @@ spine-ts/spine-webgl/dist
|
||||
spine-ts/spine-player/dist
|
||||
spine-ts/spine-threejs/dist
|
||||
spine-ts/spine-pixi/dist
|
||||
spine-ts/spine-pixi-v8/dist
|
||||
spine-libgdx/gradle
|
||||
spine-libgdx/gradlew
|
||||
spine-libgdx/gradlew.bat
|
||||
|
||||
@ -10,7 +10,8 @@ up into multiple modules:
|
||||
1. `spine-threejs/`, a self-contained [THREE.JS](https://threejs.org/) backend, built on the core classes.
|
||||
1. `spine-player/`, a self-contained player to easily display Spine animations on your website, built on the core classes and WebGL backend.
|
||||
1. `spine-phaser/`, a [Phaser](https://phaser.io/) backend, built on the core classes.
|
||||
1. `spine-pixi/`, a [PixiJS](https://pixijs.com/) backend, built on the core classes.
|
||||
1. `spine-pixi/`, a [PixiJS v7](https://pixijs.com/) backend, built on the core classes.
|
||||
1. `spine-pixi-v8/`, a [PixiJS v8](https://pixijs.com/) backend, built on the core classes.
|
||||
|
||||
In most cases, the `spine-player` module is best suited for your needs. Please refer to the [Spine Web Player documentation](https://esotericsoftware.com/spine-player) for more information.
|
||||
|
||||
@ -18,7 +19,7 @@ For documentation of the core API in `spine-core`, please refer to our [Spine Ru
|
||||
|
||||
For documentation of `spine-phaser`, please refer to our [spine-phaser Guide](https://esotericsoftware.com/spine-phaser).
|
||||
|
||||
For documentation of `spine-pixi`, please refer to our [spine-pixi Guide](https://esotericsoftware.com/spine-pixi).
|
||||
For documentation of `spine-pixi` and `spine-pixi`, please refer to our [spine-pixi Guide](https://esotericsoftware.com/spine-pixi).
|
||||
|
||||
For documentation of `spine-canvaskit`, please refer to our [spine-canvaskit Guide](https://esotericsoftware.com/spine-canvaskit).
|
||||
|
||||
@ -79,6 +80,9 @@ You can include a module in your project via a `<script>` tag from the [unpkg](h
|
||||
|
||||
// spine-pixi
|
||||
<script src="https://unpkg.com/@esotericsoftware/spine-pixi@4.2.*/dist/iife/spine-pixi.js"></script>
|
||||
|
||||
// spine-pixi-v8
|
||||
<script src="https://unpkg.com/@esotericsoftware/spine-pixi-v8@4.2.*/dist/iife/spine-pixi-v8.js"></script>
|
||||
```
|
||||
|
||||
We also provide `js.map` source maps. They will be automatically fetched from unpkg when debugging code of a spine-module in Chrome, Firefox, or Safari, mapping the JavaScript code back to its original TypeScript sources.
|
||||
@ -98,6 +102,7 @@ npm install @esotericsoftware/spine-player
|
||||
npm install @esotericsoftware/spine-threejs
|
||||
npm install @esotericsoftware/spine-phaser
|
||||
npm install @esotericsoftware/spine-pixi
|
||||
npm install @esotericsoftware/spine-pixi-v8
|
||||
```
|
||||
|
||||
spine-ts modules are provided in the [ECMAScript format](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules), which can be consumed natively by all modern browsers, or bundled by tools like [webpack](https://webpack.js.org/), [Babel](https://babeljs.io/), [Parcel](https://parceljs.org/), or [esbuild](https://esbuild.github.io/). You can import functions and classes from a spine-ts module in your JavaScript or TypeScript code using the `import` syntax to get access to all exported constants, functions, and classes of a module:
|
||||
|
||||
@ -21,6 +21,7 @@ then
|
||||
spine-player/dist/iife/* \
|
||||
spine-threejs/dist/iife/* \
|
||||
spine-pixi/dist/iife/* \
|
||||
spine-pixi-v8/dist/iife/* \
|
||||
spine-phaser/dist/iife/* \
|
||||
spine-player/css/spine-player.css
|
||||
curl -f -F "file=@spine-ts.zip" "$TS_UPDATE_URL$BRANCH"
|
||||
|
||||
@ -54,6 +54,31 @@
|
||||
<li><a href="/spine-pixi/example/physics4.html">Physics IV</a></li>
|
||||
<li><a href="/spine-pixi/example/slot-objects.html">Slot Objects</a></li>
|
||||
</ul>
|
||||
<li>Pixi V8</li>
|
||||
<ul>
|
||||
<li><a href="/spine-pixi-v8/example/index.html">Basic example</a></li>
|
||||
<li><a href="/spine-pixi-v8/example/manual-loading.html">Manual Loading</a></li>
|
||||
<li>
|
||||
<a href="/spine-pixi-v8/example/events-example.html">Events example</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-pixi-v8/example/mix-and-match-example.html">Mix and match example</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-pixi-v8/example/simple-input.html">Simple input</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-pixi-v8/example/mouse-following.html">Mouse following</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/spine-pixi-v8/example/control-bones-example.html">Control bones example</a>
|
||||
</li>
|
||||
<li><a href="/spine-pixi-v8/example/physics.html">Physics</a></li>
|
||||
<li><a href="/spine-pixi-v8/example/physics2.html">Physics II</a></li>
|
||||
<li><a href="/spine-pixi-v8/example/physics3.html">Physics III</a></li>
|
||||
<li><a href="/spine-pixi-v8/example/physics4.html">Physics IV</a></li>
|
||||
<li><a href="/spine-pixi-v8/example/slot-objects.html">Slot Objects</a></li>
|
||||
</ul>
|
||||
<li>Phaser</li>
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
58
spine-ts/package-lock.json
generated
@ -15,6 +15,7 @@
|
||||
"spine-player",
|
||||
"spine-threejs",
|
||||
"spine-pixi",
|
||||
"spine-pixi-v8",
|
||||
"spine-canvaskit",
|
||||
"spine-webgl"
|
||||
],
|
||||
@ -77,6 +78,10 @@
|
||||
"resolved": "spine-pixi",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@esotericsoftware/spine-pixi-v8": {
|
||||
"resolved": "spine-pixi-v8",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@esotericsoftware/spine-player": {
|
||||
"resolved": "spine-player",
|
||||
"link": true
|
||||
@ -290,6 +295,16 @@
|
||||
"integrity": "sha512-pUrWq3V5PiSGFLeLxoGqReTZmiiXwY3jRkIG5sLLKjyqNxrwm/04b4nw7LSmGWJcKk59XOM/YRTUwOzo4MMlow==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@xmldom/xmldom": {
|
||||
"version": "0.8.10",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
|
||||
"integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"dev": true,
|
||||
@ -2010,6 +2025,13 @@
|
||||
"dev": true,
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/parse-svg-path": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz",
|
||||
"integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"dev": true,
|
||||
@ -2057,6 +2079,31 @@
|
||||
"eventemitter3": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/pixi.js": {
|
||||
"version": "8.5.2",
|
||||
"resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-8.5.2.tgz",
|
||||
"integrity": "sha512-TOt9g8ifOj4R9DN9ST1M8t2nvnuhr5oWL5YW9ywFLbnOVgFMDcEz+Xek5Mo8Xr64D+QU3qre3IFgreBlsHxTNw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@pixi/colord": "^2.9.6",
|
||||
"@types/css-font-loading-module": "^0.0.12",
|
||||
"@types/earcut": "^2.1.4",
|
||||
"@webgpu/types": "^0.1.40",
|
||||
"@xmldom/xmldom": "^0.8.10",
|
||||
"earcut": "^2.2.4",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"ismobilejs": "^1.1.1",
|
||||
"parse-svg-path": "^0.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/pixi.js/node_modules/@webgpu/types": {
|
||||
"version": "0.1.51",
|
||||
"resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.51.tgz",
|
||||
"integrity": "sha512-ktR3u64NPjwIViNCck+z9QeyN0iPkQCUOQ07ZCV1RzlkfP+olLTeEZ95O1QHS+v4w9vJeY9xj/uJuSphsHy5rQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/posix-character-classes": {
|
||||
"version": "0.1.1",
|
||||
"dev": true,
|
||||
@ -3133,6 +3180,17 @@
|
||||
"@pixi/text": "^7.2.4"
|
||||
}
|
||||
},
|
||||
"spine-pixi-v8": {
|
||||
"name": "@esotericsoftware/spine-pixi-v8",
|
||||
"version": "4.2.62",
|
||||
"license": "LicenseRef-LICENSE",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-core": "4.2.62"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pixi.js": "^8.4.0"
|
||||
}
|
||||
},
|
||||
"spine-player": {
|
||||
"name": "@esotericsoftware/spine-player",
|
||||
"version": "4.2.62",
|
||||
|
||||
@ -8,8 +8,8 @@
|
||||
],
|
||||
"scripts": {
|
||||
"prepublish": "npm run clean && npm run build",
|
||||
"clean": "npx rimraf spine-core/dist spine-canvas/dist spine-canvaskit/dist spine-webgl/dist spine-phaser/dist spine-player/dist spine-threejs/dist spine-pixi/dist",
|
||||
"build": "npm run clean && npm run build:modules && concurrently \"npm run build:core\" \"npm run build:canvas\" \"npm run build:canvaskit\" \"npm run build:webgl\" \"npm run build:phaser\" \"npm run build:player\" \"npm run build:threejs\" \"npm run build:pixi\"",
|
||||
"clean": "npx rimraf spine-core/dist spine-canvas/dist spine-canvaskit/dist spine-webgl/dist spine-phaser/dist spine-player/dist spine-threejs/dist spine-pixi/dist spine-pixi-v8/dist",
|
||||
"build": "npm run clean && npm run build:modules && concurrently \"npm run build:core\" \"npm run build:canvas\" \"npm run build:canvaskit\" \"npm run build:webgl\" \"npm run build:phaser\" \"npm run build:player\" \"npm run build:threejs\" \"npm run build:pixi\" \"npm run build:pixi-v8\"",
|
||||
"postbuild": "npm run minify",
|
||||
"build:modules": "npx tsc -b -clean && npx tsc -b",
|
||||
"build:core": "npx esbuild --bundle spine-core/src/index.ts --tsconfig=spine-core/tsconfig.json --sourcemap --outfile=spine-core/dist/iife/spine-core.js --format=iife --global-name=spine",
|
||||
@ -20,8 +20,9 @@
|
||||
"build:phaser": "npx esbuild --bundle spine-phaser/src/index.ts --tsconfig=spine-phaser/tsconfig.json --sourcemap --outfile=spine-phaser/dist/iife/spine-phaser.js --external:Phaser --alias:phaser=Phaser --format=iife --global-name=spine",
|
||||
"build:threejs": "npx esbuild --bundle spine-threejs/src/index.ts --tsconfig=spine-threejs/tsconfig.json --sourcemap --outfile=spine-threejs/dist/iife/spine-threejs.js --external:three --format=iife --global-name=spine",
|
||||
"build:pixi": "npx esbuild --bundle spine-pixi/src/index.ts --tsconfig=spine-pixi/tsconfig.json --sourcemap --outfile=spine-pixi/dist/iife/spine-pixi.js --external:@pixi/* --format=iife --global-name=spine",
|
||||
"minify": "npx esbuild --minify spine-core/dist/iife/spine-core.js --outfile=spine-core/dist/iife/spine-core.min.js && npx esbuild --minify spine-canvas/dist/iife/spine-canvas.js --outfile=spine-canvas/dist/iife/spine-canvas.min.js && npx esbuild --minify spine-canvaskit/dist/iife/spine-canvaskit.js --outfile=spine-canvaskit/dist/iife/spine-canvaskit.min.js && npx esbuild --minify spine-player/dist/iife/spine-player.js --outfile=spine-player/dist/iife/spine-player.min.js && npx esbuild --minify spine-phaser/dist/iife/spine-phaser.js --outfile=spine-phaser/dist/iife/spine-phaser.min.js && npx esbuild --minify spine-webgl/dist/iife/spine-webgl.js --outfile=spine-webgl/dist/iife/spine-webgl.min.js && npx esbuild --minify spine-threejs/dist/iife/spine-threejs.js --outfile=spine-threejs/dist/iife/spine-threejs.min.js && npx esbuild --minify spine-pixi/dist/iife/spine-pixi.js --outfile=spine-pixi/dist/iife/spine-pixi.min.js",
|
||||
"dev": "concurrently \"npx live-server\" \"npm run dev:canvas\" \"npm run dev:canvaskit\" \"npm run dev:webgl\" \"npm run dev:phaser\" \"npm run dev:player\" \"npm run dev:threejs\" \"npm run dev:pixi\" \"npm run dev:modules\"",
|
||||
"build:pixi-v8": "npx esbuild --bundle spine-pixi-v8/src/index.ts --tsconfig=spine-pixi-v8/tsconfig.json --sourcemap --outfile=spine-pixi-v8/dist/iife/spine-pixi-v8.js --external:pixi.js --format=iife --global-name=spine",
|
||||
"minify": "npx esbuild --minify spine-core/dist/iife/spine-core.js --outfile=spine-core/dist/iife/spine-core.min.js && npx esbuild --minify spine-canvas/dist/iife/spine-canvas.js --outfile=spine-canvas/dist/iife/spine-canvas.min.js && npx esbuild --minify spine-canvaskit/dist/iife/spine-canvaskit.js --outfile=spine-canvaskit/dist/iife/spine-canvaskit.min.js && npx esbuild --minify spine-player/dist/iife/spine-player.js --outfile=spine-player/dist/iife/spine-player.min.js && npx esbuild --minify spine-phaser/dist/iife/spine-phaser.js --outfile=spine-phaser/dist/iife/spine-phaser.min.js && npx esbuild --minify spine-webgl/dist/iife/spine-webgl.js --outfile=spine-webgl/dist/iife/spine-webgl.min.js && npx esbuild --minify spine-threejs/dist/iife/spine-threejs.js --outfile=spine-threejs/dist/iife/spine-threejs.min.js && npx esbuild --minify spine-pixi/dist/iife/spine-pixi.js --outfile=spine-pixi/dist/iife/spine-pixi.min.js && npx esbuild --minify spine-pixi-v8/dist/iife/spine-pixi-v8.js --outfile=spine-pixi-v8/dist/iife/spine-pixi-v8.min.js",
|
||||
"dev": "concurrently \"npx live-server\" \"npm run dev:canvas\" \"npm run dev:canvaskit\" \"npm run dev:webgl\" \"npm run dev:phaser\" \"npm run dev:player\" \"npm run dev:threejs\" \"npm run dev:pixi\" \"npm run dev:pixi-v8\" \"npm run dev:modules\"",
|
||||
"dev:modules": "npm run build:modules -- --watch",
|
||||
"dev:canvas": "npm run build:canvas -- --watch",
|
||||
"dev:canvaskit": "npm run build:canvaskit -- --watch",
|
||||
@ -29,7 +30,8 @@
|
||||
"dev:phaser": "npm run build:phaser -- --watch",
|
||||
"dev:player": "npm run build:player -- --watch",
|
||||
"dev:threejs": "npm run build:threejs -- --watch",
|
||||
"dev:pixi": "npm run build:pixi -- --watch"
|
||||
"dev:pixi": "npm run build:pixi -- --watch",
|
||||
"dev:pixi-v8": "npm run build:pixi-v8 -- --watch"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -57,6 +59,7 @@
|
||||
"spine-player",
|
||||
"spine-threejs",
|
||||
"spine-pixi",
|
||||
"spine-pixi-v8",
|
||||
"spine-canvaskit",
|
||||
"spine-webgl"
|
||||
],
|
||||
|
||||
@ -17,6 +17,7 @@ sed -i '' "s/$currentVersion/$newVersion/" spine-canvaskit/package.json
|
||||
sed -i '' "s/$currentVersion/$newVersion/" spine-core/package.json
|
||||
sed -i '' "s/$currentVersion/$newVersion/" spine-phaser/package.json
|
||||
sed -i '' "s/$currentVersion/$newVersion/" spine-pixi/package.json
|
||||
sed -i '' "s/$currentVersion/$newVersion/" spine-pixi-v8/package.json
|
||||
sed -i '' "s/$currentVersion/$newVersion/" spine-player/package.json
|
||||
sed -i '' "s/$currentVersion/$newVersion/" spine-threejs/package.json
|
||||
sed -i '' "s/$currentVersion/$newVersion/" spine-webgl/package.json
|
||||
|
||||
3
spine-ts/spine-pixi-v8/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# spine-ts Pixi.js v8
|
||||
|
||||
Please see the top-level [README.md](../README.md) for more information.
|
||||
174
spine-ts/spine-pixi-v8/example/assets/celestial-circus-pma.atlas
Normal file
@ -0,0 +1,174 @@
|
||||
celestial-circus-pma.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
scale: 0.4
|
||||
arm-back-down
|
||||
bounds: 324, 401, 38, 82
|
||||
rotate: 90
|
||||
arm-back-up
|
||||
bounds: 290, 44, 83, 116
|
||||
rotate: 90
|
||||
arm-front-down
|
||||
bounds: 706, 2, 36, 78
|
||||
rotate: 90
|
||||
arm-front-up
|
||||
bounds: 860, 138, 77, 116
|
||||
bench
|
||||
bounds: 725, 256, 189, 48
|
||||
body-bottom
|
||||
bounds: 879, 868, 154, 124
|
||||
rotate: 90
|
||||
body-top
|
||||
bounds: 725, 128, 126, 133
|
||||
rotate: 90
|
||||
chest
|
||||
bounds: 408, 26, 104, 93
|
||||
cloud-back
|
||||
bounds: 752, 378, 202, 165
|
||||
cloud-front
|
||||
bounds: 2, 2, 325, 196
|
||||
rotate: 90
|
||||
collar
|
||||
bounds: 786, 13, 47, 26
|
||||
ear
|
||||
bounds: 1002, 643, 20, 28
|
||||
eye-back-shadow
|
||||
bounds: 428, 395, 14, 10
|
||||
eye-front-shadow
|
||||
bounds: 704, 529, 24, 14
|
||||
eye-reflex-back
|
||||
bounds: 860, 128, 8, 7
|
||||
rotate: 90
|
||||
eye-reflex-front
|
||||
bounds: 726, 386, 10, 7
|
||||
eye-white-back
|
||||
bounds: 835, 23, 13, 16
|
||||
eye-white-front
|
||||
bounds: 1005, 1000, 22, 17
|
||||
rotate: 90
|
||||
eyelashes-down-back
|
||||
bounds: 232, 329, 11, 6
|
||||
rotate: 90
|
||||
eyelashes-down-front
|
||||
bounds: 913, 851, 15, 6
|
||||
rotate: 90
|
||||
eyelashes-top-back
|
||||
bounds: 408, 395, 18, 10
|
||||
eyelashes-top-front
|
||||
bounds: 702, 179, 30, 16
|
||||
rotate: 90
|
||||
face
|
||||
bounds: 514, 26, 93, 102
|
||||
rotate: 90
|
||||
feathers-back
|
||||
bounds: 954, 625, 46, 46
|
||||
feathers-front
|
||||
bounds: 706, 40, 72, 86
|
||||
fringe-middle-back
|
||||
bounds: 200, 6, 33, 52
|
||||
rotate: 90
|
||||
fringe-middle-front
|
||||
bounds: 878, 76, 60, 50
|
||||
rotate: 90
|
||||
fringe-side-back
|
||||
bounds: 780, 41, 27, 94
|
||||
rotate: 90
|
||||
fringe-side-front
|
||||
bounds: 939, 161, 26, 93
|
||||
glove-bottom-back
|
||||
bounds: 954, 572, 51, 41
|
||||
rotate: 90
|
||||
glove-bottom-front
|
||||
bounds: 916, 256, 47, 48
|
||||
hair-back-1
|
||||
bounds: 444, 395, 132, 306
|
||||
rotate: 90
|
||||
hair-back-2
|
||||
bounds: 438, 211, 80, 285
|
||||
rotate: 90
|
||||
hair-back-3
|
||||
bounds: 719, 306, 70, 268
|
||||
rotate: 90
|
||||
hair-back-4
|
||||
bounds: 438, 121, 88, 262
|
||||
rotate: 90
|
||||
hair-back-5
|
||||
bounds: 438, 293, 88, 279
|
||||
rotate: 90
|
||||
hair-back-6
|
||||
bounds: 200, 41, 88, 286
|
||||
hair-hat-shadow
|
||||
bounds: 232, 398, 90, 41
|
||||
hand-back
|
||||
bounds: 954, 673, 60, 47
|
||||
rotate: 90
|
||||
hand-front
|
||||
bounds: 967, 172, 53, 60
|
||||
hat-back
|
||||
bounds: 954, 802, 64, 45
|
||||
rotate: 90
|
||||
hat-front
|
||||
bounds: 780, 70, 96, 56
|
||||
head-back
|
||||
bounds: 618, 17, 102, 86
|
||||
rotate: 90
|
||||
jabot
|
||||
bounds: 967, 234, 70, 55
|
||||
rotate: 90
|
||||
leg-back
|
||||
bounds: 232, 441, 210, 333
|
||||
leg-front
|
||||
bounds: 444, 529, 258, 320
|
||||
logo-brooch
|
||||
bounds: 954, 545, 16, 25
|
||||
mouth
|
||||
bounds: 408, 121, 22, 6
|
||||
neck
|
||||
bounds: 232, 342, 39, 56
|
||||
rotate: 90
|
||||
nose
|
||||
bounds: 742, 529, 6, 7
|
||||
rotate: 90
|
||||
nose-highlight
|
||||
bounds: 719, 300, 4, 4
|
||||
nose-shadow
|
||||
bounds: 869, 128, 7, 8
|
||||
pupil-back
|
||||
bounds: 730, 529, 10, 14
|
||||
pupil-front
|
||||
bounds: 254, 21, 12, 18
|
||||
rope-back
|
||||
bounds: 232, 383, 10, 492
|
||||
rotate: 90
|
||||
rope-front
|
||||
bounds: 232, 383, 10, 492
|
||||
rotate: 90
|
||||
rope-front-bottom
|
||||
bounds: 954, 735, 42, 65
|
||||
skirt
|
||||
bounds: 2, 776, 440, 246
|
||||
sock-bow
|
||||
bounds: 408, 407, 33, 32
|
||||
spine-logo-body
|
||||
bounds: 879, 853, 13, 32
|
||||
rotate: 90
|
||||
star-big
|
||||
bounds: 939, 141, 18, 24
|
||||
rotate: 90
|
||||
star-medium
|
||||
bounds: 742, 537, 6, 8
|
||||
rotate: 90
|
||||
star-small
|
||||
bounds: 719, 378, 3, 4
|
||||
rotate: 90
|
||||
underskirt
|
||||
bounds: 2, 329, 445, 228
|
||||
rotate: 90
|
||||
underskirt-back
|
||||
bounds: 444, 851, 433, 171
|
||||
wing-back
|
||||
bounds: 290, 129, 146, 252
|
||||
wing-front
|
||||
bounds: 704, 545, 304, 248
|
||||
rotate: 90
|
||||
BIN
spine-ts/spine-pixi-v8/example/assets/celestial-circus-pma.png
Normal file
|
After Width: | Height: | Size: 799 KiB |
3758
spine-ts/spine-pixi-v8/example/assets/celestial-circus-pro.json
Normal file
BIN
spine-ts/spine-pixi-v8/example/assets/celestial-circus-pro.skel
Normal file
173
spine-ts/spine-pixi-v8/example/assets/celestial-circus.atlas
Normal file
@ -0,0 +1,173 @@
|
||||
celestial-circus.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
scale: 0.4
|
||||
arm-back-down
|
||||
bounds: 324, 401, 38, 82
|
||||
rotate: 90
|
||||
arm-back-up
|
||||
bounds: 290, 44, 83, 116
|
||||
rotate: 90
|
||||
arm-front-down
|
||||
bounds: 706, 2, 36, 78
|
||||
rotate: 90
|
||||
arm-front-up
|
||||
bounds: 860, 138, 77, 116
|
||||
bench
|
||||
bounds: 725, 256, 189, 48
|
||||
body-bottom
|
||||
bounds: 879, 868, 154, 124
|
||||
rotate: 90
|
||||
body-top
|
||||
bounds: 725, 128, 126, 133
|
||||
rotate: 90
|
||||
chest
|
||||
bounds: 408, 26, 104, 93
|
||||
cloud-back
|
||||
bounds: 752, 378, 202, 165
|
||||
cloud-front
|
||||
bounds: 2, 2, 325, 196
|
||||
rotate: 90
|
||||
collar
|
||||
bounds: 786, 13, 47, 26
|
||||
ear
|
||||
bounds: 1002, 643, 20, 28
|
||||
eye-back-shadow
|
||||
bounds: 428, 395, 14, 10
|
||||
eye-front-shadow
|
||||
bounds: 704, 529, 24, 14
|
||||
eye-reflex-back
|
||||
bounds: 860, 128, 8, 7
|
||||
rotate: 90
|
||||
eye-reflex-front
|
||||
bounds: 726, 386, 10, 7
|
||||
eye-white-back
|
||||
bounds: 835, 23, 13, 16
|
||||
eye-white-front
|
||||
bounds: 1005, 1000, 22, 17
|
||||
rotate: 90
|
||||
eyelashes-down-back
|
||||
bounds: 232, 329, 11, 6
|
||||
rotate: 90
|
||||
eyelashes-down-front
|
||||
bounds: 913, 851, 15, 6
|
||||
rotate: 90
|
||||
eyelashes-top-back
|
||||
bounds: 408, 395, 18, 10
|
||||
eyelashes-top-front
|
||||
bounds: 702, 179, 30, 16
|
||||
rotate: 90
|
||||
face
|
||||
bounds: 514, 26, 93, 102
|
||||
rotate: 90
|
||||
feathers-back
|
||||
bounds: 954, 625, 46, 46
|
||||
feathers-front
|
||||
bounds: 706, 40, 72, 86
|
||||
fringe-middle-back
|
||||
bounds: 200, 6, 33, 52
|
||||
rotate: 90
|
||||
fringe-middle-front
|
||||
bounds: 878, 76, 60, 50
|
||||
rotate: 90
|
||||
fringe-side-back
|
||||
bounds: 780, 41, 27, 94
|
||||
rotate: 90
|
||||
fringe-side-front
|
||||
bounds: 939, 161, 26, 93
|
||||
glove-bottom-back
|
||||
bounds: 954, 572, 51, 41
|
||||
rotate: 90
|
||||
glove-bottom-front
|
||||
bounds: 916, 256, 47, 48
|
||||
hair-back-1
|
||||
bounds: 444, 395, 132, 306
|
||||
rotate: 90
|
||||
hair-back-2
|
||||
bounds: 438, 211, 80, 285
|
||||
rotate: 90
|
||||
hair-back-3
|
||||
bounds: 719, 306, 70, 268
|
||||
rotate: 90
|
||||
hair-back-4
|
||||
bounds: 438, 121, 88, 262
|
||||
rotate: 90
|
||||
hair-back-5
|
||||
bounds: 438, 293, 88, 279
|
||||
rotate: 90
|
||||
hair-back-6
|
||||
bounds: 200, 41, 88, 286
|
||||
hair-hat-shadow
|
||||
bounds: 232, 398, 90, 41
|
||||
hand-back
|
||||
bounds: 954, 673, 60, 47
|
||||
rotate: 90
|
||||
hand-front
|
||||
bounds: 967, 172, 53, 60
|
||||
hat-back
|
||||
bounds: 954, 802, 64, 45
|
||||
rotate: 90
|
||||
hat-front
|
||||
bounds: 780, 70, 96, 56
|
||||
head-back
|
||||
bounds: 618, 17, 102, 86
|
||||
rotate: 90
|
||||
jabot
|
||||
bounds: 967, 234, 70, 55
|
||||
rotate: 90
|
||||
leg-back
|
||||
bounds: 232, 441, 210, 333
|
||||
leg-front
|
||||
bounds: 444, 529, 258, 320
|
||||
logo-brooch
|
||||
bounds: 954, 545, 16, 25
|
||||
mouth
|
||||
bounds: 408, 121, 22, 6
|
||||
neck
|
||||
bounds: 232, 342, 39, 56
|
||||
rotate: 90
|
||||
nose
|
||||
bounds: 742, 529, 6, 7
|
||||
rotate: 90
|
||||
nose-highlight
|
||||
bounds: 719, 300, 4, 4
|
||||
nose-shadow
|
||||
bounds: 869, 128, 7, 8
|
||||
pupil-back
|
||||
bounds: 730, 529, 10, 14
|
||||
pupil-front
|
||||
bounds: 254, 21, 12, 18
|
||||
rope-back
|
||||
bounds: 232, 383, 10, 492
|
||||
rotate: 90
|
||||
rope-front
|
||||
bounds: 232, 383, 10, 492
|
||||
rotate: 90
|
||||
rope-front-bottom
|
||||
bounds: 954, 735, 42, 65
|
||||
skirt
|
||||
bounds: 2, 776, 440, 246
|
||||
sock-bow
|
||||
bounds: 408, 407, 33, 32
|
||||
spine-logo-body
|
||||
bounds: 879, 853, 13, 32
|
||||
rotate: 90
|
||||
star-big
|
||||
bounds: 939, 141, 18, 24
|
||||
rotate: 90
|
||||
star-medium
|
||||
bounds: 742, 537, 6, 8
|
||||
rotate: 90
|
||||
star-small
|
||||
bounds: 719, 378, 3, 4
|
||||
rotate: 90
|
||||
underskirt
|
||||
bounds: 2, 329, 445, 228
|
||||
rotate: 90
|
||||
underskirt-back
|
||||
bounds: 444, 851, 433, 171
|
||||
wing-back
|
||||
bounds: 290, 129, 146, 252
|
||||
wing-front
|
||||
bounds: 704, 545, 304, 248
|
||||
rotate: 90
|
||||
BIN
spine-ts/spine-pixi-v8/example/assets/celestial-circus.png
Normal file
|
After Width: | Height: | Size: 790 KiB |
86
spine-ts/spine-pixi-v8/example/assets/cloud-pot-pma.atlas
Normal file
@ -0,0 +1,86 @@
|
||||
cloud-pot-pma.png
|
||||
size: 1024, 512
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
scale: 0.5
|
||||
cloud-base-1
|
||||
bounds: 2, 300, 233, 210
|
||||
cloud-base-10
|
||||
bounds: 214, 113, 97, 101
|
||||
cloud-base-2
|
||||
bounds: 2, 90, 210, 208
|
||||
cloud-base-3
|
||||
bounds: 237, 346, 175, 164
|
||||
cloud-base-4
|
||||
bounds: 414, 347, 176, 163
|
||||
cloud-base-5
|
||||
bounds: 313, 89, 145, 125
|
||||
cloud-base-6
|
||||
bounds: 744, 374, 161, 136
|
||||
cloud-base-7
|
||||
bounds: 592, 361, 150, 149
|
||||
cloud-base-8
|
||||
bounds: 237, 216, 154, 128
|
||||
cloud-base-9
|
||||
bounds: 907, 402, 107, 108
|
||||
cloud-cheeks
|
||||
bounds: 2, 9, 218, 79
|
||||
cloud-eyes-closed
|
||||
bounds: 744, 350, 132, 22
|
||||
cloud-eyes-open
|
||||
bounds: 592, 333, 133, 26
|
||||
cloud-eyes-reflex
|
||||
bounds: 393, 224, 120, 17
|
||||
rotate: 90
|
||||
cloud-mouth-closed
|
||||
bounds: 907, 374, 49, 16
|
||||
cloud-mouth-open
|
||||
bounds: 222, 15, 59, 35
|
||||
leaf-big
|
||||
bounds: 214, 218, 20, 49
|
||||
leaf-small
|
||||
bounds: 958, 373, 17, 30
|
||||
rotate: 90
|
||||
petal-1
|
||||
bounds: 283, 2, 26, 18
|
||||
petal-2
|
||||
bounds: 283, 22, 28, 17
|
||||
rotate: 90
|
||||
petal-3
|
||||
bounds: 214, 269, 29, 21
|
||||
rotate: 90
|
||||
pot-base
|
||||
bounds: 222, 52, 76, 59
|
||||
pot-eyes-closed
|
||||
bounds: 878, 363, 46, 9
|
||||
pot-eyes-open
|
||||
bounds: 222, 2, 40, 11
|
||||
pot-mouth-open
|
||||
bounds: 990, 374, 14, 16
|
||||
pot-mouth-pouty
|
||||
bounds: 300, 93, 18, 10
|
||||
rotate: 90
|
||||
pot-mouth-smile
|
||||
bounds: 300, 77, 14, 10
|
||||
rotate: 90
|
||||
pot-mouth-smile-big
|
||||
bounds: 878, 352, 20, 9
|
||||
rain-blue
|
||||
bounds: 926, 360, 12, 18
|
||||
rotate: 90
|
||||
rain-color
|
||||
bounds: 264, 4, 9, 17
|
||||
rotate: 90
|
||||
rain-green
|
||||
bounds: 900, 349, 12, 18
|
||||
rotate: 90
|
||||
rain-white
|
||||
bounds: 727, 337, 12, 22
|
||||
rain-white-reflex
|
||||
bounds: 2, 2, 5, 10
|
||||
rotate: 90
|
||||
stem
|
||||
bounds: 907, 392, 8, 105
|
||||
rotate: 90
|
||||
stem-end
|
||||
bounds: 300, 62, 13, 13
|
||||
BIN
spine-ts/spine-pixi-v8/example/assets/cloud-pot-pma.png
Normal file
|
After Width: | Height: | Size: 350 KiB |
842
spine-ts/spine-pixi-v8/example/assets/cloud-pot.json
Normal file
@ -0,0 +1,842 @@
|
||||
{
|
||||
"skeleton": {
|
||||
"hash": "CKnF82un6n8",
|
||||
"spine": "4.2.22",
|
||||
"x": -345,
|
||||
"y": -272846.84,
|
||||
"width": 756,
|
||||
"height": 273927.84,
|
||||
"images": "./images/",
|
||||
"audio": ""
|
||||
},
|
||||
"bones": [
|
||||
{ "name": "root" },
|
||||
{ "name": "pot-control", "parent": "root", "x": 5, "y": 42, "color": "8828ffff", "icon": "arrowsB" },
|
||||
{ "name": "cloud", "parent": "pot-control", "x": 26.5, "y": 772, "color": "1ee8c0ff", "icon": "circle" },
|
||||
{ "name": "cloud-base-1", "parent": "cloud", "x": -4, "y": 57, "color": "b0d5eaff" },
|
||||
{ "name": "cloud-base-2", "parent": "cloud-base-1", "x": 148.5, "y": -18.5, "color": "b0d5eaff" },
|
||||
{ "name": "cloud-base-3", "parent": "cloud-base-1", "x": -182, "y": -87.5, "color": "b0d5eaff" },
|
||||
{ "name": "cloud-base-4", "parent": "cloud", "x": -31.5, "y": -77, "color": "b0d5eaff" },
|
||||
{ "name": "cloud-base-5", "parent": "cloud-base-4", "x": 177.5, "y": 8, "color": "b0d5eaff" },
|
||||
{ "name": "cloud-base-6", "parent": "cloud-base-1", "x": -150.5, "y": 40, "color": "b0d5eaff" },
|
||||
{ "name": "cloud-base-7", "parent": "cloud-base-1", "x": 8.5, "y": 36.5, "color": "b0d5eaff" },
|
||||
{ "name": "cloud-base-8", "parent": "cloud-base-2", "x": 3.5, "y": 68.5, "color": "b0d5eaff" },
|
||||
{ "name": "cloud-base-9", "parent": "cloud-base-3", "x": -83.5, "y": 30.5, "color": "b0d5eaff" },
|
||||
{ "name": "cloud-base-10", "parent": "cloud-base-5", "x": 137, "y": 54.5, "color": "b0d5eaff" },
|
||||
{ "name": "rain-blue", "parent": "cloud", "x": 102.49, "y": -26, "color": "2360e3ff", "icon": "diamond" },
|
||||
{ "name": "rain-color", "parent": "cloud", "x": -39.42, "y": -26, "color": "2360e3ff", "icon": "diamond" },
|
||||
{ "name": "rain-green", "parent": "cloud", "x": 35.08, "y": -26, "color": "2360e3ff", "icon": "diamond" },
|
||||
{ "name": "rain-white", "parent": "cloud", "x": -103.92, "y": -26, "color": "2360e3ff", "icon": "diamond" },
|
||||
{ "name": "pot", "parent": "pot-control", "x": -5, "y": -42, "color": "8828ffff" },
|
||||
{ "name": "pot-face", "parent": "pot", "x": -1.06, "y": 28.16, "color": "f38383ff", "icon": "gear" },
|
||||
{
|
||||
"name": "leaf-big",
|
||||
"parent": "pot",
|
||||
"length": 46.73,
|
||||
"rotation": 119.24,
|
||||
"x": 4.04,
|
||||
"y": 95.05,
|
||||
"color": "abe323ff"
|
||||
},
|
||||
{ "name": "leaf-big-tip", "parent": "leaf-big", "length": 46.73, "x": 46.73, "color": "abe323ff" },
|
||||
{
|
||||
"name": "leaf-small",
|
||||
"parent": "pot",
|
||||
"length": 51.32,
|
||||
"rotation": 50.93,
|
||||
"x": 10.16,
|
||||
"y": 96.81,
|
||||
"color": "abe323ff"
|
||||
},
|
||||
{
|
||||
"name": "stem",
|
||||
"parent": "pot",
|
||||
"length": 104.76,
|
||||
"rotation": 90,
|
||||
"x": 7.24,
|
||||
"y": 92.61,
|
||||
"color": "abe323ff"
|
||||
},
|
||||
{ "name": "stem2", "parent": "stem", "length": 69.84, "x": 104.76, "color": "abe323ff" },
|
||||
{ "name": "stem3", "parent": "stem2", "length": 34.92, "x": 69.84, "color": "abe323ff" },
|
||||
{
|
||||
"name": "petal-3",
|
||||
"parent": "stem3",
|
||||
"length": 37.74,
|
||||
"rotation": 1.03,
|
||||
"x": 30.73,
|
||||
"y": 0.64,
|
||||
"color": "2381e3ff"
|
||||
},
|
||||
{
|
||||
"name": "petal-1",
|
||||
"parent": "stem3",
|
||||
"length": 40.11,
|
||||
"rotation": 70.18,
|
||||
"x": 34.13,
|
||||
"y": 3.02,
|
||||
"color": "2381e3ff"
|
||||
},
|
||||
{
|
||||
"name": "petal-2",
|
||||
"parent": "stem3",
|
||||
"length": 48.62,
|
||||
"rotation": -80.34,
|
||||
"x": 32.09,
|
||||
"y": -4.46,
|
||||
"color": "2381e3ff"
|
||||
},
|
||||
{ "name": "cloud-face", "parent": "cloud", "y": 14.93, "color": "9e82ffff", "icon": "arrowsB" }
|
||||
],
|
||||
"slots": [
|
||||
{ "name": "rain/rain-green", "bone": "rain-green", "attachment": "rain-green" },
|
||||
{ "name": "rain/rain-blue", "bone": "rain-blue", "attachment": "rain-blue" },
|
||||
{ "name": "rain/rain-color", "bone": "rain-color", "attachment": "rain-color" },
|
||||
{ "name": "rain/rain-white", "bone": "rain-white", "attachment": "rain-white" },
|
||||
{ "name": "rain/rain-white-reflex", "bone": "rain-white", "attachment": "rain-white-reflex" },
|
||||
{ "name": "flower/petal-1", "bone": "petal-1", "attachment": "petal-1" },
|
||||
{ "name": "flower/petal-2", "bone": "petal-2", "attachment": "petal-2" },
|
||||
{ "name": "flower/petal-3", "bone": "petal-3", "attachment": "petal-3" },
|
||||
{ "name": "flower/stem", "bone": "stem", "attachment": "stem" },
|
||||
{ "name": "flower/leaf-big", "bone": "leaf-big", "attachment": "leaf-big" },
|
||||
{ "name": "flower/leaf-small", "bone": "leaf-small", "attachment": "leaf-small" },
|
||||
{ "name": "flower/stem-end", "bone": "stem3", "attachment": "stem-end" },
|
||||
{ "name": "pot/pot-base", "bone": "pot", "attachment": "pot-base" },
|
||||
{ "name": "pot/pot-mouth", "bone": "pot-face", "attachment": "pot-mouth-smile-big" },
|
||||
{ "name": "pot/pot-eyes", "bone": "pot-face", "attachment": "pot-eyes-open" },
|
||||
{ "name": "cloud/cloud-base/cloud-base-1", "bone": "cloud-base-1", "attachment": "cloud-base-1" },
|
||||
{ "name": "cloud/cloud-base/cloud-base-2", "bone": "cloud-base-2", "attachment": "cloud-base-2" },
|
||||
{ "name": "cloud/cloud-base/cloud-base-3", "bone": "cloud-base-3", "attachment": "cloud-base-3" },
|
||||
{ "name": "cloud/cloud-base/cloud-base-4", "bone": "cloud-base-4", "attachment": "cloud-base-4" },
|
||||
{ "name": "cloud/cloud-base/cloud-base-5", "bone": "cloud-base-5", "attachment": "cloud-base-5" },
|
||||
{ "name": "cloud/cloud-base/cloud-base-6", "bone": "cloud-base-6", "attachment": "cloud-base-6" },
|
||||
{ "name": "cloud/cloud-base/cloud-base-7", "bone": "cloud-base-7", "attachment": "cloud-base-7" },
|
||||
{ "name": "cloud/cloud-base/cloud-base-8", "bone": "cloud-base-8", "attachment": "cloud-base-8" },
|
||||
{ "name": "cloud/cloud-base/cloud-base-9", "bone": "cloud-base-9", "attachment": "cloud-base-9" },
|
||||
{ "name": "cloud/cloud-base/cloud-base-10", "bone": "cloud-base-10", "attachment": "cloud-base-10" },
|
||||
{ "name": "cloud/cloud-cheeks", "bone": "cloud-face", "attachment": "cloud-cheeks" },
|
||||
{ "name": "cloud/cloud-eyes", "bone": "cloud-face", "attachment": "cloud-eyes-open" },
|
||||
{ "name": "cloud/cloud-eyes-reflex", "bone": "cloud-face", "attachment": "cloud-eyes-reflex" },
|
||||
{ "name": "cloud/cloud-mouth", "bone": "cloud-face", "attachment": "cloud-mouth-closed" }
|
||||
],
|
||||
"physics": [
|
||||
{
|
||||
"name": "cloud",
|
||||
"order": 25,
|
||||
"bone": "cloud",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"inertia": 0.5,
|
||||
"strength": 172.8,
|
||||
"damping": 0.8571,
|
||||
"mass": 3
|
||||
},
|
||||
{
|
||||
"name": "cloud-face",
|
||||
"order": 24,
|
||||
"bone": "cloud-face",
|
||||
"x": 0.1923,
|
||||
"y": 0.141,
|
||||
"limit": 500,
|
||||
"inertia": 0.5,
|
||||
"damping": 0.15
|
||||
},
|
||||
{
|
||||
"name": "pot-face",
|
||||
"order": 23,
|
||||
"bone": "pot-face",
|
||||
"x": 0.1667,
|
||||
"y": 0.1026,
|
||||
"limit": 500,
|
||||
"inertia": 0.5,
|
||||
"strength": 137.3,
|
||||
"damping": 0.6078
|
||||
},
|
||||
{
|
||||
"name": "cloud-base/cloud-base-1",
|
||||
"order": 4,
|
||||
"bone": "cloud-base-1",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"limit": 500,
|
||||
"inertia": 0.3741,
|
||||
"strength": 134.7,
|
||||
"damping": 0.8163,
|
||||
"mass": 2.8
|
||||
},
|
||||
{
|
||||
"name": "cloud-base/cloud-base-2",
|
||||
"order": 5,
|
||||
"bone": "cloud-base-2",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"limit": 300,
|
||||
"inertia": 0.3741,
|
||||
"strength": 134.7,
|
||||
"damping": 0.8163,
|
||||
"mass": 2.8
|
||||
},
|
||||
{
|
||||
"name": "cloud-base/cloud-base-3",
|
||||
"order": 6,
|
||||
"bone": "cloud-base-3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"limit": 300,
|
||||
"inertia": 0.3741,
|
||||
"strength": 134.7,
|
||||
"damping": 0.8163,
|
||||
"mass": 2.8
|
||||
},
|
||||
{
|
||||
"name": "cloud-base/cloud-base-4",
|
||||
"order": 7,
|
||||
"bone": "cloud-base-4",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"limit": 500,
|
||||
"inertia": 0.3741,
|
||||
"strength": 134.7,
|
||||
"damping": 0.8163,
|
||||
"mass": 2.8
|
||||
},
|
||||
{
|
||||
"name": "cloud-base/cloud-base-5",
|
||||
"order": 8,
|
||||
"bone": "cloud-base-5",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"limit": 300,
|
||||
"inertia": 0.3741,
|
||||
"strength": 134.7,
|
||||
"damping": 0.8163,
|
||||
"mass": 2.8
|
||||
},
|
||||
{
|
||||
"name": "cloud-base/cloud-base-6",
|
||||
"order": 9,
|
||||
"bone": "cloud-base-6",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"limit": 300,
|
||||
"inertia": 0.3741,
|
||||
"strength": 134.7,
|
||||
"damping": 0.8163,
|
||||
"mass": 2.8
|
||||
},
|
||||
{
|
||||
"name": "cloud-base/cloud-base-7",
|
||||
"order": 10,
|
||||
"bone": "cloud-base-7",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"limit": 300,
|
||||
"inertia": 0.3741,
|
||||
"strength": 134.7,
|
||||
"damping": 0.8163,
|
||||
"mass": 2.8
|
||||
},
|
||||
{
|
||||
"name": "cloud-base/cloud-base-8",
|
||||
"order": 11,
|
||||
"bone": "cloud-base-8",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"limit": 300,
|
||||
"inertia": 0.3741,
|
||||
"strength": 134.7,
|
||||
"damping": 0.8163,
|
||||
"mass": 2.8
|
||||
},
|
||||
{
|
||||
"name": "cloud-base/cloud-base-9",
|
||||
"order": 12,
|
||||
"bone": "cloud-base-9",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"limit": 300,
|
||||
"inertia": 0.3741,
|
||||
"strength": 134.7,
|
||||
"damping": 0.8163,
|
||||
"mass": 2.8
|
||||
},
|
||||
{
|
||||
"name": "cloud-base/cloud-base-10",
|
||||
"order": 13,
|
||||
"bone": "cloud-base-10",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"limit": 300,
|
||||
"inertia": 0.3741,
|
||||
"strength": 134.7,
|
||||
"damping": 0.8163,
|
||||
"mass": 2.8
|
||||
},
|
||||
{
|
||||
"name": "plant/leaf-big",
|
||||
"order": 14,
|
||||
"bone": "leaf-big",
|
||||
"rotate": 0.7532,
|
||||
"shearX": 0.2468,
|
||||
"limit": 500,
|
||||
"inertia": 0.5,
|
||||
"strength": 160.5,
|
||||
"damping": 0.8367,
|
||||
"mass": 4
|
||||
},
|
||||
{
|
||||
"name": "plant/leaf-big-tip",
|
||||
"order": 22,
|
||||
"bone": "leaf-big-tip",
|
||||
"rotate": 1,
|
||||
"limit": 500,
|
||||
"inertia": 0.5,
|
||||
"strength": 160.5,
|
||||
"damping": 0.8367,
|
||||
"mass": 4
|
||||
},
|
||||
{
|
||||
"name": "plant/leaf-small",
|
||||
"order": 15,
|
||||
"bone": "leaf-small",
|
||||
"rotate": 0.6026,
|
||||
"limit": 500,
|
||||
"inertia": 0.5,
|
||||
"strength": 160.5,
|
||||
"damping": 0.8367,
|
||||
"mass": 4
|
||||
},
|
||||
{
|
||||
"name": "plant/petal-1",
|
||||
"order": 19,
|
||||
"bone": "petal-1",
|
||||
"rotate": 1,
|
||||
"limit": 500,
|
||||
"inertia": 0.5,
|
||||
"strength": 164.6,
|
||||
"damping": 0.6531,
|
||||
"mass": 2.6
|
||||
},
|
||||
{
|
||||
"name": "plant/petal-2",
|
||||
"order": 21,
|
||||
"bone": "petal-2",
|
||||
"rotate": 1,
|
||||
"limit": 500,
|
||||
"inertia": 0.5,
|
||||
"strength": 164.6,
|
||||
"damping": 0.6531,
|
||||
"mass": 2.6
|
||||
},
|
||||
{
|
||||
"name": "plant/petal-3",
|
||||
"order": 20,
|
||||
"bone": "petal-3",
|
||||
"rotate": 1,
|
||||
"limit": 500,
|
||||
"inertia": 0.5,
|
||||
"strength": 164.6,
|
||||
"damping": 0.7823,
|
||||
"mass": 3.83
|
||||
},
|
||||
{
|
||||
"name": "plant/stem",
|
||||
"order": 16,
|
||||
"bone": "stem",
|
||||
"rotate": 0.8205,
|
||||
"limit": 700,
|
||||
"inertia": 0.5,
|
||||
"strength": 152.4,
|
||||
"damping": 0.9388,
|
||||
"mass": 2.6
|
||||
},
|
||||
{
|
||||
"name": "plant/stem2",
|
||||
"order": 17,
|
||||
"bone": "stem2",
|
||||
"rotate": 0.8205,
|
||||
"limit": 700,
|
||||
"inertia": 0.5,
|
||||
"strength": 152.4,
|
||||
"damping": 0.9388,
|
||||
"mass": 2.6
|
||||
},
|
||||
{
|
||||
"name": "plant/stem3",
|
||||
"order": 18,
|
||||
"bone": "stem3",
|
||||
"rotate": 0.8205,
|
||||
"limit": 700,
|
||||
"inertia": 0.5,
|
||||
"strength": 152.4,
|
||||
"damping": 0.9388,
|
||||
"mass": 2.6
|
||||
},
|
||||
{
|
||||
"name": "rain/rain-blue",
|
||||
"order": 3,
|
||||
"bone": "rain-blue",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"strength": 0,
|
||||
"gravity": 70
|
||||
},
|
||||
{
|
||||
"name": "rain/rain-color",
|
||||
"order": 2,
|
||||
"bone": "rain-color",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"strength": 0,
|
||||
"gravity": 70
|
||||
},
|
||||
{
|
||||
"name": "rain/rain-green",
|
||||
"order": 1,
|
||||
"bone": "rain-green",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"strength": 0,
|
||||
"gravity": 70
|
||||
},
|
||||
{ "name": "rain/rain-white", "bone": "rain-white", "x": 1, "y": 1, "strength": 0, "gravity": 70 }
|
||||
],
|
||||
"skins": [
|
||||
{
|
||||
"name": "default",
|
||||
"attachments": {
|
||||
"cloud/cloud-base/cloud-base-1": {
|
||||
"cloud-base-1": { "width": 465, "height": 420 }
|
||||
},
|
||||
"cloud/cloud-base/cloud-base-2": {
|
||||
"cloud-base-2": { "width": 420, "height": 415 }
|
||||
},
|
||||
"cloud/cloud-base/cloud-base-3": {
|
||||
"cloud-base-3": { "width": 349, "height": 327 }
|
||||
},
|
||||
"cloud/cloud-base/cloud-base-4": {
|
||||
"cloud-base-4": { "width": 352, "height": 326 }
|
||||
},
|
||||
"cloud/cloud-base/cloud-base-5": {
|
||||
"cloud-base-5": { "width": 289, "height": 250 }
|
||||
},
|
||||
"cloud/cloud-base/cloud-base-6": {
|
||||
"cloud-base-6": { "width": 322, "height": 272 }
|
||||
},
|
||||
"cloud/cloud-base/cloud-base-7": {
|
||||
"cloud-base-7": { "width": 300, "height": 297 }
|
||||
},
|
||||
"cloud/cloud-base/cloud-base-8": {
|
||||
"cloud-base-8": { "width": 307, "height": 256 }
|
||||
},
|
||||
"cloud/cloud-base/cloud-base-9": {
|
||||
"cloud-base-9": { "width": 214, "height": 216 }
|
||||
},
|
||||
"cloud/cloud-base/cloud-base-10": {
|
||||
"cloud-base-10": { "width": 193, "height": 201 }
|
||||
},
|
||||
"cloud/cloud-cheeks": {
|
||||
"cloud-cheeks": { "x": -19, "y": -53.93, "width": 435, "height": 158 }
|
||||
},
|
||||
"cloud/cloud-eyes": {
|
||||
"cloud-eyes-closed": { "x": -10, "y": -5.43, "width": 263, "height": 43 },
|
||||
"cloud-eyes-open": { "x": -8, "y": -4.43, "width": 265, "height": 51 }
|
||||
},
|
||||
"cloud/cloud-eyes-reflex": {
|
||||
"cloud-eyes-reflex": { "x": -10, "y": 2.07, "width": 239, "height": 34 }
|
||||
},
|
||||
"cloud/cloud-mouth": {
|
||||
"cloud-mouth-closed": { "y": -14.93, "width": 97, "height": 32 },
|
||||
"cloud-mouth-open": { "x": -0.5, "y": -27.93, "width": 118, "height": 70 }
|
||||
},
|
||||
"flower/leaf-big": {
|
||||
"leaf-big": {
|
||||
"type": "mesh",
|
||||
"uvs": [ 1, 1, 0, 1, 0, 0.75, 0, 0.5, 0, 0.25, 0, 0, 1, 0, 1, 0.25, 1, 0.5, 1, 0.75 ],
|
||||
"triangles": [ 8, 3, 7, 3, 4, 7, 7, 4, 6, 4, 5, 6, 0, 1, 9, 1, 2, 9, 9, 2, 8, 2, 3, 8 ],
|
||||
"vertices": [ 1, 19, -5.05, -21.72, 1, 1, 19, -5.05, 18.28, 1, 2, 19, 19.45, 18.28, 0.75483, 20, -27.28, 18.28, 0.24517, 2, 19, 43.95, 18.28, 0.50538, 20, -2.78, 18.28, 0.49462, 2, 19, 68.45, 18.28, 0.25278, 20, 21.72, 18.28, 0.74722, 1, 20, 46.22, 18.28, 1, 1, 20, 46.22, -21.72, 1, 2, 19, 68.45, -21.72, 0.24458, 20, 21.72, -21.72, 0.75542, 2, 19, 43.95, -21.72, 0.4937, 20, -2.78, -21.72, 0.5063, 2, 19, 19.45, -21.72, 0.74651, 20, -27.28, -21.72, 0.25349 ],
|
||||
"hull": 10,
|
||||
"edges": [ 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14, 16, 16, 18, 18, 0 ],
|
||||
"width": 40,
|
||||
"height": 98
|
||||
}
|
||||
},
|
||||
"flower/leaf-small": {
|
||||
"leaf-small": { "x": 25.02, "y": 0.4, "rotation": -91.36, "width": 34, "height": 59 }
|
||||
},
|
||||
"flower/petal-1": {
|
||||
"petal-1": { "x": 18.88, "y": -4.54, "rotation": -160.18, "width": 52, "height": 36 }
|
||||
},
|
||||
"flower/petal-2": {
|
||||
"petal-2": { "x": 21.96, "y": 2.06, "rotation": -9.66, "width": 56, "height": 34 }
|
||||
},
|
||||
"flower/petal-3": {
|
||||
"petal-3": { "x": 16.97, "y": -5.71, "rotation": -91.03, "width": 58, "height": 42 }
|
||||
},
|
||||
"flower/stem": {
|
||||
"stem": {
|
||||
"type": "mesh",
|
||||
"uvs": [ 1, 1, 0, 1, 0, 0.90909, 0, 0.81818, 0, 0.72727, 0, 0.63636, 0, 0.54545, 0, 0.45455, 0, 0.36364, 0, 0.27273, 0, 0.18182, 0, 0.09091, 0, 0, 1, 0, 1, 0.09091, 1, 0.18182, 1, 0.27273, 1, 0.36364, 1, 0.45455, 1, 0.54545, 1, 0.63636, 1, 0.72727, 1, 0.81818, 1, 0.90909 ],
|
||||
"triangles": [ 15, 10, 14, 10, 11, 14, 14, 11, 13, 11, 12, 13, 18, 7, 17, 7, 8, 17, 17, 8, 16, 8, 9, 16, 16, 9, 15, 9, 10, 15, 0, 1, 23, 1, 2, 23, 23, 2, 22, 2, 3, 22, 22, 3, 21, 3, 4, 21, 21, 4, 20, 4, 5, 20, 20, 5, 19, 5, 6, 19, 19, 6, 18, 6, 7, 18 ],
|
||||
"vertices": [ 1, 22, -3.61, -6.76, 1, 1, 22, -3.61, 9.24, 1, 3, 22, 15.49, 9.24, 0.97258, 23, -89.27, 9.24, 0.02734, 24, -159.11, 9.24, 8.0E-5, 3, 22, 34.58, 9.24, 0.92758, 23, -70.18, 9.24, 0.07175, 24, -140.02, 9.24, 6.7E-4, 3, 22, 53.67, 9.24, 0.851, 23, -51.09, 9.24, 0.14565, 24, -120.93, 9.24, 0.00335, 3, 22, 72.76, 9.24, 0.73702, 23, -32, 9.24, 0.25075, 24, -101.84, 9.24, 0.01223, 3, 22, 91.85, 9.24, 0.59184, 23, -12.91, 9.24, 0.37282, 24, -82.74, 9.24, 0.03534, 3, 22, 110.94, 9.24, 0.43333, 23, 6.18, 9.24, 0.482, 24, -63.65, 9.24, 0.08467, 3, 22, 130.03, 9.24, 0.28467, 23, 25.27, 9.24, 0.54153, 24, -44.56, 9.24, 0.1738, 3, 22, 149.12, 9.24, 0.16502, 23, 44.37, 9.24, 0.52188, 24, -25.47, 9.24, 0.3131, 3, 22, 168.21, 9.24, 0.08234, 23, 63.46, 9.24, 0.4129, 24, -6.38, 9.24, 0.50477, 3, 22, 187.3, 9.24, 0.03198, 23, 82.55, 9.24, 0.228, 24, 12.71, 9.24, 0.74001, 1, 24, 31.8, 9.24, 1, 1, 24, 31.8, -6.76, 1, 3, 22, 187.3, -6.76, 0.02989, 23, 82.55, -6.76, 0.23389, 24, 12.71, -6.76, 0.73622, 3, 22, 168.21, -6.76, 0.07799, 23, 63.46, -6.76, 0.42357, 24, -6.38, -6.76, 0.49844, 3, 22, 149.12, -6.76, 0.1584, 23, 44.37, -6.76, 0.53549, 24, -25.47, -6.76, 0.30611, 3, 22, 130.03, -6.76, 0.27629, 23, 25.27, -6.76, 0.55594, 24, -44.56, -6.76, 0.16777, 3, 22, 110.94, -6.76, 0.42428, 23, 6.18, -6.76, 0.49529, 24, -63.65, -6.76, 0.08044, 3, 22, 91.85, -6.76, 0.58346, 23, -12.91, -6.76, 0.38366, 24, -82.74, -6.76, 0.03289, 3, 22, 72.76, -6.76, 0.73038, 23, -32, -6.76, 0.25856, 24, -101.84, -6.76, 0.01107, 3, 22, 53.67, -6.76, 0.84652, 23, -51.09, -6.76, 0.15057, 24, -120.93, -6.76, 0.00291, 3, 22, 34.58, -6.76, 0.92506, 23, -70.18, -6.76, 0.0744, 24, -140.02, -6.76, 5.4E-4, 3, 22, 15.49, -6.76, 0.97151, 23, -89.27, -6.76, 0.02843, 24, -159.11, -6.76, 6.0E-5 ],
|
||||
"hull": 24,
|
||||
"edges": [ 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14, 16, 16, 18, 18, 20, 20, 22, 22, 24, 24, 26, 26, 28, 28, 30, 30, 32, 32, 34, 34, 36, 36, 38, 38, 40, 40, 42, 42, 44, 44, 46, 46, 0 ],
|
||||
"width": 16,
|
||||
"height": 210
|
||||
}
|
||||
},
|
||||
"flower/stem-end": {
|
||||
"stem-end": { "x": 25.8, "y": -0.26, "rotation": -90, "width": 25, "height": 26 }
|
||||
},
|
||||
"pot/pot-base": {
|
||||
"pot-base": { "x": 5, "y": 42, "width": 152, "height": 118 }
|
||||
},
|
||||
"pot/pot-eyes": {
|
||||
"pot-eyes-closed": { "x": -0.94, "y": 2.34, "width": 92, "height": 17 },
|
||||
"pot-eyes-open": { "x": 0.06, "y": 3.84, "width": 80, "height": 22 }
|
||||
},
|
||||
"pot/pot-mouth": {
|
||||
"pot-mouth-open": { "x": -1.44, "y": -13.66, "width": 27, "height": 31 },
|
||||
"pot-mouth-pouty": { "x": 0.56, "y": -12.66, "width": 35, "height": 19 },
|
||||
"pot-mouth-smile": { "x": 0.56, "y": -12.16, "width": 27, "height": 20 },
|
||||
"pot-mouth-smile-big": { "x": 1.56, "y": -9.16, "width": 39, "height": 18 }
|
||||
},
|
||||
"rain/rain-blue": {
|
||||
"rain-blue": { "width": 23, "height": 36 }
|
||||
},
|
||||
"rain/rain-color": {
|
||||
"rain-color": { "width": 18, "height": 34 }
|
||||
},
|
||||
"rain/rain-green": {
|
||||
"rain-green": { "width": 23, "height": 36 }
|
||||
},
|
||||
"rain/rain-white": {
|
||||
"rain-white": { "width": 23, "height": 44 }
|
||||
},
|
||||
"rain/rain-white-reflex": {
|
||||
"rain-white-reflex": { "x": -0.5, "y": 3.5, "width": 10, "height": 19 }
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"animations": {
|
||||
"playing-in-the-rain": {
|
||||
"slots": {
|
||||
"cloud/cloud-eyes": {
|
||||
"attachment": [
|
||||
{ "time": 0.2, "name": "cloud-eyes-closed" },
|
||||
{ "time": 0.9, "name": "cloud-eyes-open" },
|
||||
{ "time": 1.7667, "name": "cloud-eyes-closed" },
|
||||
{ "time": 1.9333, "name": "cloud-eyes-open" },
|
||||
{ "time": 2.4333, "name": "cloud-eyes-closed" },
|
||||
{ "time": 2.6, "name": "cloud-eyes-open" },
|
||||
{ "time": 3.9333, "name": "cloud-eyes-closed" },
|
||||
{ "time": 4.1, "name": "cloud-eyes-open" }
|
||||
]
|
||||
},
|
||||
"cloud/cloud-mouth": {
|
||||
"attachment": [
|
||||
{ "time": 0.2, "name": "cloud-mouth-open" },
|
||||
{ "time": 0.9, "name": "cloud-mouth-closed" }
|
||||
]
|
||||
},
|
||||
"pot/pot-eyes": {
|
||||
"attachment": [
|
||||
{ "time": 0.1333, "name": "pot-eyes-closed" },
|
||||
{ "time": 0.3, "name": "pot-eyes-open" },
|
||||
{ "time": 1.0667, "name": "pot-eyes-closed" },
|
||||
{ "time": 1.5, "name": "pot-eyes-open" },
|
||||
{ "time": 3.0333, "name": "pot-eyes-closed" },
|
||||
{ "time": 3.2333, "name": "pot-eyes-open" },
|
||||
{ "time": 3.4667, "name": "pot-eyes-closed" },
|
||||
{ "time": 3.6667, "name": "pot-eyes-open" }
|
||||
]
|
||||
},
|
||||
"pot/pot-mouth": {
|
||||
"attachment": [
|
||||
{ "time": 0.1333, "name": "pot-mouth-open" },
|
||||
{ "time": 0.3, "name": "pot-mouth-smile-big" },
|
||||
{ "time": 1.0667, "name": "pot-mouth-pouty" },
|
||||
{ "time": 2.4, "name": "pot-mouth-smile" },
|
||||
{ "time": 3.0333, "name": "pot-mouth-smile-big" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"bones": {
|
||||
"pot": {
|
||||
"rotate": [
|
||||
{ "time": 1.1 },
|
||||
{ "time": 1.2, "value": -12.76 },
|
||||
{ "time": 1.5333, "curve": "stepped" },
|
||||
{ "time": 3.7667 },
|
||||
{ "time": 3.9, "value": 8.28 },
|
||||
{ "time": 4.2333, "value": -4.34 },
|
||||
{ "time": 4.4333 }
|
||||
],
|
||||
"scale": [
|
||||
{},
|
||||
{ "time": 0.2, "y": 0.752 },
|
||||
{ "time": 0.4, "x": 0.845, "y": 1.068 },
|
||||
{ "time": 0.6333 }
|
||||
]
|
||||
},
|
||||
"pot-control": {
|
||||
"translatex": [
|
||||
{
|
||||
"time": 1.0667,
|
||||
"curve": [ 1.222, -203.48, 1.378, -610.44 ]
|
||||
},
|
||||
{ "time": 1.5333, "value": -610.44, "curve": "stepped" },
|
||||
{
|
||||
"time": 2.2333,
|
||||
"value": -610.44,
|
||||
"curve": [ 2.389, -610.44, 2.544, -478.45 ]
|
||||
},
|
||||
{ "time": 2.7, "value": -478.45, "curve": "stepped" },
|
||||
{
|
||||
"time": 3.8333,
|
||||
"value": -478.45,
|
||||
"curve": [ 3.971, -478.45, 4.095, -135.56 ]
|
||||
},
|
||||
{ "time": 4.2333 }
|
||||
],
|
||||
"translatey": [
|
||||
{
|
||||
"time": 1.0333,
|
||||
"curve": [ 1.089, 10.56, 1.144, 44.34 ]
|
||||
},
|
||||
{
|
||||
"time": 1.2,
|
||||
"value": 44.34,
|
||||
"curve": [ 1.256, 44.34, 1.311, 0 ]
|
||||
},
|
||||
{ "time": 1.3667, "curve": "stepped" },
|
||||
{
|
||||
"time": 2.2333,
|
||||
"curve": [ 2.408, 0, 2.392, 44.34 ]
|
||||
},
|
||||
{
|
||||
"time": 2.4333,
|
||||
"value": 44.34,
|
||||
"curve": [ 2.455, 44.34, 2.51, 0 ]
|
||||
},
|
||||
{ "time": 2.6, "curve": "stepped" },
|
||||
{
|
||||
"time": 3.8,
|
||||
"curve": [ 3.841, 14.78, 3.893, 44.34 ]
|
||||
},
|
||||
{
|
||||
"time": 3.9333,
|
||||
"value": 44.34,
|
||||
"curve": [ 4.023, 44.34, 4.111, 14.78 ]
|
||||
},
|
||||
{ "time": 4.2 }
|
||||
]
|
||||
},
|
||||
"cloud-base-1": {
|
||||
"rotate": [
|
||||
{
|
||||
"curve": [ 0.144, -9.36, 0.289, -17.29 ]
|
||||
},
|
||||
{
|
||||
"time": 0.4333,
|
||||
"value": -17.29,
|
||||
"curve": [ 0.5, -17.29, 0.567, -4.32 ]
|
||||
},
|
||||
{ "time": 0.6333 }
|
||||
],
|
||||
"scale": [
|
||||
{
|
||||
"curve": [ 0.089, 1, 0.178, 1.064, 0.089, 1, 0.178, 1.064 ]
|
||||
},
|
||||
{
|
||||
"time": 0.2667,
|
||||
"x": 1.064,
|
||||
"y": 1.064,
|
||||
"curve": [ 0.411, 1.064, 0.556, 1.021, 0.411, 1.064, 0.556, 1.021 ]
|
||||
},
|
||||
{ "time": 0.7 }
|
||||
]
|
||||
},
|
||||
"cloud-base-4": {
|
||||
"rotate": [
|
||||
{
|
||||
"curve": [ 0.1, 5.55, 0.2, 14.81 ]
|
||||
},
|
||||
{
|
||||
"time": 0.3,
|
||||
"value": 14.81,
|
||||
"curve": [ 0.467, 14.81, 0.633, 9.25 ]
|
||||
},
|
||||
{ "time": 0.8 }
|
||||
],
|
||||
"scale": [
|
||||
{
|
||||
"curve": [ 0.089, 1, 0.178, 1.064, 0.089, 1, 0.178, 1.064 ]
|
||||
},
|
||||
{
|
||||
"time": 0.2667,
|
||||
"x": 1.064,
|
||||
"y": 1.064,
|
||||
"curve": [ 0.411, 1.064, 0.556, 1.021, 0.411, 1.064, 0.556, 1.021 ]
|
||||
},
|
||||
{ "time": 0.7 }
|
||||
]
|
||||
},
|
||||
"cloud": {
|
||||
"translate": [
|
||||
{ "time": 0.2333 },
|
||||
{ "time": 0.3333, "y": 30.43 },
|
||||
{ "time": 0.4667 },
|
||||
{ "time": 0.5667, "y": 30.43 },
|
||||
{ "time": 0.6667 },
|
||||
{ "time": 0.7667, "y": 30.43 },
|
||||
{ "time": 0.9333 }
|
||||
]
|
||||
}
|
||||
},
|
||||
"physics": {
|
||||
"rain/rain-blue": {
|
||||
"reset": [
|
||||
{ "time": 0.4667 },
|
||||
{ "time": 0.9333 },
|
||||
{ "time": 1.4 },
|
||||
{ "time": 1.8667 },
|
||||
{ "time": 2.3333 },
|
||||
{ "time": 2.8 },
|
||||
{ "time": 3.2667 },
|
||||
{ "time": 3.7333 },
|
||||
{ "time": 4.2 },
|
||||
{ "time": 4.6667 }
|
||||
]
|
||||
},
|
||||
"rain/rain-color": {
|
||||
"reset": [
|
||||
{ "time": 0.3 },
|
||||
{ "time": 0.7667 },
|
||||
{ "time": 1.2333 },
|
||||
{ "time": 1.7 },
|
||||
{ "time": 2.1667 },
|
||||
{ "time": 2.6333 },
|
||||
{ "time": 3.1 },
|
||||
{ "time": 3.5667 },
|
||||
{ "time": 4.0333 },
|
||||
{ "time": 4.5 }
|
||||
]
|
||||
},
|
||||
"rain/rain-green": {
|
||||
"reset": [
|
||||
{ "time": 0.1333 },
|
||||
{ "time": 0.6 },
|
||||
{ "time": 1.0667 },
|
||||
{ "time": 1.5333 },
|
||||
{ "time": 2 },
|
||||
{ "time": 2.4667 },
|
||||
{ "time": 2.9333 },
|
||||
{ "time": 3.4 },
|
||||
{ "time": 3.8667 },
|
||||
{ "time": 4.3333 }
|
||||
]
|
||||
},
|
||||
"rain/rain-white": {
|
||||
"reset": [
|
||||
{},
|
||||
{ "time": 0.4667 },
|
||||
{ "time": 0.9333 },
|
||||
{ "time": 1.4 },
|
||||
{ "time": 1.8667 },
|
||||
{ "time": 2.3333 },
|
||||
{ "time": 2.8 },
|
||||
{ "time": 3.2667 },
|
||||
{ "time": 3.7333 },
|
||||
{ "time": 4.2 }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"pot-moving-followed-by-rain": {
|
||||
"bones": {
|
||||
"pot-control": {
|
||||
"translate": [
|
||||
{},
|
||||
{ "time": 0.5667, "x": -389.34, "curve": "stepped" },
|
||||
{ "time": 1.1667, "x": -389.34 },
|
||||
{ "time": 2.2, "x": 463.88, "curve": "stepped" },
|
||||
{ "time": 2.4667, "x": 463.88 },
|
||||
{ "time": 3 }
|
||||
]
|
||||
}
|
||||
},
|
||||
"physics": {
|
||||
"rain/rain-blue": {
|
||||
"reset": [
|
||||
{ "time": 0.4667 },
|
||||
{ "time": 0.9333 },
|
||||
{ "time": 1.4 },
|
||||
{ "time": 1.8667 },
|
||||
{ "time": 2.3333 },
|
||||
{ "time": 2.8 },
|
||||
{ "time": 3.2667 }
|
||||
]
|
||||
},
|
||||
"rain/rain-color": {
|
||||
"reset": [
|
||||
{ "time": 0.3 },
|
||||
{ "time": 0.7667 },
|
||||
{ "time": 1.2333 },
|
||||
{ "time": 1.7 },
|
||||
{ "time": 2.1667 },
|
||||
{ "time": 2.6333 },
|
||||
{ "time": 3.1 }
|
||||
]
|
||||
},
|
||||
"rain/rain-green": {
|
||||
"reset": [
|
||||
{ "time": 0.1333 },
|
||||
{ "time": 0.6 },
|
||||
{ "time": 1.0667 },
|
||||
{ "time": 1.5333 },
|
||||
{ "time": 2 },
|
||||
{ "time": 2.4667 },
|
||||
{ "time": 2.9333 }
|
||||
]
|
||||
},
|
||||
"rain/rain-white": {
|
||||
"reset": [
|
||||
{},
|
||||
{ "time": 0.4667 },
|
||||
{ "time": 0.9333 },
|
||||
{ "time": 1.4 },
|
||||
{ "time": 1.8667 },
|
||||
{ "time": 2.3333 },
|
||||
{ "time": 2.8 }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"rain": {
|
||||
"physics": {
|
||||
"rain/rain-blue": {
|
||||
"reset": [
|
||||
{ "time": 0.4667 }
|
||||
]
|
||||
},
|
||||
"rain/rain-color": {
|
||||
"reset": [
|
||||
{ "time": 0.3 }
|
||||
]
|
||||
},
|
||||
"rain/rain-green": {
|
||||
"reset": [
|
||||
{ "time": 0.1333 }
|
||||
]
|
||||
},
|
||||
"rain/rain-white": {
|
||||
"reset": [
|
||||
{}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
spine-ts/spine-pixi-v8/example/assets/cloud-pot.skel
Normal file
19
spine-ts/spine-pixi-v8/example/assets/coin-pma.atlas
Normal file
@ -0,0 +1,19 @@
|
||||
coin-pma.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
coin-front-logo
|
||||
bounds: 2, 609, 305, 302
|
||||
coin-front-shine-logo
|
||||
bounds: 309, 629, 282, 282
|
||||
coin-front-shine-spineboy
|
||||
bounds: 2, 21, 282, 282
|
||||
coin-front-spineboy
|
||||
bounds: 2, 305, 305, 302
|
||||
coin-side-round
|
||||
bounds: 309, 345, 144, 282
|
||||
coin-side-straight
|
||||
bounds: 2, 2, 17, 282
|
||||
rotate: 90
|
||||
shine
|
||||
bounds: 593, 666, 72, 245
|
||||
BIN
spine-ts/spine-pixi-v8/example/assets/coin-pma.png
Normal file
|
After Width: | Height: | Size: 305 KiB |
25
spine-ts/spine-pixi-v8/example/assets/coin-pro.atlas
Normal file
@ -0,0 +1,25 @@
|
||||
coin-pro.png
|
||||
size:1399,302
|
||||
filter:Linear,Linear
|
||||
pma:true
|
||||
coin-front-logo
|
||||
bounds:319,2,298,298
|
||||
offsets:2,2,305,302
|
||||
coin-front-shine-logo
|
||||
bounds:901,20,280,280
|
||||
offsets:1,1,282,282
|
||||
coin-front-shine-spineboy
|
||||
bounds:619,20,280,280
|
||||
offsets:1,1,282,282
|
||||
coin-front-spineboy
|
||||
bounds:19,2,298,298
|
||||
offsets:2,2,305,302
|
||||
coin-side-round
|
||||
bounds:1183,20,142,280
|
||||
offsets:1,1,144,282
|
||||
coin-side-straight
|
||||
bounds:2,20,15,280
|
||||
offsets:1,1,17,282
|
||||
shine
|
||||
bounds:1327,57,70,243
|
||||
offsets:1,1,72,245
|
||||
1
spine-ts/spine-pixi-v8/example/assets/coin-pro.json
Normal file
BIN
spine-ts/spine-pixi-v8/example/assets/coin-pro.png
Normal file
|
After Width: | Height: | Size: 234 KiB |
BIN
spine-ts/spine-pixi-v8/example/assets/coin-pro.skel
Normal file
BIN
spine-ts/spine-pixi-v8/example/assets/dragon-ess.skel
Normal file
117
spine-ts/spine-pixi-v8/example/assets/dragon-pma.atlas
Normal file
@ -0,0 +1,117 @@
|
||||
dragon-pma.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
front-toe-a
|
||||
bounds: 797, 381, 29, 50
|
||||
front-toe-b
|
||||
bounds: 942, 118, 56, 57
|
||||
head
|
||||
bounds: 647, 81, 296, 260
|
||||
rotate: 90
|
||||
left-front-leg
|
||||
bounds: 942, 250, 84, 57
|
||||
rotate: 90
|
||||
left-front-thigh
|
||||
bounds: 852, 7, 84, 72
|
||||
left-wing01
|
||||
bounds: 736, 433, 264, 589
|
||||
right-rear-toe
|
||||
bounds: 647, 2, 109, 77
|
||||
right-wing01
|
||||
bounds: 2, 379, 365, 643
|
||||
right-wing02
|
||||
bounds: 369, 379, 365, 643
|
||||
right-wing03
|
||||
bounds: 2, 12, 365, 643
|
||||
rotate: 90
|
||||
tail03
|
||||
bounds: 758, 6, 73, 92
|
||||
rotate: 90
|
||||
tail04
|
||||
bounds: 942, 177, 56, 71
|
||||
tail05
|
||||
bounds: 736, 379, 52, 59
|
||||
rotate: 90
|
||||
tail06
|
||||
bounds: 942, 336, 95, 68
|
||||
rotate: 90
|
||||
thiagobrayner
|
||||
bounds: 909, 81, 350, 31
|
||||
rotate: 90
|
||||
|
||||
dragon-pma_2.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
back
|
||||
bounds: 795, 32, 190, 185
|
||||
chin
|
||||
bounds: 647, 157, 214, 146
|
||||
rotate: 90
|
||||
left-rear-leg
|
||||
bounds: 795, 219, 206, 177
|
||||
rotate: 90
|
||||
left-wing02
|
||||
bounds: 736, 427, 264, 589
|
||||
right-wing04
|
||||
bounds: 2, 373, 365, 643
|
||||
right-wing05
|
||||
bounds: 369, 373, 365, 643
|
||||
right-wing06
|
||||
bounds: 2, 6, 365, 643
|
||||
rotate: 90
|
||||
tail01
|
||||
bounds: 647, 2, 120, 153
|
||||
|
||||
dragon-pma_3.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
chest
|
||||
bounds: 740, 299, 136, 122
|
||||
left-rear-thigh
|
||||
bounds: 647, 218, 91, 149
|
||||
left-wing03
|
||||
bounds: 736, 423, 264, 589
|
||||
right-front-leg
|
||||
bounds: 850, 196, 101, 89
|
||||
rotate: 90
|
||||
right-front-thigh
|
||||
bounds: 740, 189, 108, 108
|
||||
right-rear-leg
|
||||
bounds: 878, 321, 116, 100
|
||||
right-rear-thigh
|
||||
bounds: 647, 67, 91, 149
|
||||
right-wing07
|
||||
bounds: 2, 369, 365, 643
|
||||
right-wing08
|
||||
bounds: 369, 369, 365, 643
|
||||
right-wing09
|
||||
bounds: 2, 2, 365, 643
|
||||
rotate: 90
|
||||
tail02
|
||||
bounds: 740, 67, 95, 120
|
||||
|
||||
dragon-pma_4.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
left-wing04
|
||||
bounds: 2, 268, 264, 589
|
||||
left-wing05
|
||||
bounds: 268, 268, 264, 589
|
||||
left-wing06
|
||||
bounds: 534, 268, 264, 589
|
||||
left-wing07
|
||||
bounds: 2, 2, 264, 589
|
||||
rotate: 90
|
||||
|
||||
dragon-pma_5.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
left-wing08
|
||||
bounds: 2, 2, 264, 589
|
||||
left-wing09
|
||||
bounds: 268, 2, 264, 589
|
||||
BIN
spine-ts/spine-pixi-v8/example/assets/dragon-pma.png
Normal file
|
After Width: | Height: | Size: 306 KiB |
BIN
spine-ts/spine-pixi-v8/example/assets/dragon-pma_2.png
Normal file
|
After Width: | Height: | Size: 213 KiB |
BIN
spine-ts/spine-pixi-v8/example/assets/dragon-pma_3.png
Normal file
|
After Width: | Height: | Size: 204 KiB |
BIN
spine-ts/spine-pixi-v8/example/assets/dragon-pma_4.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
spine-ts/spine-pixi-v8/example/assets/dragon-pma_5.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
1
spine-ts/spine-pixi-v8/example/assets/lil-gui.min.css
vendored
Normal file
8
spine-ts/spine-pixi-v8/example/assets/lil-gui.umd.min.js
vendored
Normal file
359
spine-ts/spine-pixi-v8/example/assets/mix-and-match-pma.atlas
Normal file
@ -0,0 +1,359 @@
|
||||
mix-and-match-pma.png
|
||||
size: 1024, 512
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
scale: 0.5
|
||||
base-head
|
||||
bounds: 118, 70, 95, 73
|
||||
boy/arm-front
|
||||
bounds: 831, 311, 36, 115
|
||||
rotate: 90
|
||||
boy/backpack
|
||||
bounds: 249, 357, 119, 153
|
||||
boy/backpack-pocket
|
||||
bounds: 628, 193, 34, 62
|
||||
rotate: 90
|
||||
boy/backpack-strap-front
|
||||
bounds: 330, 263, 38, 88
|
||||
rotate: 90
|
||||
boy/backpack-up
|
||||
bounds: 482, 171, 21, 70
|
||||
boy/body
|
||||
bounds: 845, 413, 97, 132
|
||||
rotate: 90
|
||||
boy/boot-ribbon-front
|
||||
bounds: 234, 304, 9, 11
|
||||
boy/collar
|
||||
bounds: 471, 243, 73, 29
|
||||
rotate: 90
|
||||
boy/ear
|
||||
bounds: 991, 352, 19, 23
|
||||
rotate: 90
|
||||
boy/eye-back-low-eyelid
|
||||
bounds: 66, 72, 17, 6
|
||||
boy/eye-back-pupil
|
||||
bounds: 694, 279, 8, 9
|
||||
rotate: 90
|
||||
boy/eye-back-up-eyelid
|
||||
bounds: 460, 101, 23, 5
|
||||
rotate: 90
|
||||
boy/eye-back-up-eyelid-back
|
||||
bounds: 979, 414, 19, 10
|
||||
rotate: 90
|
||||
boy/eye-front-low-eyelid
|
||||
bounds: 1015, 203, 22, 7
|
||||
rotate: 90
|
||||
boy/eye-front-pupil
|
||||
bounds: 309, 50, 9, 9
|
||||
boy/eye-front-up-eyelid
|
||||
bounds: 991, 373, 31, 6
|
||||
boy/eye-front-up-eyelid-back
|
||||
bounds: 107, 76, 26, 9
|
||||
rotate: 90
|
||||
boy/eye-iris-back
|
||||
bounds: 810, 260, 17, 17
|
||||
boy/eye-iris-front
|
||||
bounds: 902, 230, 18, 18
|
||||
boy/eye-white-back
|
||||
bounds: 599, 179, 20, 12
|
||||
boy/eye-white-front
|
||||
bounds: 544, 183, 27, 13
|
||||
boy/eyebrow-back
|
||||
bounds: 1002, 225, 20, 11
|
||||
rotate: 90
|
||||
boy/eyebrow-front
|
||||
bounds: 975, 234, 25, 11
|
||||
boy/hair-back
|
||||
bounds: 629, 289, 122, 81
|
||||
rotate: 90
|
||||
boy/hair-bangs
|
||||
bounds: 505, 180, 70, 37
|
||||
rotate: 90
|
||||
boy/hair-side
|
||||
bounds: 979, 435, 25, 43
|
||||
rotate: 90
|
||||
boy/hand-backfingers
|
||||
bounds: 858, 183, 19, 21
|
||||
boy/hand-front-fingers
|
||||
bounds: 879, 183, 19, 21
|
||||
boy/hat
|
||||
bounds: 218, 121, 93, 56
|
||||
boy/leg-front
|
||||
bounds: 85, 104, 31, 158
|
||||
boy/mouth-close
|
||||
bounds: 467, 100, 21, 5
|
||||
girl-blue-cape/mouth-close
|
||||
bounds: 467, 100, 21, 5
|
||||
girl-spring-dress/mouth-close
|
||||
bounds: 467, 100, 21, 5
|
||||
girl/mouth-close
|
||||
bounds: 467, 100, 21, 5
|
||||
boy/mouth-smile
|
||||
bounds: 1015, 258, 29, 7
|
||||
rotate: 90
|
||||
boy/nose
|
||||
bounds: 323, 79, 17, 10
|
||||
boy/pompom
|
||||
bounds: 979, 462, 48, 43
|
||||
rotate: 90
|
||||
boy/zip
|
||||
bounds: 922, 231, 14, 23
|
||||
rotate: 90
|
||||
girl-blue-cape/back-eyebrow
|
||||
bounds: 527, 106, 18, 12
|
||||
rotate: 90
|
||||
girl-blue-cape/body-dress
|
||||
bounds: 2, 264, 109, 246
|
||||
girl-blue-cape/body-ribbon
|
||||
bounds: 576, 193, 50, 38
|
||||
girl-blue-cape/cape-back
|
||||
bounds: 113, 317, 134, 193
|
||||
girl-blue-cape/cape-back-up
|
||||
bounds: 504, 305, 123, 106
|
||||
girl-blue-cape/cape-ribbon
|
||||
bounds: 396, 118, 50, 18
|
||||
rotate: 90
|
||||
girl-blue-cape/cape-shoulder-back
|
||||
bounds: 420, 243, 49, 59
|
||||
girl-blue-cape/cape-shoulder-front
|
||||
bounds: 2, 2, 62, 76
|
||||
girl-blue-cape/cape-up-front
|
||||
bounds: 118, 145, 98, 117
|
||||
girl-blue-cape/ear
|
||||
bounds: 837, 181, 19, 23
|
||||
girl-spring-dress/ear
|
||||
bounds: 837, 181, 19, 23
|
||||
girl/ear
|
||||
bounds: 837, 181, 19, 23
|
||||
girl-blue-cape/eye-back-low-eyelid
|
||||
bounds: 810, 252, 17, 6
|
||||
girl-spring-dress/eye-back-low-eyelid
|
||||
bounds: 810, 252, 17, 6
|
||||
girl/eye-back-low-eyelid
|
||||
bounds: 810, 252, 17, 6
|
||||
girl-blue-cape/eye-back-pupil
|
||||
bounds: 309, 40, 8, 9
|
||||
rotate: 90
|
||||
girl-spring-dress/eye-back-pupil
|
||||
bounds: 309, 40, 8, 9
|
||||
rotate: 90
|
||||
girl/eye-back-pupil
|
||||
bounds: 309, 40, 8, 9
|
||||
rotate: 90
|
||||
girl-blue-cape/eye-back-up-eyelid
|
||||
bounds: 573, 179, 24, 12
|
||||
girl-spring-dress/eye-back-up-eyelid
|
||||
bounds: 573, 179, 24, 12
|
||||
girl/eye-back-up-eyelid
|
||||
bounds: 573, 179, 24, 12
|
||||
girl-blue-cape/eye-back-up-eyelid-back
|
||||
bounds: 380, 105, 17, 11
|
||||
rotate: 90
|
||||
girl-spring-dress/eye-back-up-eyelid-back
|
||||
bounds: 380, 105, 17, 11
|
||||
rotate: 90
|
||||
girl/eye-back-up-eyelid-back
|
||||
bounds: 380, 105, 17, 11
|
||||
rotate: 90
|
||||
girl-blue-cape/eye-front-low-eyelid
|
||||
bounds: 1016, 353, 18, 6
|
||||
rotate: 90
|
||||
girl-spring-dress/eye-front-low-eyelid
|
||||
bounds: 1016, 353, 18, 6
|
||||
rotate: 90
|
||||
girl/eye-front-low-eyelid
|
||||
bounds: 1016, 353, 18, 6
|
||||
rotate: 90
|
||||
girl-blue-cape/eye-front-pupil
|
||||
bounds: 363, 94, 9, 9
|
||||
girl-spring-dress/eye-front-pupil
|
||||
bounds: 363, 94, 9, 9
|
||||
girl/eye-front-pupil
|
||||
bounds: 363, 94, 9, 9
|
||||
girl-blue-cape/eye-front-up-eyelid
|
||||
bounds: 679, 413, 30, 14
|
||||
rotate: 90
|
||||
girl-spring-dress/eye-front-up-eyelid
|
||||
bounds: 679, 413, 30, 14
|
||||
rotate: 90
|
||||
girl/eye-front-up-eyelid
|
||||
bounds: 679, 413, 30, 14
|
||||
rotate: 90
|
||||
girl-blue-cape/eye-front-up-eyelid-back
|
||||
bounds: 947, 234, 26, 11
|
||||
girl-spring-dress/eye-front-up-eyelid-back
|
||||
bounds: 947, 234, 26, 11
|
||||
girl/eye-front-up-eyelid-back
|
||||
bounds: 947, 234, 26, 11
|
||||
girl-blue-cape/eye-iris-back
|
||||
bounds: 323, 105, 17, 17
|
||||
girl-blue-cape/eye-iris-front
|
||||
bounds: 467, 107, 18, 18
|
||||
girl-blue-cape/eye-white-back
|
||||
bounds: 621, 175, 20, 16
|
||||
girl-spring-dress/eye-white-back
|
||||
bounds: 621, 175, 20, 16
|
||||
girl-blue-cape/eye-white-front
|
||||
bounds: 643, 175, 20, 16
|
||||
girl-spring-dress/eye-white-front
|
||||
bounds: 643, 175, 20, 16
|
||||
girl/eye-white-front
|
||||
bounds: 643, 175, 20, 16
|
||||
girl-blue-cape/front-eyebrow
|
||||
bounds: 309, 101, 18, 12
|
||||
rotate: 90
|
||||
girl-blue-cape/hair-back
|
||||
bounds: 712, 317, 117, 98
|
||||
girl-blue-cape/hair-bangs
|
||||
bounds: 313, 170, 91, 40
|
||||
rotate: 90
|
||||
girl-blue-cape/hair-head-side-back
|
||||
bounds: 544, 198, 30, 52
|
||||
girl-blue-cape/hair-head-side-front
|
||||
bounds: 466, 127, 41, 42
|
||||
girl-blue-cape/hair-side
|
||||
bounds: 175, 2, 36, 71
|
||||
rotate: 90
|
||||
girl-blue-cape/hand-front-fingers
|
||||
bounds: 902, 207, 19, 21
|
||||
girl-spring-dress/hand-front-fingers
|
||||
bounds: 902, 207, 19, 21
|
||||
girl-blue-cape/leg-front
|
||||
bounds: 519, 413, 30, 158
|
||||
rotate: 90
|
||||
girl-blue-cape/mouth-smile
|
||||
bounds: 1015, 227, 29, 7
|
||||
rotate: 90
|
||||
girl-spring-dress/mouth-smile
|
||||
bounds: 1015, 227, 29, 7
|
||||
rotate: 90
|
||||
girl/mouth-smile
|
||||
bounds: 1015, 227, 29, 7
|
||||
rotate: 90
|
||||
girl-blue-cape/nose
|
||||
bounds: 342, 82, 11, 7
|
||||
girl-spring-dress/nose
|
||||
bounds: 342, 82, 11, 7
|
||||
girl/nose
|
||||
bounds: 342, 82, 11, 7
|
||||
girl-blue-cape/sleeve-back
|
||||
bounds: 416, 95, 42, 29
|
||||
girl-blue-cape/sleeve-front
|
||||
bounds: 249, 303, 52, 119
|
||||
rotate: 90
|
||||
girl-spring-dress/arm-front
|
||||
bounds: 829, 292, 17, 111
|
||||
rotate: 90
|
||||
girl-spring-dress/back-eyebrow
|
||||
bounds: 309, 81, 18, 12
|
||||
rotate: 90
|
||||
girl-spring-dress/body-up
|
||||
bounds: 66, 2, 64, 66
|
||||
girl-spring-dress/cloak-down
|
||||
bounds: 758, 227, 50, 50
|
||||
girl-spring-dress/cloak-up
|
||||
bounds: 628, 229, 64, 58
|
||||
girl-spring-dress/eye-iris-back
|
||||
bounds: 342, 105, 17, 17
|
||||
girl-spring-dress/eye-iris-front
|
||||
bounds: 487, 107, 18, 18
|
||||
girl-spring-dress/front-eyebrow
|
||||
bounds: 323, 91, 18, 12
|
||||
girl-spring-dress/hair-back
|
||||
bounds: 370, 417, 147, 93
|
||||
girl-spring-dress/hair-bangs
|
||||
bounds: 829, 250, 91, 40
|
||||
girl-spring-dress/hair-head-side-back
|
||||
bounds: 509, 126, 30, 52
|
||||
girl-spring-dress/hair-head-side-front
|
||||
bounds: 816, 206, 41, 42
|
||||
girl-spring-dress/hair-side
|
||||
bounds: 248, 2, 36, 71
|
||||
rotate: 90
|
||||
girl-spring-dress/leg-front
|
||||
bounds: 831, 381, 30, 158
|
||||
rotate: 90
|
||||
girl-spring-dress/neck
|
||||
bounds: 85, 70, 20, 32
|
||||
girl-spring-dress/shoulder-ribbon
|
||||
bounds: 175, 44, 36, 24
|
||||
girl-spring-dress/skirt
|
||||
bounds: 2, 80, 182, 81
|
||||
rotate: 90
|
||||
girl-spring-dress/underskirt
|
||||
bounds: 519, 445, 175, 65
|
||||
girl/arm-front
|
||||
bounds: 712, 279, 36, 115
|
||||
rotate: 90
|
||||
girl/back-eyebrow
|
||||
bounds: 309, 61, 18, 12
|
||||
rotate: 90
|
||||
girl/bag-base
|
||||
bounds: 694, 219, 62, 58
|
||||
girl/bag-strap-front
|
||||
bounds: 370, 304, 12, 96
|
||||
rotate: 90
|
||||
girl/bag-top
|
||||
bounds: 765, 175, 49, 50
|
||||
girl/body
|
||||
bounds: 370, 318, 97, 132
|
||||
rotate: 90
|
||||
girl/boot-ribbon-front
|
||||
bounds: 323, 64, 13, 13
|
||||
girl/eye-iris-back
|
||||
bounds: 361, 105, 17, 17
|
||||
girl/eye-iris-front
|
||||
bounds: 507, 106, 18, 18
|
||||
girl/eye-white-back
|
||||
bounds: 665, 175, 20, 16
|
||||
girl/front-eyebrow
|
||||
bounds: 343, 91, 18, 12
|
||||
girl/hair-back
|
||||
bounds: 696, 417, 147, 93
|
||||
girl/hair-bangs
|
||||
bounds: 922, 247, 91, 40
|
||||
girl/hair-flap-down-front
|
||||
bounds: 415, 171, 70, 65
|
||||
rotate: 90
|
||||
girl/hair-head-side-back
|
||||
bounds: 991, 381, 30, 52
|
||||
girl/hair-head-side-front
|
||||
bounds: 859, 206, 41, 42
|
||||
girl/hair-patch
|
||||
bounds: 132, 2, 66, 41
|
||||
rotate: 90
|
||||
girl/hair-side
|
||||
bounds: 692, 181, 36, 71
|
||||
rotate: 90
|
||||
girl/hair-strand-back-1
|
||||
bounds: 948, 289, 58, 74
|
||||
rotate: 90
|
||||
girl/hair-strand-back-2
|
||||
bounds: 355, 170, 91, 58
|
||||
rotate: 90
|
||||
girl/hair-strand-back-3
|
||||
bounds: 215, 40, 92, 79
|
||||
girl/hair-strand-front-1
|
||||
bounds: 234, 263, 38, 94
|
||||
rotate: 90
|
||||
girl/hair-strand-front-2
|
||||
bounds: 576, 233, 70, 50
|
||||
rotate: 90
|
||||
girl/hair-strand-front-3
|
||||
bounds: 313, 124, 44, 81
|
||||
rotate: 90
|
||||
girl/hand-front-fingers
|
||||
bounds: 923, 208, 19, 21
|
||||
girl/hat
|
||||
bounds: 218, 179, 93, 82
|
||||
girl/leg-front
|
||||
bounds: 831, 349, 30, 158
|
||||
rotate: 90
|
||||
girl/pompom
|
||||
bounds: 416, 126, 48, 43
|
||||
girl/scarf
|
||||
bounds: 113, 264, 119, 51
|
||||
girl/scarf-back
|
||||
bounds: 502, 252, 72, 51
|
||||
girl/zip
|
||||
bounds: 816, 179, 19, 25
|
||||
BIN
spine-ts/spine-pixi-v8/example/assets/mix-and-match-pma.png
Normal file
|
After Width: | Height: | Size: 369 KiB |
BIN
spine-ts/spine-pixi-v8/example/assets/mix-and-match-pro.skel
Normal file
2017
spine-ts/spine-pixi-v8/example/assets/pixi.min.js
vendored
Normal file
BIN
spine-ts/spine-pixi-v8/example/assets/raptor-jaw-tooth.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
101
spine-ts/spine-pixi-v8/example/assets/raptor-pma.atlas
Normal file
@ -0,0 +1,101 @@
|
||||
raptor-pma.png
|
||||
size: 1024, 512
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
scale: 0.5
|
||||
back-arm
|
||||
bounds: 829, 88, 46, 25
|
||||
rotate: 90
|
||||
back-bracer
|
||||
bounds: 195, 238, 39, 28
|
||||
rotate: 90
|
||||
back-hand
|
||||
bounds: 724, 140, 36, 34
|
||||
rotate: 90
|
||||
back-knee
|
||||
bounds: 760, 131, 49, 67
|
||||
rotate: 90
|
||||
back-thigh
|
||||
bounds: 225, 238, 39, 24
|
||||
rotate: 90
|
||||
eyes-open
|
||||
bounds: 975, 204, 47, 45
|
||||
front-arm
|
||||
bounds: 969, 112, 48, 26
|
||||
front-bracer
|
||||
bounds: 724, 97, 41, 29
|
||||
rotate: 90
|
||||
front-hand
|
||||
bounds: 251, 239, 41, 38
|
||||
front-open-hand
|
||||
bounds: 856, 76, 43, 44
|
||||
rotate: 90
|
||||
front-thigh
|
||||
bounds: 729, 178, 57, 29
|
||||
rotate: 90
|
||||
gun
|
||||
bounds: 894, 251, 107, 103
|
||||
gun-nohand
|
||||
bounds: 764, 241, 105, 102
|
||||
head
|
||||
bounds: 756, 345, 136, 149
|
||||
lower-leg
|
||||
bounds: 475, 237, 73, 98
|
||||
rotate: 90
|
||||
mouth-grind
|
||||
bounds: 975, 172, 47, 30
|
||||
mouth-smile
|
||||
bounds: 975, 140, 47, 30
|
||||
neck
|
||||
bounds: 366, 282, 18, 21
|
||||
raptor-back-arm
|
||||
bounds: 636, 97, 82, 86
|
||||
rotate: 90
|
||||
raptor-body
|
||||
bounds: 2, 2, 632, 233
|
||||
raptor-front-arm
|
||||
bounds: 871, 168, 81, 102
|
||||
rotate: 90
|
||||
raptor-front-leg
|
||||
bounds: 2, 237, 191, 257
|
||||
raptor-hindleg-back
|
||||
bounds: 195, 279, 169, 215
|
||||
raptor-horn
|
||||
bounds: 431, 312, 182, 80
|
||||
rotate: 90
|
||||
raptor-horn-back
|
||||
bounds: 513, 318, 176, 77
|
||||
rotate: 90
|
||||
raptor-jaw
|
||||
bounds: 894, 356, 126, 138
|
||||
raptor-jaw-tooth
|
||||
bounds: 294, 240, 37, 48
|
||||
rotate: 90
|
||||
raptor-mouth-inside
|
||||
bounds: 344, 241, 36, 41
|
||||
rotate: 90
|
||||
raptor-saddle-strap-back
|
||||
bounds: 575, 242, 54, 74
|
||||
raptor-saddle-strap-front
|
||||
bounds: 764, 182, 57, 95
|
||||
rotate: 90
|
||||
raptor-saddle-w-shadow
|
||||
bounds: 592, 323, 162, 171
|
||||
raptor-tail-shadow
|
||||
bounds: 366, 305, 189, 63
|
||||
rotate: 90
|
||||
raptor-tongue
|
||||
bounds: 387, 239, 86, 64
|
||||
stirrup-back
|
||||
bounds: 829, 136, 44, 35
|
||||
rotate: 90
|
||||
stirrup-front
|
||||
bounds: 866, 121, 45, 50
|
||||
rotate: 90
|
||||
stirrup-strap
|
||||
bounds: 918, 120, 49, 46
|
||||
torso
|
||||
bounds: 636, 181, 54, 91
|
||||
rotate: 90
|
||||
visor
|
||||
bounds: 631, 237, 131, 84
|
||||
BIN
spine-ts/spine-pixi-v8/example/assets/raptor-pma.png
Normal file
|
After Width: | Height: | Size: 409 KiB |
9185
spine-ts/spine-pixi-v8/example/assets/raptor-pro.json
Normal file
93
spine-ts/spine-pixi-v8/example/assets/raptor.atlas
Normal file
@ -0,0 +1,93 @@
|
||||
raptor.png
|
||||
size: 1024, 512
|
||||
filter: Linear, Linear
|
||||
scale: 0.5
|
||||
back-arm
|
||||
bounds: 895, 295, 46, 25
|
||||
back-bracer
|
||||
bounds: 992, 216, 39, 28
|
||||
rotate: 90
|
||||
back-hand
|
||||
bounds: 594, 58, 36, 34
|
||||
back-knee
|
||||
bounds: 729, 86, 49, 67
|
||||
rotate: 90
|
||||
back-thigh
|
||||
bounds: 379, 2, 39, 24
|
||||
eyes-open
|
||||
bounds: 902, 194, 47, 45
|
||||
rotate: 90
|
||||
front-arm
|
||||
bounds: 945, 306, 48, 26
|
||||
front-bracer
|
||||
bounds: 949, 197, 41, 29
|
||||
front-hand
|
||||
bounds: 949, 266, 41, 38
|
||||
front-open-hand
|
||||
bounds: 875, 148, 43, 44
|
||||
front-thigh
|
||||
bounds: 793, 171, 57, 29
|
||||
rotate: 90
|
||||
gun
|
||||
bounds: 379, 28, 107, 103
|
||||
rotate: 90
|
||||
gun-nohand
|
||||
bounds: 487, 87, 105, 102
|
||||
head
|
||||
bounds: 807, 361, 136, 149
|
||||
lower-leg
|
||||
bounds: 827, 195, 73, 98
|
||||
mouth-grind
|
||||
bounds: 920, 145, 47, 30
|
||||
rotate: 90
|
||||
mouth-smile
|
||||
bounds: 992, 257, 47, 30
|
||||
rotate: 90
|
||||
neck
|
||||
bounds: 359, 114, 18, 21
|
||||
raptor-back-arm
|
||||
bounds: 653, 142, 82, 86
|
||||
raptor-body
|
||||
bounds: 2, 277, 632, 233
|
||||
raptor-front-arm
|
||||
bounds: 484, 4, 81, 102
|
||||
rotate: 90
|
||||
raptor-front-leg
|
||||
bounds: 2, 18, 191, 257
|
||||
raptor-hindleg-back
|
||||
bounds: 636, 295, 169, 215
|
||||
raptor-horn
|
||||
bounds: 195, 22, 182, 80
|
||||
raptor-horn-back
|
||||
bounds: 945, 334, 176, 77
|
||||
rotate: 90
|
||||
raptor-jaw
|
||||
bounds: 359, 137, 126, 138
|
||||
raptor-jaw-tooth
|
||||
bounds: 895, 322, 37, 48
|
||||
rotate: 90
|
||||
raptor-mouth-inside
|
||||
bounds: 949, 228, 36, 41
|
||||
rotate: 90
|
||||
raptor-saddle-strap-back
|
||||
bounds: 653, 86, 54, 74
|
||||
rotate: 90
|
||||
raptor-saddle-strap-front
|
||||
bounds: 594, 94, 57, 95
|
||||
raptor-saddle-w-shadow
|
||||
bounds: 195, 104, 162, 171
|
||||
raptor-tail-shadow
|
||||
bounds: 636, 230, 189, 63
|
||||
raptor-tongue
|
||||
bounds: 807, 295, 86, 64
|
||||
stirrup-back
|
||||
bounds: 952, 151, 44, 35
|
||||
rotate: 90
|
||||
stirrup-front
|
||||
bounds: 902, 243, 45, 50
|
||||
stirrup-strap
|
||||
bounds: 824, 147, 49, 46
|
||||
torso
|
||||
bounds: 737, 137, 54, 91
|
||||
visor
|
||||
bounds: 487, 191, 131, 84
|
||||
BIN
spine-ts/spine-pixi-v8/example/assets/raptor.png
Normal file
|
After Width: | Height: | Size: 413 KiB |
11
spine-ts/spine-pixi-v8/example/assets/sack-pma.atlas
Normal file
@ -0,0 +1,11 @@
|
||||
sack-pma.png
|
||||
size: 512, 512
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
scale: 0.5
|
||||
cape-back
|
||||
bounds: 237, 149, 260, 260
|
||||
cape-front
|
||||
bounds: 237, 43, 200, 104
|
||||
sack
|
||||
bounds: 2, 2, 233, 407
|
||||
BIN
spine-ts/spine-pixi-v8/example/assets/sack-pma.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
4940
spine-ts/spine-pixi-v8/example/assets/sack-pro.json
Normal file
BIN
spine-ts/spine-pixi-v8/example/assets/sack-pro.skel
Normal file
210
spine-ts/spine-pixi-v8/example/assets/snowglobe-pma.atlas
Normal file
@ -0,0 +1,210 @@
|
||||
snowglobe-pma.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
scale: 0.5
|
||||
arm-down-r
|
||||
bounds: 884, 129, 76, 53
|
||||
arm-up-l
|
||||
bounds: 718, 23, 49, 114
|
||||
rotate: 90
|
||||
arm-up-r
|
||||
bounds: 867, 69, 58, 104
|
||||
rotate: 90
|
||||
blue-present-base
|
||||
bounds: 884, 883, 126, 139
|
||||
eye-reflex-l
|
||||
bounds: 991, 347, 12, 13
|
||||
eye-reflex-r
|
||||
bounds: 867, 129, 10, 12
|
||||
rotate: 90
|
||||
eye-white-l
|
||||
bounds: 987, 697, 35, 43
|
||||
eye-white-r
|
||||
bounds: 560, 2, 34, 48
|
||||
eyelashes-l
|
||||
bounds: 982, 2, 32, 40
|
||||
gift-base
|
||||
bounds: 884, 335, 125, 105
|
||||
rotate: 90
|
||||
gift-decoration
|
||||
bounds: 518, 2, 48, 40
|
||||
rotate: 90
|
||||
globe-borders
|
||||
bounds: 2, 141, 880, 881
|
||||
glove-l
|
||||
bounds: 982, 44, 40, 61
|
||||
glove-shadow-l
|
||||
bounds: 991, 403, 28, 57
|
||||
glove-shadow-r
|
||||
bounds: 960, 204, 38, 62
|
||||
rotate: 90
|
||||
green-present-base
|
||||
bounds: 138, 13, 126, 139
|
||||
rotate: 90
|
||||
hair-front
|
||||
bounds: 884, 590, 150, 101
|
||||
rotate: 90
|
||||
hair-side
|
||||
bounds: 995, 574, 27, 53
|
||||
hair-strand-2
|
||||
bounds: 987, 629, 26, 66
|
||||
hair-strand-5
|
||||
bounds: 690, 7, 25, 47
|
||||
hair-strand-6
|
||||
bounds: 995, 507, 14, 35
|
||||
head-base
|
||||
bounds: 2, 4, 134, 135
|
||||
leg-down-l
|
||||
bounds: 596, 3, 92, 51
|
||||
leg-up-l
|
||||
bounds: 718, 74, 65, 147
|
||||
rotate: 90
|
||||
leg-up-l-fuzzy
|
||||
bounds: 834, 2, 73, 65
|
||||
leg-up-r
|
||||
bounds: 576, 56, 83, 140
|
||||
rotate: 90
|
||||
leg-up-r-fuzzy
|
||||
bounds: 909, 2, 65, 71
|
||||
rotate: 90
|
||||
mouth
|
||||
bounds: 991, 362, 39, 13
|
||||
rotate: 90
|
||||
neck-scarf
|
||||
bounds: 279, 25, 142, 114
|
||||
nose
|
||||
bounds: 995, 488, 17, 14
|
||||
rotate: 90
|
||||
nose-shadow
|
||||
bounds: 299, 8, 15, 15
|
||||
red-present-base
|
||||
bounds: 884, 742, 126, 139
|
||||
scarf-end-l
|
||||
bounds: 884, 462, 126, 109
|
||||
rotate: 90
|
||||
scarf-end-r
|
||||
bounds: 423, 52, 151, 87
|
||||
scarf-ribbon-middle-r
|
||||
bounds: 960, 244, 62, 89
|
||||
scarf-shadow
|
||||
bounds: 884, 184, 149, 74
|
||||
rotate: 90
|
||||
shoe-l
|
||||
bounds: 973, 107, 49, 95
|
||||
shoe-r
|
||||
bounds: 423, 6, 44, 93
|
||||
rotate: 90
|
||||
shoelace
|
||||
bounds: 279, 2, 21, 18
|
||||
rotate: 90
|
||||
snow
|
||||
bounds: 995, 544, 27, 28
|
||||
string
|
||||
bounds: 138, 6, 5, 53
|
||||
rotate: 90
|
||||
|
||||
snowglobe-pma_2.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
scale: 0.5
|
||||
arm-down-l
|
||||
bounds: 884, 579, 56, 54
|
||||
arm-down-l-fuzzy
|
||||
bounds: 884, 635, 57, 59
|
||||
arm-down-r-fuzzy
|
||||
bounds: 884, 696, 61, 66
|
||||
blue-present-decoration
|
||||
bounds: 884, 216, 41, 40
|
||||
green-present-decoration
|
||||
bounds: 884, 216, 41, 40
|
||||
ear-l
|
||||
bounds: 884, 527, 55, 50
|
||||
ear-r
|
||||
bounds: 291, 94, 45, 66
|
||||
rotate: 90
|
||||
eyelashes-r
|
||||
bounds: 2, 2, 32, 47
|
||||
rotate: 90
|
||||
globe-texture-strong
|
||||
bounds: 2, 141, 880, 881
|
||||
glove-fingers-l
|
||||
bounds: 884, 361, 39, 51
|
||||
glove-fingers-r
|
||||
bounds: 884, 469, 41, 56
|
||||
glove-r
|
||||
bounds: 76, 36, 44, 65
|
||||
rotate: 90
|
||||
hair-strand-1
|
||||
bounds: 359, 102, 37, 65
|
||||
rotate: 90
|
||||
hair-strand-3
|
||||
bounds: 884, 414, 40, 53
|
||||
hair-strand-4
|
||||
bounds: 939, 893, 37, 69
|
||||
iris-l
|
||||
bounds: 884, 173, 40, 41
|
||||
iris-r
|
||||
bounds: 143, 39, 40, 41
|
||||
leg-down-r
|
||||
bounds: 2, 36, 72, 103
|
||||
pupil-l
|
||||
bounds: 51, 2, 32, 32
|
||||
pupil-r
|
||||
bounds: 85, 2, 32, 32
|
||||
red-present-decoration
|
||||
bounds: 426, 99, 41, 40
|
||||
scarf-pompom-l
|
||||
bounds: 884, 309, 50, 46
|
||||
rotate: 90
|
||||
scarf-pompom-r
|
||||
bounds: 884, 258, 49, 47
|
||||
rotate: 90
|
||||
scarf-ribbon-bottom-l
|
||||
bounds: 884, 856, 106, 53
|
||||
rotate: 90
|
||||
scarf-ribbon-bottom-r
|
||||
bounds: 76, 82, 105, 57
|
||||
scarf-ribbon-middle-l
|
||||
bounds: 884, 764, 63, 90
|
||||
scarf-ribbon-top-l
|
||||
bounds: 884, 964, 105, 58
|
||||
scarf-ribbon-top-r
|
||||
bounds: 183, 86, 106, 53
|
||||
|
||||
snowglobe-pma_3.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
scale: 0.5
|
||||
globe-texture
|
||||
bounds: 2, 2, 880, 881
|
||||
|
||||
snowglobe-pma_4.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
scale: 0.5
|
||||
elf-shadow
|
||||
bounds: 2, 2, 395, 158
|
||||
globe-reflections
|
||||
bounds: 2, 162, 646, 835
|
||||
globe-shadow
|
||||
bounds: 650, 77, 920, 366
|
||||
rotate: 90
|
||||
hat
|
||||
bounds: 399, 7, 153, 221
|
||||
rotate: 90
|
||||
|
||||
snowglobe-pma_5.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
scale: 0.5
|
||||
body
|
||||
bounds: 710, 569, 139, 151
|
||||
globe-base-back
|
||||
bounds: 2, 2, 606, 258
|
||||
globe-base-front
|
||||
bounds: 2, 262, 706, 458
|
||||
BIN
spine-ts/spine-pixi-v8/example/assets/snowglobe-pma.png
Normal file
|
After Width: | Height: | Size: 673 KiB |
BIN
spine-ts/spine-pixi-v8/example/assets/snowglobe-pma_2.png
Normal file
|
After Width: | Height: | Size: 784 KiB |
BIN
spine-ts/spine-pixi-v8/example/assets/snowglobe-pma_3.png
Normal file
|
After Width: | Height: | Size: 400 KiB |
BIN
spine-ts/spine-pixi-v8/example/assets/snowglobe-pma_4.png
Normal file
|
After Width: | Height: | Size: 240 KiB |
BIN
spine-ts/spine-pixi-v8/example/assets/snowglobe-pma_5.png
Normal file
|
After Width: | Height: | Size: 392 KiB |
2911
spine-ts/spine-pixi-v8/example/assets/snowglobe-pro.json
Normal file
BIN
spine-ts/spine-pixi-v8/example/assets/snowglobe-pro.skel
Normal file
BIN
spine-ts/spine-pixi-v8/example/assets/spine_logo.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
95
spine-ts/spine-pixi-v8/example/assets/spineboy-pma.atlas
Normal file
@ -0,0 +1,95 @@
|
||||
spineboy-pma.png
|
||||
size: 1024, 256
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
scale: 0.5
|
||||
crosshair
|
||||
bounds: 352, 7, 45, 45
|
||||
eye-indifferent
|
||||
bounds: 862, 105, 47, 45
|
||||
eye-surprised
|
||||
bounds: 505, 79, 47, 45
|
||||
front-bracer
|
||||
bounds: 826, 66, 29, 40
|
||||
front-fist-closed
|
||||
bounds: 786, 65, 38, 41
|
||||
front-fist-open
|
||||
bounds: 710, 51, 43, 44
|
||||
rotate: 90
|
||||
front-foot
|
||||
bounds: 210, 6, 63, 35
|
||||
front-shin
|
||||
bounds: 665, 128, 41, 92
|
||||
rotate: 90
|
||||
front-thigh
|
||||
bounds: 2, 2, 23, 56
|
||||
rotate: 90
|
||||
front-upper-arm
|
||||
bounds: 250, 205, 23, 49
|
||||
goggles
|
||||
bounds: 665, 171, 131, 83
|
||||
gun
|
||||
bounds: 798, 152, 105, 102
|
||||
head
|
||||
bounds: 2, 27, 136, 149
|
||||
hoverboard-board
|
||||
bounds: 2, 178, 246, 76
|
||||
hoverboard-thruster
|
||||
bounds: 722, 96, 30, 32
|
||||
rotate: 90
|
||||
hoverglow-small
|
||||
bounds: 275, 81, 137, 38
|
||||
mouth-grind
|
||||
bounds: 614, 97, 47, 30
|
||||
mouth-oooo
|
||||
bounds: 612, 65, 47, 30
|
||||
mouth-smile
|
||||
bounds: 661, 64, 47, 30
|
||||
muzzle-glow
|
||||
bounds: 382, 54, 25, 25
|
||||
muzzle-ring
|
||||
bounds: 275, 54, 25, 105
|
||||
rotate: 90
|
||||
muzzle01
|
||||
bounds: 911, 95, 67, 40
|
||||
rotate: 90
|
||||
muzzle02
|
||||
bounds: 792, 108, 68, 42
|
||||
muzzle03
|
||||
bounds: 956, 171, 83, 53
|
||||
rotate: 90
|
||||
muzzle04
|
||||
bounds: 275, 7, 75, 45
|
||||
muzzle05
|
||||
bounds: 140, 3, 68, 38
|
||||
neck
|
||||
bounds: 250, 182, 18, 21
|
||||
portal-bg
|
||||
bounds: 140, 43, 133, 133
|
||||
portal-flare1
|
||||
bounds: 554, 65, 56, 30
|
||||
portal-flare2
|
||||
bounds: 759, 112, 57, 31
|
||||
rotate: 90
|
||||
portal-flare3
|
||||
bounds: 554, 97, 58, 30
|
||||
portal-shade
|
||||
bounds: 275, 121, 133, 133
|
||||
portal-streaks1
|
||||
bounds: 410, 126, 126, 128
|
||||
portal-streaks2
|
||||
bounds: 538, 129, 125, 125
|
||||
rear-bracer
|
||||
bounds: 857, 67, 28, 36
|
||||
rear-foot
|
||||
bounds: 663, 96, 57, 30
|
||||
rear-shin
|
||||
bounds: 414, 86, 38, 89
|
||||
rotate: 90
|
||||
rear-thigh
|
||||
bounds: 756, 63, 28, 47
|
||||
rear-upper-arm
|
||||
bounds: 60, 5, 20, 44
|
||||
rotate: 90
|
||||
torso
|
||||
bounds: 905, 164, 49, 90
|
||||
BIN
spine-ts/spine-pixi-v8/example/assets/spineboy-pma.png
Normal file
|
After Width: | Height: | Size: 239 KiB |
8723
spine-ts/spine-pixi-v8/example/assets/spineboy-pro.json
Normal file
BIN
spine-ts/spine-pixi-v8/example/assets/spineboy-pro.skel
Normal file
94
spine-ts/spine-pixi-v8/example/assets/spineboy.atlas
Normal file
@ -0,0 +1,94 @@
|
||||
spineboy.png
|
||||
size: 1024, 256
|
||||
filter: Linear, Linear
|
||||
scale: 0.5
|
||||
crosshair
|
||||
bounds: 352, 7, 45, 45
|
||||
eye-indifferent
|
||||
bounds: 862, 105, 47, 45
|
||||
eye-surprised
|
||||
bounds: 505, 79, 47, 45
|
||||
front-bracer
|
||||
bounds: 826, 66, 29, 40
|
||||
front-fist-closed
|
||||
bounds: 786, 65, 38, 41
|
||||
front-fist-open
|
||||
bounds: 710, 51, 43, 44
|
||||
rotate: 90
|
||||
front-foot
|
||||
bounds: 210, 6, 63, 35
|
||||
front-shin
|
||||
bounds: 665, 128, 41, 92
|
||||
rotate: 90
|
||||
front-thigh
|
||||
bounds: 2, 2, 23, 56
|
||||
rotate: 90
|
||||
front-upper-arm
|
||||
bounds: 250, 205, 23, 49
|
||||
goggles
|
||||
bounds: 665, 171, 131, 83
|
||||
gun
|
||||
bounds: 798, 152, 105, 102
|
||||
head
|
||||
bounds: 2, 27, 136, 149
|
||||
hoverboard-board
|
||||
bounds: 2, 178, 246, 76
|
||||
hoverboard-thruster
|
||||
bounds: 722, 96, 30, 32
|
||||
rotate: 90
|
||||
hoverglow-small
|
||||
bounds: 275, 81, 137, 38
|
||||
mouth-grind
|
||||
bounds: 614, 97, 47, 30
|
||||
mouth-oooo
|
||||
bounds: 612, 65, 47, 30
|
||||
mouth-smile
|
||||
bounds: 661, 64, 47, 30
|
||||
muzzle-glow
|
||||
bounds: 382, 54, 25, 25
|
||||
muzzle-ring
|
||||
bounds: 275, 54, 25, 105
|
||||
rotate: 90
|
||||
muzzle01
|
||||
bounds: 911, 95, 67, 40
|
||||
rotate: 90
|
||||
muzzle02
|
||||
bounds: 792, 108, 68, 42
|
||||
muzzle03
|
||||
bounds: 956, 171, 83, 53
|
||||
rotate: 90
|
||||
muzzle04
|
||||
bounds: 275, 7, 75, 45
|
||||
muzzle05
|
||||
bounds: 140, 3, 68, 38
|
||||
neck
|
||||
bounds: 250, 182, 18, 21
|
||||
portal-bg
|
||||
bounds: 140, 43, 133, 133
|
||||
portal-flare1
|
||||
bounds: 554, 65, 56, 30
|
||||
portal-flare2
|
||||
bounds: 759, 112, 57, 31
|
||||
rotate: 90
|
||||
portal-flare3
|
||||
bounds: 554, 97, 58, 30
|
||||
portal-shade
|
||||
bounds: 275, 121, 133, 133
|
||||
portal-streaks1
|
||||
bounds: 410, 126, 126, 128
|
||||
portal-streaks2
|
||||
bounds: 538, 129, 125, 125
|
||||
rear-bracer
|
||||
bounds: 857, 67, 28, 36
|
||||
rear-foot
|
||||
bounds: 663, 96, 57, 30
|
||||
rear-shin
|
||||
bounds: 414, 86, 38, 89
|
||||
rotate: 90
|
||||
rear-thigh
|
||||
bounds: 756, 63, 28, 47
|
||||
rear-upper-arm
|
||||
bounds: 60, 5, 20, 44
|
||||
rotate: 90
|
||||
torso
|
||||
bounds: 905, 164, 49, 90
|
||||
BIN
spine-ts/spine-pixi-v8/example/assets/spineboy.png
Normal file
|
After Width: | Height: | Size: 240 KiB |
19
spine-ts/spine-pixi-v8/example/assets/stretchyman-pma.atlas
Normal file
@ -0,0 +1,19 @@
|
||||
stretchyman-pma.png
|
||||
size: 1024, 256
|
||||
filter: Linear, Linear
|
||||
pma: true
|
||||
back-arm
|
||||
bounds: 679, 173, 72, 202
|
||||
rotate: 90
|
||||
back-leg
|
||||
bounds: 2, 2, 100, 318
|
||||
rotate: 90
|
||||
body
|
||||
bounds: 2, 104, 141, 452
|
||||
rotate: 90
|
||||
front-arm
|
||||
bounds: 456, 100, 145, 221
|
||||
rotate: 90
|
||||
head
|
||||
bounds: 322, 15, 87, 102
|
||||
rotate: 90
|
||||
BIN
spine-ts/spine-pixi-v8/example/assets/stretchyman-pma.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
spine-ts/spine-pixi-v8/example/assets/stretchyman-pro.skel
Normal file
71
spine-ts/spine-pixi-v8/example/coin.html
Normal file
@ -0,0 +1,71 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-pixi-v8</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8.4.1/dist/pixi.min.js"></script>
|
||||
<script src="../dist/iife/spine-pixi-v8.js"></script>
|
||||
<script src="./assets/lil-gui.umd.min.js"></script>
|
||||
<link href="./assets/lil-gui.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
(async function () {
|
||||
var app = new PIXI.Application();
|
||||
await app.init({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
resolution: window.devicePixelRatio || 1,
|
||||
autoDensity: true,
|
||||
resizeTo: window,
|
||||
backgroundColor: 0x2c3e50,
|
||||
hello: true,
|
||||
})
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||
PIXI.Assets.add({alias: "spineboyData", src: "./assets/coin-pro.json"});
|
||||
PIXI.Assets.add({alias: "spineboyAtlas", src: "./assets/coin-pro.atlas"});
|
||||
await PIXI.Assets.load(["spineboyData", "spineboyAtlas"]);
|
||||
|
||||
// Create the spine display object
|
||||
const spineboy = spine.Spine.from({
|
||||
skeleton: "spineboyData",
|
||||
atlas: "spineboyAtlas",
|
||||
scale: 1,
|
||||
// darkTint: true,
|
||||
});
|
||||
spineboy.autoUpdate = false;
|
||||
|
||||
// Set the default mix time to use when transitioning
|
||||
// from one animation to the next.
|
||||
spineboy.state.data.defaultMix = 0.2;
|
||||
|
||||
// Center the spine object on screen.
|
||||
spineboy.x = window.innerWidth / 2;
|
||||
spineboy.y = window.innerHeight / 2;
|
||||
|
||||
// Set animation "run" on track 0, looped.
|
||||
spineboy.state.setAnimation(0, "animation", true);
|
||||
|
||||
// Add the display object to the stage.
|
||||
app.stage.addChild(spineboy);
|
||||
|
||||
const myObject = { time: 0, time2: 0 };
|
||||
let prevValue = myObject.time;
|
||||
let prevValue2 = myObject.time2;
|
||||
spineboy.update(prevValue / 10)
|
||||
|
||||
const gui = new lil.GUI({});
|
||||
gui
|
||||
.add(myObject, 'time').min(0).max(10).step(0.001)
|
||||
.name( 'time' )
|
||||
.onChange(value => {
|
||||
spineboy.update((value - prevValue) / 10)
|
||||
prevValue = value;
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
109
spine-ts/spine-pixi-v8/example/control-bones-example.html
Normal file
@ -0,0 +1,109 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-pixi-v8</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8.4.1/dist/pixi.min.js"></script>
|
||||
<script src="../dist/iife/spine-pixi-v8.js"></script>
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
(async function () {
|
||||
var app = new PIXI.Application();
|
||||
await app.init({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
resolution: window.devicePixelRatio || 1,
|
||||
autoDensity: true,
|
||||
resizeTo: window,
|
||||
backgroundColor: 0x2c3e50,
|
||||
hello: true,
|
||||
})
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
app.stage.eventMode = 'static';
|
||||
app.stage.hitArea = app.screen;
|
||||
let dragObject = null;
|
||||
let lastX = -1, lastY = -1;
|
||||
const endDrag = () => (dragObject = null);
|
||||
app.stage
|
||||
.on('pointerup', endDrag)
|
||||
.on('pointerupoutside', endDrag)
|
||||
.on('pointermove', ({ x, y }) => {
|
||||
if (dragObject) {
|
||||
dragObject.x += x - lastX;
|
||||
dragObject.y += y - lastY;
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
}
|
||||
});
|
||||
|
||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||
PIXI.Assets.add({alias: "stretchymanData", src: "./assets/stretchyman-pro.skel"});
|
||||
PIXI.Assets.add({alias: "stretchymanAtlas", src: "./assets/stretchyman-pma.atlas"});
|
||||
await PIXI.Assets.load(["stretchymanData", "stretchymanAtlas"]);
|
||||
|
||||
// Create the spine display object
|
||||
const stretchyman = spine.Spine.from({skeleton: "stretchymanData", atlas: "stretchymanAtlas",
|
||||
scale: 0.75,
|
||||
});
|
||||
|
||||
// Set the default mix time to use when transitioning
|
||||
// from one animation to the next.
|
||||
stretchyman.state.data.defaultMix = 0.2;
|
||||
|
||||
// Center the spine object on screen.
|
||||
stretchyman.x = window.innerWidth / 2;
|
||||
stretchyman.y = window.innerHeight / 2 + stretchyman.getBounds().height / 2;
|
||||
|
||||
// Set animation "run" on track 0, looped.
|
||||
stretchyman.state.setAnimation(0, "idle", true);
|
||||
app.stage.addChild(stretchyman);
|
||||
|
||||
const controlBoneNames = [
|
||||
"back-arm-ik-target",
|
||||
"back-leg-ik-target",
|
||||
"front-arm-ik-target",
|
||||
"front-leg-ik-target",
|
||||
];
|
||||
const controlBones = [];
|
||||
|
||||
// wait a frame as pixi bounds do not work until rendered
|
||||
await new Promise((resolve) => requestAnimationFrame(resolve));
|
||||
|
||||
for (var i = 0; i < controlBoneNames.length; i++) {
|
||||
const bone = stretchyman.skeleton.findBone(controlBoneNames[i]);
|
||||
const point = { x: bone.worldX, y: bone.worldY };
|
||||
stretchyman.skeletonToPixiWorldCoordinates(point);
|
||||
|
||||
const control = new PIXI.Graphics()
|
||||
.circle(0, 0, 6)
|
||||
.fill('#ff00ff')
|
||||
control.x = point.x;
|
||||
control.y = point.y;
|
||||
controlBones.push({ bone, control });
|
||||
app.stage.addChild(control);
|
||||
|
||||
control.interactive = "static";
|
||||
control.on('pointerdown', ({ x, y }) => {
|
||||
dragObject = control;
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
})
|
||||
}
|
||||
|
||||
const point = { x: 0, y: 0 };
|
||||
stretchyman.beforeUpdateWorldTransforms = () => {
|
||||
for (let { bone, control } of controlBones) {
|
||||
point.x = control.x;
|
||||
point.y = control.y;
|
||||
stretchyman.pixiWorldCoordinatesToBone(point, bone);
|
||||
bone.x = point.x;
|
||||
bone.y = point.y;
|
||||
}
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
86
spine-ts/spine-pixi-v8/example/dragon.html
Normal file
@ -0,0 +1,86 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-pixi-v8</title>
|
||||
<script src="./assets/pixi.min.js"></script>
|
||||
<script src="../dist/iife/spine-pixi-v8.js"></script>
|
||||
<script src="./assets/lil-gui.umd.min.js"></script>
|
||||
<link href="./assets/lil-gui.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
(async function () {
|
||||
var app = new PIXI.Application();
|
||||
await app.init({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
resolution: window.devicePixelRatio || 1,
|
||||
autoDensity: true,
|
||||
resizeTo: window,
|
||||
backgroundColor: 0x2c3e50,
|
||||
hello: true,
|
||||
})
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||
PIXI.Assets.add({alias: "spineboyData", src: "./assets/dragon-ess.skel" });
|
||||
PIXI.Assets.add({alias: "spineboyAtlas", src: "./assets/dragon-pma.atlas" });
|
||||
PIXI.Assets.add({alias: "spineboyData2", src: "./assets/dragon-ess.skel" });
|
||||
PIXI.Assets.add({alias: "spineboyAtlas2", src: "./assets/dragon-pma.atlas" });
|
||||
await PIXI.Assets.load(["spineboyData", "spineboyAtlas", "spineboyData", "spineboyAtlas2", "raptor_jaw"]);
|
||||
|
||||
// Create the spine display object
|
||||
const spineboy = spine.Spine.from({skeleton: "spineboyData", atlas: "spineboyAtlas", scale: 0.5 });
|
||||
const spineboy2 = spine.Spine.from({skeleton: "spineboyData", atlas: "spineboyAtlas", scale: 0.5 });
|
||||
spineboy.autoUpdate = false;
|
||||
spineboy2.autoUpdate = false;
|
||||
|
||||
// Set the default mix time to use when transitioning
|
||||
// from one animation to the next.
|
||||
spineboy.state.data.defaultMix = 0.2;
|
||||
|
||||
// Center the spine object on screen.
|
||||
spineboy.x = window.innerWidth / 2;
|
||||
spineboy.y = window.innerHeight / 2 - 30;
|
||||
|
||||
spineboy2.x = window.innerWidth / 2;
|
||||
spineboy2.y = window.innerHeight / 2 + 200;
|
||||
spineboy2.state.setAnimation(0, "flying", true);
|
||||
|
||||
// Set animation "run" on track 0, looped.
|
||||
spineboy.state.setAnimation(0, "flying", true);
|
||||
|
||||
// Add the display object to the stage.
|
||||
app.stage.addChild(spineboy);
|
||||
app.stage.addChild(spineboy2);
|
||||
|
||||
const myObject = { time: 0, time2: 0 };
|
||||
let prevValue = myObject.time;
|
||||
let prevValue2 = myObject.time2;
|
||||
spineboy.update(prevValue / 10)
|
||||
spineboy2.update(prevValue2 / 10)
|
||||
|
||||
const gui = new lil.GUI({});
|
||||
gui
|
||||
.add(myObject, 'time').min(0).max(10).step(0.01)
|
||||
.name( 'time' )
|
||||
.onChange(value => {
|
||||
spineboy.update((value - prevValue) / 10)
|
||||
prevValue = value;
|
||||
});
|
||||
|
||||
gui
|
||||
.add(myObject, 'time2').min(0).max(10).step(0.01)
|
||||
.name( 'time2' )
|
||||
.onChange(value => {
|
||||
spineboy2.update((value - prevValue2) / 10)
|
||||
prevValue2 = value;
|
||||
});
|
||||
|
||||
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
81
spine-ts/spine-pixi-v8/example/events-example.html
Normal file
@ -0,0 +1,81 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-pixi-v8</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8.4.1/dist/pixi.min.js"></script>
|
||||
<script src="../dist/iife/spine-pixi-v8.js"></script>
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Creates a transparent logging overlay. -->
|
||||
<div id="log" class="overlay" style="user-select: none;" max-height: 300px; overflow: auto;>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function log(message) {
|
||||
const log = document.querySelector("#log");
|
||||
log.innerText += message + "\n";
|
||||
log.scrollTop = log.scrollHeight;
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
(async function () {
|
||||
var app = new PIXI.Application();
|
||||
await app.init({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
resolution: window.devicePixelRatio || 1,
|
||||
autoDensity: true,
|
||||
resizeTo: window,
|
||||
backgroundColor: 0x2c3e50,
|
||||
hello: true,
|
||||
})
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||
PIXI.Assets.add({alias: "spineboyData", src: "./assets/spineboy-pro.skel" });
|
||||
PIXI.Assets.add({alias: "spineboyAtlas", src: "./assets/spineboy-pma.atlas" });
|
||||
await PIXI.Assets.load(["spineboyData", "spineboyAtlas"]);
|
||||
|
||||
// Create the Spine display object
|
||||
const spineboy = spine.Spine.from({skeleton: "spineboyData", atlas: "spineboyAtlas",
|
||||
scale: 0.5,
|
||||
});
|
||||
|
||||
// Set animation "run" on track 0, looped.
|
||||
spineboy.state.setAnimation(0, "run", true);
|
||||
|
||||
// Set callbacks to receive animation state events.
|
||||
spineboy.state.addListener({
|
||||
start: (entry) => log(`Started animation ${entry.animation.name}`),
|
||||
interrupt: (entry) => log(`Interrupted animation ${entry.animation.name}`),
|
||||
end: (entry) => log(`Ended animation ${entry.animation.name}`),
|
||||
dispose: (entry) => log(`Disposed animation ${entry.animation.name}`),
|
||||
complete: (entry) => log(`Completed animation ${entry.animation.name}`),
|
||||
});
|
||||
|
||||
// Add a custom event listener along with an
|
||||
// unlooped animation to see the custom event logged.
|
||||
const trackEntry = spineboy.state.addAnimation(0, "walk", false, 3);
|
||||
trackEntry.listener = {
|
||||
event: (entry, event) =>
|
||||
log(`Custom event for ${entry.animation.name}: ${event.data.name}`),
|
||||
};
|
||||
|
||||
spineboy.state.addAnimation(0, "run", true, 0);
|
||||
|
||||
// Set the default mix time to use when transitioning
|
||||
// from one animation to the next.
|
||||
spineboy.state.data.defaultMix = 0.2;
|
||||
|
||||
// Center the spine object on screen.
|
||||
spineboy.x = window.innerWidth / 2;
|
||||
spineboy.y = window.innerHeight / 2 + spineboy.getBounds().height / 2;
|
||||
|
||||
// Add the display object to the stage.
|
||||
app.stage.addChild(spineboy);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
51
spine-ts/spine-pixi-v8/example/index.html
Normal file
@ -0,0 +1,51 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-pixi-v8</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8.4.1/dist/pixi.min.js"></script>
|
||||
<script src="../dist/iife/spine-pixi-v8.js"></script>
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
(async function () {
|
||||
var app = new PIXI.Application();
|
||||
await app.init({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
resolution: window.devicePixelRatio || 1,
|
||||
autoDensity: true,
|
||||
resizeTo: window,
|
||||
backgroundColor: 0x2c3e50,
|
||||
hello: true,
|
||||
})
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||
PIXI.Assets.add({alias: "spineboyData", src: "./assets/spineboy-pro.skel"});
|
||||
PIXI.Assets.add({alias: "spineboyAtlas", src: "./assets/spineboy-pma.atlas"});
|
||||
await PIXI.Assets.load(["spineboyData", "spineboyAtlas"]);
|
||||
|
||||
// Create the spine display object
|
||||
const spineboy = spine.Spine.from({skeleton: "spineboyData", atlas: "spineboyAtlas",
|
||||
scale: 0.5,
|
||||
});
|
||||
|
||||
// Set the default mix time to use when transitioning
|
||||
// from one animation to the next.
|
||||
spineboy.state.data.defaultMix = 0.2;
|
||||
|
||||
// Center the spine object on screen.
|
||||
spineboy.x = window.innerWidth / 2;
|
||||
spineboy.y = window.innerHeight / 2 + spineboy.getBounds().height / 2;
|
||||
|
||||
// Set animation "run" on track 0, looped.
|
||||
spineboy.state.setAnimation(0, "run", true);
|
||||
|
||||
// Add the display object to the stage.
|
||||
app.stage.addChild(spineboy);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
58
spine-ts/spine-pixi-v8/example/manual-loading.html
Normal file
@ -0,0 +1,58 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-pixi-v8</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8.4.1/dist/pixi.min.js"></script>
|
||||
<script src="../dist/iife/spine-pixi-v8.js"></script>
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
(async function () {
|
||||
var app = new PIXI.Application();
|
||||
await app.init({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
resolution: window.devicePixelRatio || 1,
|
||||
autoDensity: true,
|
||||
resizeTo: window,
|
||||
backgroundColor: 0x2c3e50,
|
||||
hello: true,
|
||||
})
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||
PIXI.Assets.add({alias: "spineboyData", src: "./assets/spineboy-pro.skel" });
|
||||
PIXI.Assets.add({alias: "spineboyAtlas", src: "./assets/spineboy-pma.atlas" });
|
||||
await PIXI.Assets.load(["spineboyData", "spineboyAtlas"]);
|
||||
|
||||
// Manually load the data and create a Spine display object from it using
|
||||
// the Spine core API. This will not use the interal cache like Spine.from(),
|
||||
// so you have to cache data yourself.
|
||||
const atlas = PIXI.Assets.get("spineboyAtlas");
|
||||
const attachmentLoader = new spine.AtlasAttachmentLoader(atlas);
|
||||
const binaryLoader = new spine.SkeletonBinary(attachmentLoader);
|
||||
binaryLoader.scale = 0.5;
|
||||
const skeletonData = binaryLoader.readSkeletonData(
|
||||
PIXI.Assets.get("spineboyData")
|
||||
);
|
||||
const spineboy = new spine.Spine(skeletonData);
|
||||
|
||||
// Set the default mix time to use when transitioning
|
||||
// from one animation to the next.
|
||||
spineboy.state.data.defaultMix = 0.2;
|
||||
|
||||
// Center the spine object on screen.
|
||||
spineboy.x = window.innerWidth / 2;
|
||||
spineboy.y = window.innerHeight / 2 + spineboy.getBounds().height / 2;
|
||||
|
||||
// Set animation "run" on track 0, looped.
|
||||
spineboy.state.setAnimation(0, "run", true);
|
||||
|
||||
// Add the display object to the stage.
|
||||
app.stage.addChild(spineboy);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
68
spine-ts/spine-pixi-v8/example/mix-and-match-example.html
Normal file
@ -0,0 +1,68 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-pixi-v8</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8.4.1/dist/pixi.min.js"></script>
|
||||
<script src="../dist/iife/spine-pixi-v8.js"></script>
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
(async function () {
|
||||
var app = new PIXI.Application();
|
||||
await app.init({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
resolution: window.devicePixelRatio || 1,
|
||||
autoDensity: true,
|
||||
resizeTo: window,
|
||||
backgroundColor: 0x2c3e50,
|
||||
hello: true,
|
||||
})
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||
PIXI.Assets.add({alias: "mixAndMatchData", src: "./assets/mix-and-match-pro.skel" });
|
||||
PIXI.Assets.add({alias: "mixAndMatchAtlas", src: "./assets/mix-and-match-pma.atlas" });
|
||||
await PIXI.Assets.load(["mixAndMatchData", "mixAndMatchAtlas"]);
|
||||
|
||||
// Create the Spine display object
|
||||
const mixAndMatch = spine.Spine.from({skeleton: "mixAndMatchData", atlas: "mixAndMatchAtlas",
|
||||
scale: 0.5,
|
||||
});
|
||||
|
||||
// Set the default mix time to use when transitioning
|
||||
// from one animation to the next.
|
||||
mixAndMatch.state.data.defaultMix = 0.2;
|
||||
|
||||
// Add animations.
|
||||
mixAndMatch.state.setAnimation(0, "walk", true);
|
||||
mixAndMatch.state.addAnimation(0, "dance", true, 1.0);
|
||||
mixAndMatch.state.addAnimation(0, "walk", true, 1.0);
|
||||
|
||||
// Add a custom skin
|
||||
const skeletonData = mixAndMatch.skeleton.data;
|
||||
const skin = new spine.Skin("custom");
|
||||
skin.addSkin(skeletonData.findSkin("nose/short"));
|
||||
skin.addSkin(skeletonData.findSkin("skin-base"));
|
||||
skin.addSkin(skeletonData.findSkin("eyes/violet"));
|
||||
skin.addSkin(skeletonData.findSkin("hair/brown"));
|
||||
skin.addSkin(skeletonData.findSkin("clothes/hoodie-orange"));
|
||||
skin.addSkin(skeletonData.findSkin("legs/pants-jeans"));
|
||||
skin.addSkin(skeletonData.findSkin("accessories/bag"));
|
||||
skin.addSkin(skeletonData.findSkin("accessories/hat-red-yellow"));
|
||||
skin.addSkin(skeletonData.findSkin("eyelids/girly"));
|
||||
mixAndMatch.skeleton.setSkin(skin);
|
||||
mixAndMatch.skeleton.setSlotsToSetupPose();
|
||||
|
||||
// Center the spine object on screen.
|
||||
mixAndMatch.x = window.innerWidth / 2;
|
||||
mixAndMatch.y = window.innerHeight / 2 + mixAndMatch.getBounds().height / 2;
|
||||
|
||||
// Add the display object to the stage.
|
||||
app.stage.addChild(mixAndMatch);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
91
spine-ts/spine-pixi-v8/example/mouse-following.html
Normal file
@ -0,0 +1,91 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-pixi-v8</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8.4.1/dist/pixi.min.js"></script>
|
||||
<script src="../dist/iife/spine-pixi-v8.js"></script>
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
(async function () {
|
||||
var app = new PIXI.Application();
|
||||
await app.init({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
resolution: window.devicePixelRatio || 1,
|
||||
autoDensity: true,
|
||||
resizeTo: window,
|
||||
backgroundColor: 0x2c3e50,
|
||||
hello: true,
|
||||
})
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||
PIXI.Assets.add({alias: "spineboyData", src: "./assets/spineboy-pro.skel" });
|
||||
PIXI.Assets.add({alias: "spineboyAtlas", src: "./assets/spineboy-pma.atlas" });
|
||||
await PIXI.Assets.load(["spineboyData", "spineboyAtlas"]);
|
||||
|
||||
// Create the spine display object
|
||||
const spineboy = spine.Spine.from({skeleton: "spineboyData", atlas: "spineboyAtlas",
|
||||
scale: 0.5,
|
||||
});
|
||||
|
||||
// Set the default mix time to use when transitioning
|
||||
// from one animation to another.
|
||||
spineboy.state.data.defaultMix = 0.2;
|
||||
|
||||
// Center the Spine object on screen.
|
||||
spineboy.x = window.innerWidth / 2;
|
||||
spineboy.y = window.innerHeight / 2 + spineboy.getBounds().height / 2;
|
||||
|
||||
// Set looping animations "idle" on track 0 and "aim" on track 1.
|
||||
spineboy.state.setAnimation(0, "idle", true);
|
||||
spineboy.state.setAnimation(1, "aim", true);
|
||||
|
||||
// Add the display object to the stage.
|
||||
app.stage.addChild(spineboy);
|
||||
app.stage.hitArea = new PIXI.Rectangle(0, 0, app.view.width, app.view.height);
|
||||
|
||||
// Make the stage interactive and register pointer events
|
||||
app.stage.eventMode = "dynamic";
|
||||
let isDragging = false;
|
||||
|
||||
app.stage.on("pointerdown", (e) => {
|
||||
isDragging = true;
|
||||
setBonePosition(e);
|
||||
});
|
||||
|
||||
app.stage.on("globalpointermove", (e) => {
|
||||
if (isDragging) setBonePosition(e);
|
||||
});
|
||||
|
||||
app.stage.on("pointerup", (e) => (isDragging = false));
|
||||
|
||||
const setBonePosition = (e) => {
|
||||
// Transform the mouse/touch coordinates to Spineboy's coordinate
|
||||
// system origin. `position` is then relative to Spineboy's root
|
||||
// bone.
|
||||
const position = new spine.Vector2(
|
||||
e.data.global.x - spineboy.x,
|
||||
e.data.global.y - spineboy.y
|
||||
);
|
||||
|
||||
// Find the crosshair bone.
|
||||
const crosshairBone = spineboy.skeleton.findBone("crosshair");
|
||||
|
||||
// Take the mouse position, which is relative to the root bone,
|
||||
// and transform it to the crosshair bone's parent root bone
|
||||
// coordinate system via `worldToLocal()`. `position` is relative
|
||||
// to the crosshair bone's parent bone after this
|
||||
crosshairBone.parent.worldToLocal(position);
|
||||
|
||||
// Set the crosshair bone's position to the mouse position
|
||||
crosshairBone.x = position.x;
|
||||
crosshairBone.y = position.y;
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
47
spine-ts/spine-pixi-v8/example/physics.html
Normal file
@ -0,0 +1,47 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-pixi-v8</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8.4.1/dist/pixi.min.js"></script>
|
||||
<script src="../dist/iife/spine-pixi-v8.js"></script>
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
(async function () {
|
||||
var app = new PIXI.Application();
|
||||
await app.init({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
resolution: window.devicePixelRatio || 1,
|
||||
autoDensity: true,
|
||||
resizeTo: window,
|
||||
backgroundColor: 0x2c3e50,
|
||||
hello: true,
|
||||
})
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||
PIXI.Assets.add({alias: "sackData", src: "./assets/sack-pro.skel"});
|
||||
PIXI.Assets.add({alias: "sackAtlas", src: "./assets/sack-pma.atlas"});
|
||||
await PIXI.Assets.load(["sackData", "sackAtlas"]);
|
||||
|
||||
// Create the spine display object
|
||||
const sack = spine.Spine.from({skeleton: "sackData", atlas: "sackAtlas",
|
||||
scale: 0.2,
|
||||
});
|
||||
|
||||
// Center the spine object on screen.
|
||||
sack.x = window.innerWidth / 2;
|
||||
sack.y = window.innerHeight / 2 + sack.getBounds().height / 2;
|
||||
|
||||
// Set animation "cape-follow-example" on track 0, looped.
|
||||
sack.state.setAnimation(0, "cape-follow-example", true);
|
||||
|
||||
// Add the display object to the stage.
|
||||
app.stage.addChild(sack);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
142
spine-ts/spine-pixi-v8/example/physics2.html
Normal file
@ -0,0 +1,142 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-pixi-v8</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8.4.1/dist/pixi.min.js"></script>
|
||||
<script src="../dist/iife/spine-pixi-v8.js"></script>
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
(async function () {
|
||||
var app = new PIXI.Application();
|
||||
await app.init({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
resolution: window.devicePixelRatio || 1,
|
||||
autoDensity: true,
|
||||
resizeTo: window,
|
||||
backgroundColor: 0x2c3e50,
|
||||
hello: true,
|
||||
})
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||
PIXI.Assets.add({alias: "girlData", src: "./assets/celestial-circus-pro.skel"});
|
||||
PIXI.Assets.add({alias: "girlAtlas", src: "./assets/celestial-circus-pma.atlas"});
|
||||
await PIXI.Assets.load(["girlData", "girlAtlas"]);
|
||||
|
||||
// Create the spine display object
|
||||
const girl = spine.Spine.from({skeleton: "girlData", atlas: "girlAtlas",
|
||||
scale: 0.2,
|
||||
});
|
||||
|
||||
// Center the spine object on screen.
|
||||
girl.x = window.innerWidth / 2;
|
||||
girl.y = window.innerHeight / 2 + girl.getBounds().height / 4;
|
||||
|
||||
// Set animation "eyeblink-long" on track 0, looped.
|
||||
girl.state.setAnimation(0, "eyeblink-long", true);
|
||||
|
||||
// Add the display object to the stage.
|
||||
app.stage.addChild(girl);
|
||||
|
||||
// Make the stage interactive and register pointer events
|
||||
app.stage.eventMode = "dynamic";
|
||||
app.stage.hitArea = app.screen;
|
||||
let isDragging = false;
|
||||
let lastX = -1, lastY = -1;
|
||||
|
||||
app.stage.on("pointerdown", (e) => {
|
||||
isDragging = true;
|
||||
let mousePosition = new spine.Vector2(e.data.global.x, e.data.global.y);
|
||||
lastX = mousePosition.x;
|
||||
lastY = mousePosition.y;
|
||||
});
|
||||
|
||||
app.stage.on("pointermove", (e) => {
|
||||
if (isDragging) {
|
||||
let mousePosition = new spine.Vector2(e.data.global.x, e.data.global.y);
|
||||
girl.x += mousePosition.x - lastX;
|
||||
girl.y += mousePosition.y - lastY;
|
||||
girl.skeleton.physicsTranslate(mousePosition.x - lastX, mousePosition.y - lastY);
|
||||
lastX = mousePosition.x;
|
||||
lastY = mousePosition.y;
|
||||
}
|
||||
});
|
||||
|
||||
const endDrag = () => (isDragging = false);
|
||||
app.stage.on("pointerup", endDrag);
|
||||
app.stage.on("pointerupoutside", endDrag);
|
||||
document.addEventListener('fullscreenchange', () => {
|
||||
endDrag();
|
||||
});
|
||||
|
||||
const buttonContainer = new PIXI.Container();
|
||||
buttonContainer.position.set(0, 0);
|
||||
|
||||
const buttonBackground = new PIXI.Graphics();
|
||||
buttonBackground.beginFill(0x000000); // Button background color
|
||||
buttonBackground.drawRoundedRect(0, 0, 140, 100, 5); // Button dimensions
|
||||
buttonBackground.endFill();
|
||||
buttonContainer.addChild(buttonBackground);
|
||||
buttonContainer.alpha = 0.7;
|
||||
|
||||
const fontStyle = {
|
||||
fill: 0xdddddd,
|
||||
fontFamily: 'sans-serif',
|
||||
fontSize: 16,
|
||||
};
|
||||
|
||||
// Create the text label for the heading
|
||||
const textHeading = new PIXI.Text("Drag anywhere", fontStyle); // Button text and color
|
||||
textHeading.position.set(15, 15); // Set the position of the text within the button
|
||||
buttonContainer.addChild(textHeading);
|
||||
|
||||
// Create the text label for the FPS counter
|
||||
const textFps = new PIXI.Text("0 fps", fontStyle);
|
||||
textFps.position.set(15, 40);
|
||||
buttonContainer.addChild(textFps);
|
||||
|
||||
// Create the text label for the button toggle fullscreen
|
||||
const textButton = new PIXI.Text("Fullscreen", fontStyle); // Button text and color
|
||||
textButton.position.set(15, 65); // Set the position of the text within the button
|
||||
buttonContainer.addChild(textButton);
|
||||
|
||||
// Add the button container to the stage
|
||||
app.stage.addChild(buttonContainer);
|
||||
|
||||
buttonContainer.interactive = true;
|
||||
buttonContainer.buttonMode = true;
|
||||
|
||||
let fsEnabled = false;
|
||||
buttonContainer.on('pointerdown', () => {
|
||||
if (fsEnabled) {
|
||||
document.exitFullscreen();
|
||||
textButton.text = "Fullscreen";
|
||||
} else {
|
||||
app.renderer.view.requestFullscreen();
|
||||
textButton.text = "Windowed";
|
||||
}
|
||||
fsEnabled = !fsEnabled;
|
||||
});
|
||||
|
||||
app.ticker.add(throttle(() => textFps.text = app.ticker.FPS.toFixed(2) + " fps", 250));
|
||||
})();
|
||||
|
||||
const throttle = (func, delay) => {
|
||||
let lastCall = 0;
|
||||
|
||||
return (...args) => {
|
||||
const now = Date.now();
|
||||
|
||||
if (now - lastCall >= delay) {
|
||||
func.apply(this, args);
|
||||
lastCall = now;
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
47
spine-ts/spine-pixi-v8/example/physics3.html
Normal file
@ -0,0 +1,47 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-pixi-v8</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8.4.1/dist/pixi.min.js"></script>
|
||||
<script src="../dist/iife/spine-pixi-v8.js"></script>
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
(async function () {
|
||||
var app = new PIXI.Application();
|
||||
await app.init({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
resolution: window.devicePixelRatio || 1,
|
||||
autoDensity: true,
|
||||
resizeTo: window,
|
||||
backgroundColor: 0x2c3e50,
|
||||
hello: true,
|
||||
})
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||
PIXI.Assets.add({alias: "snowglobeData", src: "./assets/snowglobe-pro.skel"});
|
||||
PIXI.Assets.add({alias: "snowglobeAtlas", src: "./assets/snowglobe-pma.atlas"});
|
||||
await PIXI.Assets.load(["snowglobeData", "snowglobeAtlas"]);
|
||||
|
||||
// Create the spine display object
|
||||
const snowglobe = spine.Spine.from({skeleton: "snowglobeData", atlas: "snowglobeAtlas",
|
||||
scale: 0.25,
|
||||
});
|
||||
|
||||
// Center the spine object on screen.
|
||||
snowglobe.x = window.innerWidth / 2;
|
||||
snowglobe.y = window.innerHeight / 2 + snowglobe.getBounds().height / 4;
|
||||
|
||||
// Set animation "FOA" on track 0, looped.
|
||||
snowglobe.state.setAnimation(0, "shake", true);
|
||||
|
||||
// Add the display object to the stage.
|
||||
app.stage.addChild(snowglobe);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
47
spine-ts/spine-pixi-v8/example/physics4.html
Normal file
@ -0,0 +1,47 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-pixi-v8</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8.4.1/dist/pixi.min.js"></script>
|
||||
<script src="../dist/iife/spine-pixi-v8.js"></script>
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
(async function () {
|
||||
var app = new PIXI.Application();
|
||||
await app.init({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
resolution: window.devicePixelRatio || 1,
|
||||
autoDensity: true,
|
||||
resizeTo: window,
|
||||
backgroundColor: 0x2c3e50,
|
||||
hello: true,
|
||||
})
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||
PIXI.Assets.add({alias: "cloudPotData", src: "./assets/cloud-pot.skel"});
|
||||
PIXI.Assets.add({alias: "cloudPotAtlas", src: "./assets/cloud-pot-pma.atlas"});
|
||||
await PIXI.Assets.load(["cloudPotData", "cloudPotAtlas"]);
|
||||
|
||||
// Create the spine display object
|
||||
const cloudPot = spine.Spine.from({skeleton: "cloudPotData", atlas: "cloudPotAtlas",
|
||||
scale: 0.25,
|
||||
});
|
||||
|
||||
// Center the spine object on screen.
|
||||
cloudPot.x = window.innerWidth / 2;
|
||||
cloudPot.y = window.innerHeight / 2 + cloudPot.getBounds().height / 4;
|
||||
|
||||
// Set animation "playing-in-the-rain" on track 0, looped.
|
||||
cloudPot.state.setAnimation(0, "playing-in-the-rain", true);
|
||||
|
||||
// Add the display object to the stage.
|
||||
app.stage.addChild(cloudPot);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
98
spine-ts/spine-pixi-v8/example/simple-input.html
Normal file
@ -0,0 +1,98 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-pixi-v8</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8.4.1/dist/pixi.min.js"></script>
|
||||
<script src="../dist/iife/spine-pixi-v8.js"></script>
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
(async function () {
|
||||
var app = new PIXI.Application();
|
||||
await app.init({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
resolution: window.devicePixelRatio || 1,
|
||||
autoDensity: true,
|
||||
resizeTo: window,
|
||||
backgroundColor: 0x2c3e50,
|
||||
hello: true,
|
||||
})
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||
PIXI.Assets.add({alias: "spineboyData", src: "./assets/spineboy-pro.skel" });
|
||||
PIXI.Assets.add({alias: "spineboyAtlas", src: "./assets/spineboy-pma.atlas" });
|
||||
await PIXI.Assets.load(["spineboyData", "spineboyAtlas"]);
|
||||
|
||||
// Create the spine display object
|
||||
const spineboy = spine.Spine.from({skeleton: "spineboyData", atlas: "spineboyAtlas",
|
||||
scale: 0.5,
|
||||
});
|
||||
|
||||
// Set the default animation and the
|
||||
// default mix for transitioning between animations.
|
||||
spineboy.state.setAnimation(0, "hoverboard", true);
|
||||
spineboy.state.data.defaultMix = 0.2;
|
||||
|
||||
// Center the spine object on screen.
|
||||
spineboy.x = window.innerWidth / 2;
|
||||
spineboy.y = window.innerHeight / 2 + spineboy.getBounds().height / 2;
|
||||
|
||||
// Make it so that you can interact with Spineboy.
|
||||
// Handle the case that you click/tap the screen.
|
||||
spineboy.eventMode = 'static';
|
||||
spineboy.on('pointerdown', onClick);
|
||||
|
||||
// Add the display object to the stage.
|
||||
app.stage.addChild(spineboy);
|
||||
|
||||
// Add variables for movement, speed.
|
||||
let moveLeft = false;
|
||||
let moveRight = false;
|
||||
const speed = 5;
|
||||
|
||||
// Handle the case that the keyboard keys specified below are pressed.
|
||||
function onKeyDown(key) {
|
||||
if (key.code === "ArrowLeft" || key.code === "KeyA") {
|
||||
moveLeft = true;
|
||||
spineboy.skeleton.scaleX = -1;
|
||||
} else if (key.code === "ArrowRight" || key.code === "KeyD") {
|
||||
moveRight = true;
|
||||
spineboy.skeleton.scaleX = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle when the keys are released, if they were pressed.
|
||||
function onKeyUp(key) {
|
||||
if (key.code === "ArrowLeft" || key.code === "KeyA") {
|
||||
moveLeft = false;
|
||||
} else if (key.code === "ArrowRight" || key.code === "KeyD") {
|
||||
moveRight = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle if you click/tap the screen.
|
||||
function onClick() {
|
||||
spineboy.state.setAnimation(1, "shoot", false, 0);
|
||||
}
|
||||
|
||||
// Add event listeners so that the window will correctly handle input.
|
||||
window.addEventListener("keydown", onKeyDown);
|
||||
window.addEventListener("keyup", onKeyUp);
|
||||
|
||||
// Update the application to move Spineboy if input is detected.
|
||||
app.ticker.add(() => {
|
||||
if (moveLeft) {
|
||||
spineboy.x -= speed;
|
||||
}
|
||||
if (moveRight) {
|
||||
spineboy.x += speed;
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
141
spine-ts/spine-pixi-v8/example/slot-objects.html
Normal file
@ -0,0 +1,141 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>spine-pixi-v8</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pixi.js@8.4.1/dist/pixi.min.js"></script>
|
||||
<script src="../dist/iife/spine-pixi-v8.js"></script>
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
(async function () {
|
||||
var app = new PIXI.Application();
|
||||
await app.init({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
resolution: window.devicePixelRatio || 1,
|
||||
autoDensity: true,
|
||||
resizeTo: window,
|
||||
backgroundColor: 0x2c3e50,
|
||||
hello: true,
|
||||
})
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||
PIXI.Assets.add({alias: "spineboyData", src: "./assets/spineboy-pro.skel" });
|
||||
PIXI.Assets.add({alias: "spineboyAtlas", src: "./assets/spineboy-pma.atlas" });
|
||||
PIXI.Assets.add({alias: "raptor_jaw", src: "./assets/raptor-jaw-tooth.png" });
|
||||
await PIXI.Assets.load(["spineboyData", "spineboyAtlas", "raptor_jaw"]);
|
||||
|
||||
// Create the spine display object
|
||||
const spineboy = spine.Spine.from({skeleton: "spineboyData", atlas: "spineboyAtlas", scale: 0.25 });
|
||||
|
||||
// Set the default mix time to use when transitioning
|
||||
// from one animation to the next.
|
||||
spineboy.state.data.defaultMix = 0.2;
|
||||
|
||||
// Center the spine object on screen.
|
||||
spineboy.x = window.innerWidth / 2;
|
||||
spineboy.y = window.innerHeight / 2 + spineboy.getBounds().height / 2;
|
||||
|
||||
// Set animation "run" on track 0, looped.
|
||||
spineboy.state.setAnimation(0, "walk", true);
|
||||
|
||||
// Add the display object to the stage.
|
||||
app.stage.addChild(spineboy);
|
||||
|
||||
const tooth1 = PIXI.Sprite.from('raptor_jaw');
|
||||
const tooth2 = PIXI.Sprite.from('raptor_jaw');
|
||||
const text = new PIXI.Text('Text GUN');
|
||||
|
||||
const toothContainer = new PIXI.Container();
|
||||
toothContainer.addChild(tooth1);
|
||||
toothContainer.name = "tooth";
|
||||
text.name = "text";
|
||||
|
||||
// putting logo2 on top of the hand using slot directly and remove the attachment hand
|
||||
let frontFist;
|
||||
setTimeout(() => {
|
||||
frontFist = spineboy.skeleton.findSlot("front-foot");
|
||||
tooth1.x = -10;
|
||||
tooth1.y = -70;
|
||||
spineboy.addSlotObject(frontFist, toothContainer);
|
||||
frontFist.setAttachment(null);
|
||||
}, 1000);
|
||||
|
||||
// scaling the bone, will scale the pixi object too
|
||||
setTimeout(() => {
|
||||
frontFist.bone.scaleX = .5;
|
||||
frontFist.bone.scaleY = .5;
|
||||
}, 2000);
|
||||
|
||||
// adding a pixi text in a slot using slot index
|
||||
let mouth;
|
||||
setTimeout(() => {
|
||||
spineboy.addSlotObject("gun", text);
|
||||
}, 4000);
|
||||
|
||||
// adding one pixi object to an already "occupied" slot will remove the old one,
|
||||
// and move the given one to the slot
|
||||
setTimeout(() => {
|
||||
spineboy.addSlotObject("gun", toothContainer);
|
||||
}, 5000);
|
||||
|
||||
// adding multiple DisplayObjects to a slot using a Container to control their offset, size, ...
|
||||
setTimeout(() => {
|
||||
toothContainer.addChild(tooth2);
|
||||
tooth2.x = 30;
|
||||
tooth2.y = -70;
|
||||
tooth2.angle = 30;
|
||||
tooth2.tint = 0xFF5500;
|
||||
}, 6000);
|
||||
|
||||
// removing the container won't automatically destroy the displayObject contained, so take care of them
|
||||
setTimeout(() => {
|
||||
spineboy.removeSlotObject("gun");
|
||||
console.log(toothContainer.destroyed)
|
||||
console.log(tooth1.destroyed)
|
||||
console.log(tooth2.destroyed)
|
||||
toothContainer.destroy();
|
||||
tooth1.destroy();
|
||||
console.log(toothContainer.destroyed)
|
||||
console.log(tooth1.destroyed)
|
||||
console.log(tooth2.destroyed)
|
||||
}, 7000);
|
||||
|
||||
// removing a specific slot object, that is not in that slot do nothing
|
||||
setTimeout(() => {
|
||||
spineboy.addSlotObject("gun", tooth2);
|
||||
spineboy.removeSlotObject("gun", text);
|
||||
}, 8000);
|
||||
|
||||
// removing a specific slot object
|
||||
setTimeout(() => {
|
||||
spineboy.removeSlotObject("gun", tooth2);
|
||||
tooth2.destroy();
|
||||
}, 9000);
|
||||
|
||||
// resetting the slot with the original attachment
|
||||
setTimeout(() => {
|
||||
frontFist.setToSetupPose();
|
||||
frontFist.bone.setToSetupPose();
|
||||
}, 10000);
|
||||
|
||||
// showing an animation with clipping -> Pixi masks will be created
|
||||
// for clipping attachments having slot objects
|
||||
setTimeout(() => {
|
||||
spineboy.state.setAnimation(0, "portal", true)
|
||||
const tooth3 = PIXI.Sprite.from('raptor_jaw');
|
||||
tooth3.scale.set(2);
|
||||
tooth3.x = -60;
|
||||
tooth3.y = 120;
|
||||
tooth3.angle = 180;
|
||||
const foot1 = new PIXI.Container();
|
||||
foot1.addChild(tooth3);
|
||||
spineboy.addSlotObject("rear-foot", foot1);
|
||||
}, 11000);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
39
spine-ts/spine-pixi-v8/package.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "@esotericsoftware/spine-pixi-v8",
|
||||
"version": "4.2.62",
|
||||
"description": "The official Spine Runtimes for PixiJS v8.",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"scripts": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/esotericsoftware/spine-runtimes.git"
|
||||
},
|
||||
"keywords": [
|
||||
"gamedev",
|
||||
"animations",
|
||||
"2d",
|
||||
"spine",
|
||||
"game-dev",
|
||||
"runtimes",
|
||||
"skeletal"
|
||||
],
|
||||
"author": "Esoteric Software LLC",
|
||||
"license": "LicenseRef-LICENSE",
|
||||
"bugs": {
|
||||
"url": "https://github.com/esotericsoftware/spine-runtimes/issues"
|
||||
},
|
||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-core": "4.2.62"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pixi.js": "^8.4.0"
|
||||
}
|
||||
}
|
||||
138
spine-ts/spine-pixi-v8/src/BatchableSpineSlot.ts
Normal file
@ -0,0 +1,138 @@
|
||||
/** ****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import { AttachmentCacheData, Spine } from './Spine';
|
||||
|
||||
import type { Batch, Batcher, BLEND_MODES, DefaultBatchableMeshElement, Matrix, Texture, Topology } from 'pixi.js';
|
||||
|
||||
export class BatchableSpineSlot implements DefaultBatchableMeshElement {
|
||||
indexOffset = 0;
|
||||
attributeOffset = 0;
|
||||
|
||||
indexSize!: number;
|
||||
attributeSize!: number;
|
||||
|
||||
batcherName = 'darkTint';
|
||||
|
||||
topology:Topology = 'triangle-list';
|
||||
|
||||
readonly packAsQuad = false;
|
||||
|
||||
renderable!: Spine;
|
||||
|
||||
positions!: Float32Array;
|
||||
indices!: number[] | Uint16Array;
|
||||
uvs!: Float32Array;
|
||||
|
||||
roundPixels!: 0 | 1;
|
||||
data!: AttachmentCacheData;
|
||||
blendMode!: BLEND_MODES;
|
||||
|
||||
darkTint!: number;
|
||||
|
||||
texture!: Texture;
|
||||
|
||||
transform!: Matrix;
|
||||
|
||||
// used internally by batcher specific. Stored for efficient updating.
|
||||
_textureId!: number;
|
||||
_attributeStart!: number;
|
||||
_indexStart!: number;
|
||||
_batcher!: Batcher;
|
||||
_batch!: Batch;
|
||||
|
||||
|
||||
get color () {
|
||||
const slotColor = this.data.color;
|
||||
|
||||
const parentColor: number = this.renderable.groupColor;
|
||||
const parentAlpha: number = this.renderable.groupAlpha;
|
||||
let abgr: number;
|
||||
|
||||
const mixedA = (slotColor.a * parentAlpha) * 255;
|
||||
|
||||
if (parentColor !== 0xFFFFFF) {
|
||||
const parentB = (parentColor >> 16) & 0xFF;
|
||||
const parentG = (parentColor >> 8) & 0xFF;
|
||||
const parentR = parentColor & 0xFF;
|
||||
|
||||
const mixedR = (slotColor.r * parentR);
|
||||
const mixedG = (slotColor.g * parentG);
|
||||
const mixedB = (slotColor.b * parentB);
|
||||
|
||||
abgr = ((mixedA) << 24) | (mixedB << 16) | (mixedG << 8) | mixedR;
|
||||
}
|
||||
else {
|
||||
abgr = ((mixedA) << 24) | ((slotColor.b * 255) << 16) | ((slotColor.g * 255) << 8) | (slotColor.r * 255);
|
||||
}
|
||||
|
||||
return abgr;
|
||||
}
|
||||
|
||||
get darkColor () {
|
||||
const darkColor = this.data.darkColor;
|
||||
return ((darkColor.b * 255) << 16) | ((darkColor.g * 255) << 8) | (darkColor.r * 255);
|
||||
}
|
||||
|
||||
get groupTransform () { return this.renderable.groupTransform; }
|
||||
|
||||
setData (
|
||||
renderable: Spine,
|
||||
data: AttachmentCacheData,
|
||||
blendMode: BLEND_MODES,
|
||||
roundPixels: 0 | 1) {
|
||||
this.renderable = renderable;
|
||||
this.transform = renderable.groupTransform;
|
||||
this.data = data;
|
||||
|
||||
if (data.clipped) {
|
||||
const clippedData = data.clippedData;
|
||||
|
||||
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;
|
||||
this.attributeSize = data.vertices.length / 2;
|
||||
this.positions = data.vertices;
|
||||
this.indices = data.indices;
|
||||
this.uvs = data.uvs;
|
||||
}
|
||||
|
||||
this.texture = data.texture;
|
||||
this.roundPixels = roundPixels;
|
||||
|
||||
this.blendMode = blendMode;
|
||||
|
||||
this.batcherName = data.darkTint ? 'darkTint' : 'default';
|
||||
}
|
||||
}
|
||||
912
spine-ts/spine-pixi-v8/src/Spine.ts
Normal file
@ -0,0 +1,912 @@
|
||||
/** ****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import {
|
||||
Assets,
|
||||
Bounds,
|
||||
Cache,
|
||||
Container,
|
||||
ContainerOptions,
|
||||
DEG_TO_RAD,
|
||||
DestroyOptions,
|
||||
fastCopy,
|
||||
Graphics,
|
||||
PointData,
|
||||
Texture,
|
||||
Ticker,
|
||||
ViewContainer,
|
||||
} from 'pixi.js';
|
||||
import { ISpineDebugRenderer } from './SpineDebugRenderer';
|
||||
import {
|
||||
AnimationState,
|
||||
AnimationStateData,
|
||||
AtlasAttachmentLoader,
|
||||
Attachment,
|
||||
Bone,
|
||||
ClippingAttachment,
|
||||
Color,
|
||||
MeshAttachment,
|
||||
Physics,
|
||||
Pool,
|
||||
RegionAttachment,
|
||||
Skeleton,
|
||||
SkeletonBinary,
|
||||
SkeletonBounds,
|
||||
SkeletonClipping,
|
||||
SkeletonData,
|
||||
SkeletonJson,
|
||||
Slot,
|
||||
type TextureAtlas,
|
||||
TrackEntry,
|
||||
Vector2,
|
||||
} from '@esotericsoftware/spine-core';
|
||||
|
||||
/**
|
||||
* Options to create a {@link Spine} using {@link Spine.from}.
|
||||
*/
|
||||
export interface SpineFromOptions {
|
||||
/** the asset name for the skeleton `.skel` or `.json` file previously loaded into the Assets */
|
||||
skeleton: string;
|
||||
|
||||
/** the asset name for the atlas file previously loaded into the Assets */
|
||||
atlas: string;
|
||||
|
||||
/** The value passed to the skeleton reader. If omitted, 1 is passed. See {@link SkeletonBinary.scale} for details. */
|
||||
scale?: number;
|
||||
|
||||
/** Set the {@link Spine.autoUpdate} value. If omitted, it is set to `true`. */
|
||||
autoUpdate?: boolean;
|
||||
|
||||
/**
|
||||
* If `true`, use the dark tint renderer to render the skeleton
|
||||
* If `false`, use the default pixi renderer to render the skeleton
|
||||
* If `undefined`, use the dark tint renderer if at least one slot has tint black
|
||||
*/
|
||||
darkTint?: boolean;
|
||||
};
|
||||
|
||||
const vectorAux = new Vector2();
|
||||
|
||||
Skeleton.yDown = true;
|
||||
|
||||
const clipper = new SkeletonClipping();
|
||||
|
||||
export interface SpineOptions extends ContainerOptions {
|
||||
/** the {@link SkeletonData} used to instantiate the skeleton */
|
||||
skeletonData: SkeletonData;
|
||||
|
||||
/** See {@link SpineFromOptions.autoUpdate}. */
|
||||
autoUpdate?: boolean;
|
||||
|
||||
/** See {@link SpineFromOptions.darkTint}. */
|
||||
darkTint?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* AnimationStateListener {@link https://en.esotericsoftware.com/spine-api-reference#AnimationStateListener events} exposed for Pixi.
|
||||
*/
|
||||
export interface SpineEvents {
|
||||
complete: [trackEntry: TrackEntry];
|
||||
dispose: [trackEntry: TrackEntry];
|
||||
end: [trackEntry: TrackEntry];
|
||||
event: [trackEntry: TrackEntry, event: Event];
|
||||
interrupt: [trackEntry: TrackEntry];
|
||||
start: [trackEntry: TrackEntry];
|
||||
}
|
||||
|
||||
export interface AttachmentCacheData {
|
||||
id: string;
|
||||
clipped: boolean;
|
||||
vertices: Float32Array;
|
||||
uvs: Float32Array;
|
||||
indices: number[];
|
||||
color: Color;
|
||||
darkColor: Color;
|
||||
darkTint: boolean;
|
||||
skipRender: boolean;
|
||||
texture: Texture;
|
||||
clippedData?: {
|
||||
vertices: Float32Array;
|
||||
uvs: Float32Array;
|
||||
indices: Uint16Array;
|
||||
vertexCount: number;
|
||||
indicesCount: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface SlotsToClipping {
|
||||
slot: Slot,
|
||||
mask?: Graphics,
|
||||
maskComputed?: boolean,
|
||||
vertices: Array<number>,
|
||||
};
|
||||
|
||||
const maskPool = new Pool<Graphics>(() => new Graphics);
|
||||
|
||||
/**
|
||||
* The class to instantiate a {@link Spine} game object in Pixi.
|
||||
* The static method {@link Spine.from} should be used to instantiate a Spine game object.
|
||||
*/
|
||||
export class Spine extends ViewContainer {
|
||||
// Pixi properties
|
||||
public batched = true;
|
||||
public buildId = 0;
|
||||
public override readonly renderPipeId = 'spine';
|
||||
public _didSpineUpdate = false;
|
||||
|
||||
public beforeUpdateWorldTransforms: (object: Spine) => void = () => { /** */ };
|
||||
public afterUpdateWorldTransforms: (object: Spine) => void = () => { /** */ };
|
||||
|
||||
// Spine properties
|
||||
/** The skeleton for this Spine game object. */
|
||||
public skeleton: Skeleton;
|
||||
/** The animation state for this Spine game object. */
|
||||
public state: AnimationState;
|
||||
public skeletonBounds?: SkeletonBounds;
|
||||
|
||||
private darkTint = false;
|
||||
private _debug?: ISpineDebugRenderer | undefined = undefined;
|
||||
|
||||
readonly _slotsObject: Record<string, { slot: Slot, container: Container } | null> = Object.create(null);
|
||||
private clippingSlotToPixiMasks: Record<string, SlotsToClipping> = Object.create(null);
|
||||
|
||||
private getSlotFromRef (slotRef: number | string | Slot): Slot {
|
||||
let slot: Slot | null;
|
||||
|
||||
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}`);
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
public spineAttachmentsDirty = true;
|
||||
public spineTexturesDirty = true;
|
||||
|
||||
private _lastAttachments: Attachment[] = [];
|
||||
|
||||
private _stateChanged = true;
|
||||
private attachmentCacheData: Record<string, AttachmentCacheData>[] = [];
|
||||
|
||||
public get debug (): ISpineDebugRenderer | undefined {
|
||||
return this._debug;
|
||||
}
|
||||
|
||||
/** Pass a {@link SpineDebugRenderer} or create your own {@link ISpineDebugRenderer} to render bones, meshes, ...
|
||||
* @example spineGO.debug = new SpineDebugRenderer();
|
||||
*/
|
||||
public set debug (value: ISpineDebugRenderer | undefined) {
|
||||
if (this._debug) {
|
||||
this._debug.unregisterSpine(this);
|
||||
}
|
||||
if (value) {
|
||||
value.registerSpine(this);
|
||||
}
|
||||
this._debug = value;
|
||||
}
|
||||
|
||||
private autoUpdateWarned = false;
|
||||
private _autoUpdate = true;
|
||||
|
||||
public get autoUpdate (): boolean {
|
||||
return this._autoUpdate;
|
||||
}
|
||||
/** When `true`, the Spine AnimationState and the Skeleton will be automatically updated using the {@link Ticker.shared} instance. */
|
||||
public set autoUpdate (value: boolean) {
|
||||
if (value) {
|
||||
Ticker.shared.add(this.internalUpdate, this);
|
||||
this.autoUpdateWarned = false;
|
||||
}
|
||||
else {
|
||||
Ticker.shared.remove(this.internalUpdate, this);
|
||||
}
|
||||
|
||||
this._autoUpdate = value;
|
||||
}
|
||||
|
||||
constructor (options: SpineOptions | SkeletonData) {
|
||||
if (options instanceof SkeletonData) {
|
||||
options = {
|
||||
skeletonData: options,
|
||||
};
|
||||
}
|
||||
|
||||
super();
|
||||
|
||||
const skeletonData = options instanceof SkeletonData ? options : options.skeletonData;
|
||||
|
||||
this.skeleton = new Skeleton(skeletonData);
|
||||
this.state = new AnimationState(new AnimationStateData(skeletonData));
|
||||
this.autoUpdate = options?.autoUpdate ?? true;
|
||||
|
||||
// dark tint can be enabled by options, otherwise is enable if at least one slot has tint black
|
||||
this.darkTint = options?.darkTint === undefined
|
||||
? this.skeleton.slots.some(slot => !!slot.data.darkColor)
|
||||
: options?.darkTint;
|
||||
|
||||
const slots = this.skeleton.slots;
|
||||
|
||||
for (let i = 0; i < slots.length; i++) {
|
||||
this.attachmentCacheData[i] = Object.create(null);
|
||||
}
|
||||
|
||||
this._updateState(0);
|
||||
}
|
||||
|
||||
/** If {@link Spine.autoUpdate} is `false`, this method allows to update the AnimationState and the Skeleton with the given delta. */
|
||||
public update (dt: number): void {
|
||||
if (this.autoUpdate && !this.autoUpdateWarned) {
|
||||
console.warn('You are calling update on a Spine instance that has autoUpdate set to true. This is probably not what you want.');
|
||||
this.autoUpdateWarned = true;
|
||||
}
|
||||
|
||||
this.internalUpdate(0, dt);
|
||||
}
|
||||
|
||||
protected internalUpdate (_deltaFrame: any, deltaSeconds?: number): void {
|
||||
// Because reasons, pixi uses deltaFrames at 60fps.
|
||||
// We ignore the default deltaFrames and use the deltaSeconds from pixi ticker.
|
||||
this._updateState(deltaSeconds ?? Ticker.shared.deltaMS / 1000);
|
||||
}
|
||||
|
||||
get bounds () {
|
||||
if (this._boundsDirty) {
|
||||
this.updateBounds();
|
||||
}
|
||||
|
||||
return this._bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the position of the bone given in input through a {@link IPointData}.
|
||||
* @param bone: the bone name or the bone instance to set the position
|
||||
* @param outPos: the new position of the bone.
|
||||
* @throws {Error}: if the given bone is not found in the skeleton, an error is thrown
|
||||
*/
|
||||
public setBonePosition (bone: string | Bone, position: PointData): void {
|
||||
const boneAux = bone;
|
||||
|
||||
if (typeof bone === 'string') {
|
||||
bone = this.skeleton.findBone(bone) as Bone;
|
||||
}
|
||||
|
||||
if (!bone) throw Error(`Cant set bone position, bone ${String(boneAux)} not found`);
|
||||
vectorAux.set(position.x, position.y);
|
||||
|
||||
if (bone.parent) {
|
||||
const aux = bone.parent.worldToLocal(vectorAux);
|
||||
|
||||
bone.x = aux.x;
|
||||
bone.y = -aux.y;
|
||||
}
|
||||
else {
|
||||
bone.x = vectorAux.x;
|
||||
bone.y = vectorAux.y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the position of the bone given in input into an {@link IPointData}.
|
||||
* @param bone: the bone name or the bone instance to get the position from
|
||||
* @param outPos: an optional {@link IPointData} to use to return the bone position, rathern than instantiating a new object.
|
||||
* @returns {IPointData | undefined}: the position of the bone, or undefined if no matching bone is found in the skeleton
|
||||
*/
|
||||
public getBonePosition (bone: string | Bone, outPos?: PointData): PointData | undefined {
|
||||
const boneAux = bone;
|
||||
|
||||
if (typeof bone === 'string') {
|
||||
bone = this.skeleton.findBone(bone) as Bone;
|
||||
}
|
||||
|
||||
if (!bone) {
|
||||
console.error(`Cant set bone position! Bone ${String(boneAux)} not found`);
|
||||
|
||||
return outPos;
|
||||
}
|
||||
|
||||
if (!outPos) {
|
||||
outPos = { x: 0, y: 0 };
|
||||
}
|
||||
|
||||
outPos.x = bone.worldX;
|
||||
outPos.y = bone.worldY;
|
||||
|
||||
return outPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will update the state based on the specified time, this will not apply the state to the skeleton
|
||||
* as this is differed until the `applyState` method is called.
|
||||
*
|
||||
* @param time the time at which to set the state
|
||||
* @internal
|
||||
*/
|
||||
_updateState (time: number) {
|
||||
this.state.update(time);
|
||||
this.skeleton.update(time);
|
||||
|
||||
this._stateChanged = true;
|
||||
|
||||
this._boundsDirty = true;
|
||||
|
||||
this.onViewUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the state to this spine instance.
|
||||
* - updates the state to the skeleton
|
||||
* - updates its world transform (spine world transform)
|
||||
* - validates the attachments - to flag if the attachments have changed this state
|
||||
* - transforms the attachments - to update the vertices of the attachments based on the new positions
|
||||
* - update the slot attachments - to update the position, rotation, scale, and visibility of the attached containers
|
||||
* @internal
|
||||
*/
|
||||
_applyState () {
|
||||
if (!this._stateChanged) return;
|
||||
this._stateChanged = false;
|
||||
|
||||
const { skeleton } = this;
|
||||
|
||||
this.state.apply(skeleton);
|
||||
|
||||
this.beforeUpdateWorldTransforms(this);
|
||||
skeleton.updateWorldTransform(Physics.update);
|
||||
this.afterUpdateWorldTransforms(this);
|
||||
|
||||
this.validateAttachments();
|
||||
|
||||
this.transformAttachments();
|
||||
|
||||
this.updateSlotObjects();
|
||||
}
|
||||
|
||||
private validateAttachments () {
|
||||
const currentDrawOrder = this.skeleton.drawOrder;
|
||||
|
||||
const lastAttachments = this._lastAttachments;
|
||||
|
||||
let index = 0;
|
||||
|
||||
let spineAttachmentsDirty = false;
|
||||
|
||||
for (let i = 0; i < currentDrawOrder.length; i++) {
|
||||
const slot = currentDrawOrder[i];
|
||||
const attachment = slot.getAttachment();
|
||||
|
||||
if (attachment) {
|
||||
if (attachment !== lastAttachments[index]) {
|
||||
spineAttachmentsDirty = true;
|
||||
lastAttachments[index] = attachment;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (index !== lastAttachments.length) {
|
||||
spineAttachmentsDirty = true;
|
||||
lastAttachments.length = index;
|
||||
}
|
||||
|
||||
this.spineAttachmentsDirty = spineAttachmentsDirty;
|
||||
}
|
||||
|
||||
private updateAndSetPixiMask (slot: Slot, last: boolean) {
|
||||
// assign/create the currentClippingSlot
|
||||
const attachment = slot.attachment;
|
||||
if (attachment && attachment instanceof ClippingAttachment) {
|
||||
const clip = (this.clippingSlotToPixiMasks[slot.data.name] ||= { slot, vertices: new Array<number>() });
|
||||
clip.maskComputed = false;
|
||||
this.currentClippingSlot = this.clippingSlotToPixiMasks[slot.data.name];
|
||||
return;
|
||||
}
|
||||
|
||||
// assign the currentClippingSlot mask to the slot object
|
||||
let currentClippingSlot = this.currentClippingSlot;
|
||||
let slotObject = this._slotsObject[slot.data.name];
|
||||
if (currentClippingSlot && slotObject) {
|
||||
let slotClipping = currentClippingSlot.slot;
|
||||
let clippingAttachment = slotClipping.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;
|
||||
if (!mask) {
|
||||
mask = maskPool.obtain();
|
||||
currentClippingSlot.mask = mask;
|
||||
this.addChild(mask);
|
||||
}
|
||||
|
||||
// compute the pixi mask polygon, if the clipped slot is the first one clipped by this currentClippingSlot
|
||||
if (!currentClippingSlot.maskComputed) {
|
||||
currentClippingSlot.maskComputed = true;
|
||||
const worldVerticesLength = clippingAttachment.worldVerticesLength;
|
||||
const vertices = currentClippingSlot.vertices;
|
||||
clippingAttachment.computeWorldVertices(slotClipping, 0, worldVerticesLength, vertices, 0, 2);
|
||||
mask.clear().poly(vertices).stroke({ width: 0 }).fill({ alpha: .25 });
|
||||
}
|
||||
slotObject.container.mask = mask;
|
||||
} else if (slotObject?.container.mask) {
|
||||
// remove the mask, if slot object has a mask, but currentClippingSlot is undefined
|
||||
slotObject.container.mask = null;
|
||||
}
|
||||
|
||||
// if current slot is the ending one of the currentClippingSlot mask, set currentClippingSlot to undefined
|
||||
if (currentClippingSlot && (currentClippingSlot.slot.attachment as ClippingAttachment).endSlot == slot.data) {
|
||||
this.currentClippingSlot = undefined;
|
||||
}
|
||||
|
||||
// clean up unused masks
|
||||
if (last) {
|
||||
for (const key in this.clippingSlotToPixiMasks) {
|
||||
const clippingSlotToPixiMask = this.clippingSlotToPixiMasks[key];
|
||||
if ((!(clippingSlotToPixiMask.slot.attachment instanceof ClippingAttachment) || !clippingSlotToPixiMask.maskComputed) && clippingSlotToPixiMask.mask) {
|
||||
this.removeChild(clippingSlotToPixiMask.mask);
|
||||
maskPool.free(clippingSlotToPixiMask.mask);
|
||||
clippingSlotToPixiMask.mask = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private currentClippingSlot: SlotsToClipping | undefined;
|
||||
private transformAttachments () {
|
||||
const currentDrawOrder = this.skeleton.drawOrder;
|
||||
|
||||
for (let i = 0; i < currentDrawOrder.length; i++) {
|
||||
const slot = currentDrawOrder[i];
|
||||
|
||||
this.updateAndSetPixiMask(slot, i === currentDrawOrder.length - 1);
|
||||
|
||||
const attachment = slot.getAttachment();
|
||||
|
||||
if (attachment) {
|
||||
if (attachment instanceof MeshAttachment || attachment instanceof RegionAttachment) {
|
||||
const cacheData = this._getCachedData(slot, attachment);
|
||||
|
||||
if (attachment instanceof RegionAttachment) {
|
||||
attachment.computeWorldVertices(slot, cacheData.vertices, 0, 2);
|
||||
}
|
||||
else {
|
||||
attachment.computeWorldVertices(
|
||||
slot,
|
||||
0,
|
||||
attachment.worldVerticesLength,
|
||||
cacheData.vertices,
|
||||
0,
|
||||
2,
|
||||
);
|
||||
}
|
||||
|
||||
fastCopy((attachment.uvs as Float32Array).buffer, cacheData.uvs.buffer);
|
||||
|
||||
const skeleton = slot.bone.skeleton;
|
||||
const skeletonColor = skeleton.color;
|
||||
const slotColor = slot.color;
|
||||
|
||||
const attachmentColor = attachment.color;
|
||||
|
||||
cacheData.color.set(
|
||||
skeletonColor.r * slotColor.r * attachmentColor.r,
|
||||
skeletonColor.g * slotColor.g * attachmentColor.g,
|
||||
skeletonColor.b * slotColor.b * attachmentColor.b,
|
||||
skeletonColor.a * slotColor.a * attachmentColor.a,
|
||||
);
|
||||
|
||||
if (slot.darkColor) {
|
||||
cacheData.darkColor.setFromColor(slot.darkColor);
|
||||
}
|
||||
|
||||
cacheData.skipRender = cacheData.clipped = false;
|
||||
|
||||
const texture = attachment.region?.texture.texture || Texture.EMPTY;
|
||||
|
||||
if (cacheData.texture !== texture) {
|
||||
cacheData.texture = texture;
|
||||
this.spineTexturesDirty = true;
|
||||
}
|
||||
|
||||
if (clipper.isClipping()) {
|
||||
this.updateClippingData(cacheData);
|
||||
}
|
||||
}
|
||||
else if (attachment instanceof ClippingAttachment) {
|
||||
clipper.clipStart(slot, attachment);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
clipper.clipEndWithSlot(slot);
|
||||
}
|
||||
clipper.clipEnd();
|
||||
}
|
||||
|
||||
private updateClippingData (cacheData: AttachmentCacheData) {
|
||||
cacheData.clipped = true;
|
||||
|
||||
clipper.clipTrianglesUnpacked(
|
||||
cacheData.vertices,
|
||||
cacheData.indices,
|
||||
cacheData.indices.length,
|
||||
cacheData.uvs,
|
||||
);
|
||||
|
||||
const { clippedVertices, clippedUVs, clippedTriangles } = clipper;
|
||||
|
||||
const verticesCount = clippedVertices.length / 2;
|
||||
const indicesCount = clippedTriangles.length;
|
||||
|
||||
if (!cacheData.clippedData) {
|
||||
cacheData.clippedData = {
|
||||
vertices: new Float32Array(verticesCount * 2),
|
||||
uvs: new Float32Array(verticesCount * 2),
|
||||
vertexCount: verticesCount,
|
||||
indices: new Uint16Array(indicesCount),
|
||||
indicesCount,
|
||||
};
|
||||
|
||||
this.spineAttachmentsDirty = true;
|
||||
}
|
||||
|
||||
const clippedData = cacheData.clippedData;
|
||||
|
||||
const sizeChange = clippedData.vertexCount !== verticesCount || indicesCount !== clippedData.indicesCount;
|
||||
|
||||
cacheData.skipRender = verticesCount === 0;
|
||||
|
||||
if (sizeChange) {
|
||||
this.spineAttachmentsDirty = true;
|
||||
|
||||
if (clippedData.vertexCount < verticesCount) {
|
||||
// buffer reuse!
|
||||
clippedData.vertices = new Float32Array(verticesCount * 2);
|
||||
clippedData.uvs = new Float32Array(verticesCount * 2);
|
||||
}
|
||||
|
||||
if (clippedData.indices.length < indicesCount) {
|
||||
clippedData.indices = new Uint16Array(indicesCount);
|
||||
}
|
||||
}
|
||||
|
||||
const { vertices, uvs, indices } = clippedData;
|
||||
|
||||
for (let i = 0; i < verticesCount; i++) {
|
||||
vertices[i * 2] = clippedVertices[i * 2];
|
||||
vertices[(i * 2) + 1] = clippedVertices[(i * 2) + 1];
|
||||
|
||||
uvs[i * 2] = clippedUVs[(i * 2)];
|
||||
uvs[(i * 2) + 1] = clippedUVs[(i * 2) + 1];
|
||||
}
|
||||
|
||||
clippedData.vertexCount = verticesCount;
|
||||
|
||||
for (let i = 0; i < indicesCount; i++) {
|
||||
if (indices[i] !== clippedTriangles[i]) {
|
||||
this.spineAttachmentsDirty = true;
|
||||
indices[i] = clippedTriangles[i];
|
||||
}
|
||||
}
|
||||
|
||||
clippedData.indicesCount = indicesCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* ensure that attached containers map correctly to their slots
|
||||
* along with their position, rotation, scale, and visibility.
|
||||
*/
|
||||
private updateSlotObjects () {
|
||||
for (const i in this._slotsObject) {
|
||||
const slotAttachment = this._slotsObject[i];
|
||||
|
||||
if (!slotAttachment) continue;
|
||||
|
||||
this.updateSlotObject(slotAttachment);
|
||||
}
|
||||
}
|
||||
|
||||
private updateSlotObject (slotAttachment: { slot: Slot, container: Container }) {
|
||||
const { slot, container } = slotAttachment;
|
||||
|
||||
container.visible = this.skeleton.drawOrder.includes(slot);
|
||||
|
||||
if (container.visible) {
|
||||
const bone = slot.bone;
|
||||
|
||||
container.position.set(bone.worldX, bone.worldY);
|
||||
|
||||
container.scale.x = bone.getWorldScaleX();
|
||||
container.scale.y = bone.getWorldScaleY();
|
||||
|
||||
container.rotation = bone.getWorldRotationX() * DEG_TO_RAD;
|
||||
|
||||
container.alpha = this.skeleton.color.a * slot.color.a;
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getCachedData (slot: Slot, attachment: RegionAttachment | MeshAttachment): AttachmentCacheData {
|
||||
return this.attachmentCacheData[slot.data.index][attachment.name] || this.initCachedData(slot, attachment);
|
||||
}
|
||||
|
||||
private initCachedData (slot: Slot, attachment: RegionAttachment | MeshAttachment): AttachmentCacheData {
|
||||
let vertices: Float32Array;
|
||||
|
||||
if (attachment instanceof RegionAttachment) {
|
||||
vertices = new Float32Array(8);
|
||||
|
||||
this.attachmentCacheData[slot.data.index][attachment.name] = {
|
||||
id: `${slot.data.index}-${attachment.name}`,
|
||||
vertices,
|
||||
clipped: false,
|
||||
indices: [0, 1, 2, 0, 2, 3],
|
||||
uvs: new Float32Array(attachment.uvs.length),
|
||||
color: new Color(1, 1, 1, 1),
|
||||
darkColor: new Color(0, 0, 0, 0),
|
||||
darkTint: this.darkTint,
|
||||
skipRender: false,
|
||||
texture: attachment.region?.texture.texture,
|
||||
};
|
||||
}
|
||||
else {
|
||||
vertices = new Float32Array(attachment.worldVerticesLength);
|
||||
|
||||
this.attachmentCacheData[slot.data.index][attachment.name] = {
|
||||
id: `${slot.data.index}-${attachment.name}`,
|
||||
vertices,
|
||||
clipped: false,
|
||||
indices: attachment.triangles,
|
||||
uvs: new Float32Array(attachment.uvs.length),
|
||||
color: new Color(1, 1, 1, 1),
|
||||
darkColor: new Color(0, 0, 0, 0),
|
||||
darkTint: this.darkTint,
|
||||
skipRender: false,
|
||||
texture: attachment.region?.texture.texture,
|
||||
};
|
||||
}
|
||||
|
||||
return this.attachmentCacheData[slot.data.index][attachment.name];
|
||||
}
|
||||
|
||||
protected onViewUpdate () {
|
||||
// increment from the 12th bit!
|
||||
this._didChangeId += 1 << 12;
|
||||
|
||||
this._boundsDirty = true;
|
||||
|
||||
if (this.didViewUpdate) return;
|
||||
this.didViewUpdate = true;
|
||||
|
||||
const renderGroup = this.renderGroup || this.parentRenderGroup;
|
||||
|
||||
if (renderGroup) {
|
||||
renderGroup.onChildViewUpdate(this);
|
||||
}
|
||||
|
||||
this.debug?.renderDebug(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches a PixiJS container to a specified slot. This will map the world transform of the slots bone
|
||||
* to the attached container. A container can only be attached to one slot at a time.
|
||||
*
|
||||
* @param container - The container to attach to the slot
|
||||
* @param slotRef - The slot id or slot to attach to
|
||||
*/
|
||||
public addSlotObject (slot: number | string | Slot, container: Container) {
|
||||
slot = this.getSlotFromRef(slot);
|
||||
|
||||
// need to check in on the container too...
|
||||
for (const i in this._slotsObject) {
|
||||
if (this._slotsObject[i]?.container === container) {
|
||||
this.removeSlotObject(this._slotsObject[i].slot);
|
||||
}
|
||||
}
|
||||
|
||||
this.removeSlotObject(slot);
|
||||
|
||||
container.includeInBuild = false;
|
||||
|
||||
// TODO only add once??
|
||||
this.addChild(container);
|
||||
|
||||
const slotObject = { container, slot };
|
||||
this._slotsObject[slot.data.name] = slotObject;
|
||||
|
||||
this.updateSlotObject(slotObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a PixiJS container from the slot it is attached to.
|
||||
*
|
||||
* @param container - The container to detach from the slot
|
||||
* @param slotOrContainer - The container, slot id or slot to detach from
|
||||
*/
|
||||
public removeSlotObject (slotOrContainer: number | string | Slot | Container) {
|
||||
let containerToRemove: Container | undefined;
|
||||
|
||||
if (slotOrContainer instanceof Container) {
|
||||
for (const i in this._slotsObject) {
|
||||
if (this._slotsObject[i]?.container === slotOrContainer) {
|
||||
this._slotsObject[i] = null;
|
||||
|
||||
containerToRemove = slotOrContainer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const slot = this.getSlotFromRef(slotOrContainer);
|
||||
|
||||
containerToRemove = this._slotsObject[slot.data.name]?.container;
|
||||
this._slotsObject[slot.data.name] = null;
|
||||
}
|
||||
|
||||
if (containerToRemove) {
|
||||
this.removeChild(containerToRemove);
|
||||
|
||||
containerToRemove.includeInBuild = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a container attached to a slot, or undefined if no container is attached.
|
||||
*
|
||||
* @param slotRef - The slot id or slot to get the attachment from
|
||||
* @returns - The container attached to the slot
|
||||
*/
|
||||
public getSlotObject (slot: number | string | Slot) {
|
||||
slot = this.getSlotFromRef(slot);
|
||||
|
||||
return this._slotsObject[slot.data.name]?.container;
|
||||
}
|
||||
|
||||
private updateBounds () {
|
||||
this._boundsDirty = false;
|
||||
|
||||
this.skeletonBounds ||= new SkeletonBounds();
|
||||
|
||||
const skeletonBounds = this.skeletonBounds;
|
||||
|
||||
skeletonBounds.update(this.skeleton, true);
|
||||
|
||||
if (skeletonBounds.minX === Infinity) {
|
||||
this._applyState();
|
||||
|
||||
const drawOrder = this.skeleton.drawOrder;
|
||||
const bounds = this._bounds;
|
||||
|
||||
bounds.clear();
|
||||
|
||||
for (let i = 0; i < drawOrder.length; i++) {
|
||||
const slot = drawOrder[i];
|
||||
|
||||
const attachment = slot.getAttachment();
|
||||
|
||||
if (attachment && (attachment instanceof RegionAttachment || attachment instanceof MeshAttachment)) {
|
||||
const cacheData = this._getCachedData(slot, attachment);
|
||||
|
||||
bounds.addVertexData(cacheData.vertices, 0, cacheData.vertices.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this._bounds.minX = skeletonBounds.minX;
|
||||
this._bounds.minY = skeletonBounds.minY;
|
||||
this._bounds.maxX = skeletonBounds.maxX;
|
||||
this._bounds.maxY = skeletonBounds.maxY;
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
addBounds (bounds: Bounds) {
|
||||
bounds.addBounds(this.bounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys this sprite renderable and optionally its texture.
|
||||
* @param options - Options parameter. A boolean will act as if all options
|
||||
* have been set to that value
|
||||
* @param {boolean} [options.texture=false] - Should it destroy the current texture of the renderable as well
|
||||
* @param {boolean} [options.textureSource=false] - Should it destroy the textureSource of the renderable as well
|
||||
*/
|
||||
public override destroy (options: DestroyOptions = false) {
|
||||
super.destroy(options);
|
||||
|
||||
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._lastAttachments.length = 0;
|
||||
this.attachmentCacheData = null as any;
|
||||
}
|
||||
|
||||
/** Converts a point from the skeleton coordinate system to the Pixi world coordinate system. */
|
||||
public skeletonToPixiWorldCoordinates (point: { x: number; y: number }) {
|
||||
this.worldTransform.apply(point, point);
|
||||
}
|
||||
|
||||
/** Converts a point from the Pixi world coordinate system to the skeleton coordinate system. */
|
||||
public pixiWorldCoordinatesToSkeleton (point: { x: number; y: number }) {
|
||||
this.worldTransform.applyInverse(point, point);
|
||||
}
|
||||
|
||||
/** Converts a point from the Pixi world coordinate system to the bone's local coordinate system. */
|
||||
public pixiWorldCoordinatesToBone (point: { x: number; y: number }, bone: Bone) {
|
||||
this.pixiWorldCoordinatesToSkeleton(point);
|
||||
if (bone.parent) {
|
||||
bone.parent.worldToLocal(point as Vector2);
|
||||
}
|
||||
else {
|
||||
bone.worldToLocal(point as Vector2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to instantiate a Spine game object.
|
||||
* Before instantiating a Spine game object, the skeleton (`.skel` or `.json`) and the atlas text files must be loaded into the Assets. For example:
|
||||
* ```
|
||||
* PIXI.Assets.add("sackData", "./assets/sack-pro.skel");
|
||||
* PIXI.Assets.add("sackAtlas", "./assets/sack-pma.atlas");
|
||||
* await PIXI.Assets.load(["sackData", "sackAtlas"]);
|
||||
* ```
|
||||
* Once a Spine game object is created, its skeleton data is cached into {@link Cache} using the key:
|
||||
* `${skeletonAssetName}-${atlasAssetName}-${options?.scale ?? 1}`
|
||||
*
|
||||
* @param options - Options to configure the Spine game object. See {@link SpineFromOptions}
|
||||
* @returns {Spine} The Spine game object instantiated
|
||||
*/
|
||||
static from ({ skeleton, atlas, scale = 1, darkTint, autoUpdate = true }: SpineFromOptions) {
|
||||
const cacheKey = `${skeleton}-${atlas}-${scale}`;
|
||||
|
||||
if (Cache.has(cacheKey)) {
|
||||
return new Spine(Cache.get<SkeletonData>(cacheKey));
|
||||
}
|
||||
|
||||
const skeletonAsset = Assets.get<any | Uint8Array>(skeleton);
|
||||
|
||||
const atlasAsset = Assets.get<TextureAtlas>(atlas);
|
||||
const attachmentLoader = new AtlasAttachmentLoader(atlasAsset);
|
||||
const parser = skeletonAsset instanceof Uint8Array
|
||||
? new SkeletonBinary(attachmentLoader)
|
||||
: new SkeletonJson(attachmentLoader);
|
||||
|
||||
parser.scale = scale;
|
||||
const skeletonData = parser.readSkeletonData(skeletonAsset);
|
||||
|
||||
Cache.set(cacheKey, skeletonData);
|
||||
|
||||
return new Spine({
|
||||
skeletonData,
|
||||
darkTint,
|
||||
autoUpdate,
|
||||
});
|
||||
}
|
||||
}
|
||||
615
spine-ts/spine-pixi-v8/src/SpineDebugRenderer.ts
Normal file
@ -0,0 +1,615 @@
|
||||
/** ****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Container, Graphics, Text } from 'pixi.js';
|
||||
import { Spine } from './Spine';
|
||||
import {
|
||||
ClippingAttachment,
|
||||
MeshAttachment,
|
||||
PathAttachment,
|
||||
RegionAttachment,
|
||||
SkeletonBounds
|
||||
} from '@esotericsoftware/spine-core';
|
||||
|
||||
import type { AnimationStateListener } from '@esotericsoftware/spine-core';
|
||||
|
||||
/**
|
||||
* Make a class that extends from this interface to create your own debug renderer.
|
||||
* @public
|
||||
*/
|
||||
export interface ISpineDebugRenderer {
|
||||
/**
|
||||
* This will be called every frame, after the spine has been updated.
|
||||
*/
|
||||
renderDebug: (spine: Spine) => void;
|
||||
|
||||
/**
|
||||
* This is called when the `spine.debug` object is set to null or when the spine is destroyed.
|
||||
*/
|
||||
unregisterSpine: (spine: Spine) => void;
|
||||
|
||||
/**
|
||||
* This is called when the `spine.debug` object is set to a new instance of a debug renderer.
|
||||
*/
|
||||
registerSpine: (spine: Spine) => void;
|
||||
}
|
||||
|
||||
type DebugDisplayObjects = {
|
||||
bones: Container;
|
||||
skeletonXY: Graphics;
|
||||
regionAttachmentsShape: Graphics;
|
||||
meshTrianglesLine: Graphics;
|
||||
meshHullLine: Graphics;
|
||||
clippingPolygon: Graphics;
|
||||
boundingBoxesRect: Graphics;
|
||||
boundingBoxesCircle: Graphics;
|
||||
boundingBoxesPolygon: Graphics;
|
||||
pathsCurve: Graphics;
|
||||
pathsLine: Graphics;
|
||||
parentDebugContainer: Container;
|
||||
eventText: Container;
|
||||
eventCallback: AnimationStateListener;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a debug renderer that uses PixiJS Graphics under the hood.
|
||||
* @public
|
||||
*/
|
||||
export class SpineDebugRenderer implements ISpineDebugRenderer {
|
||||
private readonly registeredSpines: Map<Spine, DebugDisplayObjects> = new Map();
|
||||
|
||||
public drawMeshHull = true;
|
||||
public drawMeshTriangles = true;
|
||||
public drawBones = true;
|
||||
public drawPaths = true;
|
||||
public drawBoundingBoxes = true;
|
||||
public drawClipping = true;
|
||||
public drawRegionAttachments = true;
|
||||
public drawEvents = true;
|
||||
|
||||
public lineWidth = 1;
|
||||
public regionAttachmentsColor = 0x0078ff;
|
||||
public meshHullColor = 0x0078ff;
|
||||
public meshTrianglesColor = 0xffcc00;
|
||||
public clippingPolygonColor = 0xff00ff;
|
||||
public boundingBoxesRectColor = 0x00ff00;
|
||||
public boundingBoxesPolygonColor = 0x00ff00;
|
||||
public boundingBoxesCircleColor = 0x00ff00;
|
||||
public pathsCurveColor = 0xff0000;
|
||||
public pathsLineColor = 0xff00ff;
|
||||
public skeletonXYColor = 0xff0000;
|
||||
public bonesColor = 0x00eecc;
|
||||
public eventFontSize = 24;
|
||||
public eventFontColor = 0x0;
|
||||
|
||||
/**
|
||||
* The debug is attached by force to each spine object.
|
||||
* So we need to create it inside the spine when we get the first update
|
||||
*/
|
||||
public registerSpine (spine: Spine): void {
|
||||
if (this.registeredSpines.has(spine)) {
|
||||
console.warn('SpineDebugRenderer.registerSpine() - this spine is already registered!', spine);
|
||||
|
||||
return;
|
||||
}
|
||||
const debugDisplayObjects: DebugDisplayObjects = {
|
||||
parentDebugContainer: new Container(),
|
||||
bones: new Container(),
|
||||
skeletonXY: new Graphics(),
|
||||
regionAttachmentsShape: new Graphics(),
|
||||
meshTrianglesLine: new Graphics(),
|
||||
meshHullLine: new Graphics(),
|
||||
clippingPolygon: new Graphics(),
|
||||
boundingBoxesRect: new Graphics(),
|
||||
boundingBoxesCircle: new Graphics(),
|
||||
boundingBoxesPolygon: new Graphics(),
|
||||
pathsCurve: new Graphics(),
|
||||
pathsLine: new Graphics(),
|
||||
eventText: new Container(),
|
||||
eventCallback: {
|
||||
event: (_, event) => {
|
||||
if (this.drawEvents) {
|
||||
const scale = Math.abs(spine.scale.x || spine.scale.y || 1);
|
||||
const text = new Text({
|
||||
text: event.data.name,
|
||||
style: {
|
||||
fontSize: this.eventFontSize / scale,
|
||||
fill: this.eventFontColor,
|
||||
fontFamily: 'monospace'
|
||||
}
|
||||
});
|
||||
|
||||
text.scale.x = Math.sign(spine.scale.x);
|
||||
text.anchor.set(0.5);
|
||||
debugDisplayObjects.eventText.addChild(text);
|
||||
setTimeout(() => {
|
||||
if (!text.destroyed) {
|
||||
text.destroy();
|
||||
}
|
||||
}, 250);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.bones);
|
||||
debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.skeletonXY);
|
||||
debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.regionAttachmentsShape);
|
||||
debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.meshTrianglesLine);
|
||||
debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.meshHullLine);
|
||||
debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.clippingPolygon);
|
||||
debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.boundingBoxesRect);
|
||||
debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.boundingBoxesCircle);
|
||||
debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.boundingBoxesPolygon);
|
||||
debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.pathsCurve);
|
||||
debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.pathsLine);
|
||||
debugDisplayObjects.parentDebugContainer.addChild(debugDisplayObjects.eventText);
|
||||
|
||||
(debugDisplayObjects.parentDebugContainer as any).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;
|
||||
|
||||
spine.addChild(debugDisplayObjects.parentDebugContainer);
|
||||
|
||||
spine.state.addListener(debugDisplayObjects.eventCallback);
|
||||
|
||||
this.registeredSpines.set(spine, debugDisplayObjects);
|
||||
}
|
||||
|
||||
public renderDebug (spine: Spine): void {
|
||||
if (!this.registeredSpines.has(spine)) {
|
||||
// This should never happen. Spines are registered when you assign spine.debug
|
||||
this.registerSpine(spine);
|
||||
}
|
||||
|
||||
const debugDisplayObjects = this.registeredSpines.get(spine);
|
||||
|
||||
if (!debugDisplayObjects) {
|
||||
return;
|
||||
}
|
||||
spine.addChild(debugDisplayObjects.parentDebugContainer);
|
||||
|
||||
debugDisplayObjects.skeletonXY.clear();
|
||||
debugDisplayObjects.regionAttachmentsShape.clear();
|
||||
debugDisplayObjects.meshTrianglesLine.clear();
|
||||
debugDisplayObjects.meshHullLine.clear();
|
||||
debugDisplayObjects.clippingPolygon.clear();
|
||||
debugDisplayObjects.boundingBoxesRect.clear();
|
||||
debugDisplayObjects.boundingBoxesCircle.clear();
|
||||
debugDisplayObjects.boundingBoxesPolygon.clear();
|
||||
debugDisplayObjects.pathsCurve.clear();
|
||||
debugDisplayObjects.pathsLine.clear();
|
||||
|
||||
for (let len = debugDisplayObjects.bones.children.length; len > 0; len--) {
|
||||
debugDisplayObjects.bones.children[len - 1].destroy({ children: true, texture: true, textureSource: true });
|
||||
}
|
||||
|
||||
const scale = Math.abs(spine.scale.x || spine.scale.y || 1);
|
||||
const lineWidth = this.lineWidth / scale;
|
||||
|
||||
if (this.drawBones) {
|
||||
this.drawBonesFunc(spine, debugDisplayObjects, lineWidth, scale);
|
||||
}
|
||||
|
||||
if (this.drawPaths) {
|
||||
this.drawPathsFunc(spine, debugDisplayObjects, lineWidth);
|
||||
}
|
||||
|
||||
if (this.drawBoundingBoxes) {
|
||||
this.drawBoundingBoxesFunc(spine, debugDisplayObjects, lineWidth);
|
||||
}
|
||||
|
||||
if (this.drawClipping) {
|
||||
this.drawClippingFunc(spine, debugDisplayObjects, lineWidth);
|
||||
}
|
||||
|
||||
if (this.drawMeshHull || this.drawMeshTriangles) {
|
||||
this.drawMeshHullAndMeshTriangles(spine, debugDisplayObjects, lineWidth);
|
||||
}
|
||||
|
||||
if (this.drawRegionAttachments) {
|
||||
this.drawRegionAttachmentsFunc(spine, debugDisplayObjects, lineWidth);
|
||||
}
|
||||
|
||||
if (this.drawEvents) {
|
||||
for (const child of debugDisplayObjects.eventText.children) {
|
||||
child.alpha -= 0.05;
|
||||
child.y -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private drawBonesFunc (spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number, scale: number): void {
|
||||
const skeleton = spine.skeleton;
|
||||
const skeletonX = skeleton.x;
|
||||
const skeletonY = skeleton.y;
|
||||
const bones = skeleton.bones;
|
||||
|
||||
debugDisplayObjects.skeletonXY.strokeStyle = { width: lineWidth, color: this.skeletonXYColor };
|
||||
|
||||
for (let i = 0, len = bones.length; i < len; i++) {
|
||||
const bone = bones[i];
|
||||
const boneLen = bone.data.length;
|
||||
const starX = skeletonX + bone.worldX;
|
||||
const starY = skeletonY + bone.worldY;
|
||||
const endX = skeletonX + (boneLen * bone.a) + bone.worldX;
|
||||
const endY = skeletonY + (boneLen * bone.b) + bone.worldY;
|
||||
|
||||
if (bone.data.name === 'root' || bone.data.parent === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const w = Math.abs(starX - endX);
|
||||
const h = Math.abs(starY - endY);
|
||||
// a = w, // side length a
|
||||
const a2 = Math.pow(w, 2); // square root of side length a
|
||||
const b = h; // side length b
|
||||
const b2 = Math.pow(h, 2); // square root of side length b
|
||||
const c = Math.sqrt(a2 + b2); // side length c
|
||||
const c2 = Math.pow(c, 2); // square root of side length c
|
||||
const rad = Math.PI / 180;
|
||||
// A = Math.acos([a2 + c2 - b2] / [2 * a * c]) || 0, // Angle A
|
||||
// C = Math.acos([a2 + b2 - c2] / [2 * a * b]) || 0, // C angle
|
||||
const B = Math.acos((c2 + b2 - a2) / (2 * b * c)) || 0; // angle of corner B
|
||||
|
||||
if (c === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const gp = new Graphics();
|
||||
|
||||
debugDisplayObjects.bones.addChild(gp);
|
||||
|
||||
// draw bone
|
||||
const refRation = c / 50 / scale;
|
||||
|
||||
gp.context
|
||||
.poly([0, 0, 0 - refRation, c - (refRation * 3), 0, c - refRation, 0 + refRation, c - (refRation * 3)])
|
||||
.fill(this.bonesColor);
|
||||
gp.x = starX;
|
||||
gp.y = starY;
|
||||
gp.pivot.y = c;
|
||||
|
||||
// Calculate bone rotation angle
|
||||
let rotation = 0;
|
||||
|
||||
if (starX < endX && starY < endY) {
|
||||
// bottom right
|
||||
rotation = -B + (180 * rad);
|
||||
}
|
||||
else if (starX > endX && starY < endY) {
|
||||
// bottom left
|
||||
rotation = (180 * rad) + B;
|
||||
}
|
||||
else if (starX > endX && starY > endY) {
|
||||
// top left
|
||||
rotation = -B;
|
||||
}
|
||||
else if (starX < endX && starY > endY) {
|
||||
// bottom left
|
||||
rotation = B;
|
||||
}
|
||||
else if (starY === endY && starX < endX) {
|
||||
// To the right
|
||||
rotation = 90 * rad;
|
||||
}
|
||||
else if (starY === endY && starX > endX) {
|
||||
// go left
|
||||
rotation = -90 * rad;
|
||||
}
|
||||
else if (starX === endX && starY < endY) {
|
||||
// down
|
||||
rotation = 180 * rad;
|
||||
}
|
||||
else if (starX === endX && starY > endY) {
|
||||
// up
|
||||
rotation = 0;
|
||||
}
|
||||
gp.rotation = rotation;
|
||||
|
||||
// Draw the starting rotation point of the bone
|
||||
gp.circle(0, c, refRation * 1.2)
|
||||
.fill({ color: 0x000000, alpha: 0.6 })
|
||||
.stroke({ width: lineWidth + refRation / 2.4, color: this.bonesColor });
|
||||
}
|
||||
|
||||
// Draw the skeleton starting point "X" form
|
||||
const startDotSize = lineWidth * 3;
|
||||
|
||||
debugDisplayObjects.skeletonXY.context
|
||||
.moveTo(skeletonX - startDotSize, skeletonY - startDotSize)
|
||||
.lineTo(skeletonX + startDotSize, skeletonY + startDotSize)
|
||||
.moveTo(skeletonX + startDotSize, skeletonY - startDotSize)
|
||||
.lineTo(skeletonX - startDotSize, skeletonY + startDotSize)
|
||||
.stroke();
|
||||
}
|
||||
|
||||
private drawRegionAttachmentsFunc (spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void {
|
||||
const skeleton = spine.skeleton;
|
||||
const slots = skeleton.slots;
|
||||
|
||||
for (let i = 0, len = slots.length; i < len; i++) {
|
||||
const slot = slots[i];
|
||||
const attachment = slot.getAttachment();
|
||||
|
||||
if (attachment === null || !(attachment instanceof RegionAttachment)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const regionAttachment = attachment;
|
||||
|
||||
const vertices = new Float32Array(8);
|
||||
|
||||
regionAttachment.computeWorldVertices(slot, vertices, 0, 2);
|
||||
|
||||
debugDisplayObjects.regionAttachmentsShape.poly(Array.from(vertices.slice(0, 8)));
|
||||
}
|
||||
|
||||
debugDisplayObjects.regionAttachmentsShape.stroke({
|
||||
color: this.regionAttachmentsColor,
|
||||
width: lineWidth
|
||||
});
|
||||
}
|
||||
|
||||
private drawMeshHullAndMeshTriangles (spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void {
|
||||
const skeleton = spine.skeleton;
|
||||
const slots = skeleton.slots;
|
||||
|
||||
for (let i = 0, len = slots.length; i < len; i++) {
|
||||
const slot = slots[i];
|
||||
|
||||
if (!slot.bone.active) {
|
||||
continue;
|
||||
}
|
||||
const attachment = slot.getAttachment();
|
||||
|
||||
if (attachment === null || !(attachment instanceof MeshAttachment)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const meshAttachment = attachment;
|
||||
|
||||
const vertices = new Float32Array(meshAttachment.worldVerticesLength);
|
||||
const triangles = meshAttachment.triangles;
|
||||
let hullLength = meshAttachment.hullLength;
|
||||
|
||||
meshAttachment.computeWorldVertices(slot, 0, meshAttachment.worldVerticesLength, vertices, 0, 2);
|
||||
// draw the skinned mesh (triangle)
|
||||
if (this.drawMeshTriangles) {
|
||||
for (let i = 0, len = triangles.length; i < len; i += 3) {
|
||||
const v1 = triangles[i] * 2;
|
||||
const v2 = triangles[i + 1] * 2;
|
||||
const v3 = triangles[i + 2] * 2;
|
||||
|
||||
debugDisplayObjects.meshTrianglesLine.context
|
||||
.moveTo(vertices[v1], vertices[v1 + 1])
|
||||
.lineTo(vertices[v2], vertices[v2 + 1])
|
||||
.lineTo(vertices[v3], vertices[v3 + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
// draw skin border
|
||||
if (this.drawMeshHull && hullLength > 0) {
|
||||
hullLength = (hullLength >> 1) * 2;
|
||||
let lastX = vertices[hullLength - 2];
|
||||
let lastY = vertices[hullLength - 1];
|
||||
|
||||
for (let i = 0, len = hullLength; i < len; i += 2) {
|
||||
const x = vertices[i];
|
||||
const y = vertices[i + 1];
|
||||
|
||||
debugDisplayObjects.meshHullLine.context
|
||||
.moveTo(x, y)
|
||||
.lineTo(lastX, lastY);
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debugDisplayObjects.meshHullLine.stroke({ width: lineWidth, color: this.meshHullColor });
|
||||
debugDisplayObjects.meshTrianglesLine.stroke({ width: lineWidth, color: this.meshTrianglesColor });
|
||||
}
|
||||
|
||||
drawClippingFunc (spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void {
|
||||
const skeleton = spine.skeleton;
|
||||
const slots = skeleton.slots;
|
||||
|
||||
for (let i = 0, len = slots.length; i < len; i++) {
|
||||
const slot = slots[i];
|
||||
|
||||
if (!slot.bone.active) {
|
||||
continue;
|
||||
}
|
||||
const attachment = slot.getAttachment();
|
||||
|
||||
if (attachment === null || !(attachment instanceof ClippingAttachment)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const clippingAttachment = attachment;
|
||||
|
||||
const nn = clippingAttachment.worldVerticesLength;
|
||||
const world = new Float32Array(nn);
|
||||
|
||||
clippingAttachment.computeWorldVertices(slot, 0, nn, world, 0, 2);
|
||||
debugDisplayObjects.clippingPolygon.poly(Array.from(world));
|
||||
}
|
||||
|
||||
debugDisplayObjects.clippingPolygon.stroke({
|
||||
width: lineWidth, color: this.clippingPolygonColor, alpha: 1
|
||||
});
|
||||
}
|
||||
|
||||
drawBoundingBoxesFunc (spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void {
|
||||
// draw the total outline of the bounding box
|
||||
const bounds = new SkeletonBounds();
|
||||
bounds.update(spine.skeleton, true);
|
||||
|
||||
if (bounds.minX !== Infinity) {
|
||||
debugDisplayObjects.boundingBoxesRect
|
||||
.rect(bounds.minX, bounds.minY, bounds.getWidth(), bounds.getHeight())
|
||||
.stroke({ width: lineWidth, color: this.boundingBoxesRectColor });
|
||||
}
|
||||
|
||||
const polygons = bounds.polygons;
|
||||
const drawPolygon = (polygonVertices: ArrayLike<number>, _offset: unknown, count: number): void => {
|
||||
if (count < 3) {
|
||||
throw new Error('Polygon must contain at least 3 vertices');
|
||||
}
|
||||
const paths: number[] = [];
|
||||
const dotSize = lineWidth * 2;
|
||||
|
||||
for (let i = 0, len = polygonVertices.length; i < len; i += 2) {
|
||||
const x1 = polygonVertices[i];
|
||||
const y1 = polygonVertices[i + 1];
|
||||
|
||||
// draw the bounding box node
|
||||
debugDisplayObjects.boundingBoxesCircle.beginFill(this.boundingBoxesCircleColor);
|
||||
debugDisplayObjects.boundingBoxesCircle.drawCircle(x1, y1, dotSize);
|
||||
debugDisplayObjects.boundingBoxesCircle.fill(0);
|
||||
|
||||
debugDisplayObjects.boundingBoxesCircle
|
||||
.circle(x1, y1, dotSize)
|
||||
.fill({ color: this.boundingBoxesCircleColor })
|
||||
|
||||
paths.push(x1, y1);
|
||||
}
|
||||
|
||||
// draw the bounding box area
|
||||
debugDisplayObjects.boundingBoxesPolygon
|
||||
.poly(paths)
|
||||
.fill({
|
||||
color: this.boundingBoxesPolygonColor,
|
||||
alpha: 0.1
|
||||
})
|
||||
.stroke({
|
||||
width: lineWidth,
|
||||
color: this.boundingBoxesPolygonColor
|
||||
});
|
||||
};
|
||||
|
||||
for (let i = 0, len = polygons.length; i < len; i++) {
|
||||
const polygon = polygons[i];
|
||||
|
||||
drawPolygon(polygon, 0, polygon.length);
|
||||
}
|
||||
}
|
||||
|
||||
private drawPathsFunc (spine: Spine, debugDisplayObjects: DebugDisplayObjects, lineWidth: number): void {
|
||||
const skeleton = spine.skeleton;
|
||||
const slots = skeleton.slots;
|
||||
|
||||
for (let i = 0, len = slots.length; i < len; i++) {
|
||||
const slot = slots[i];
|
||||
|
||||
if (!slot.bone.active) {
|
||||
continue;
|
||||
}
|
||||
const attachment = slot.getAttachment();
|
||||
|
||||
if (attachment === null || !(attachment instanceof PathAttachment)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const pathAttachment = attachment;
|
||||
let nn = pathAttachment.worldVerticesLength;
|
||||
const world = new Float32Array(nn);
|
||||
|
||||
pathAttachment.computeWorldVertices(slot, 0, nn, world, 0, 2);
|
||||
let x1 = world[2];
|
||||
let y1 = world[3];
|
||||
let x2 = 0;
|
||||
let y2 = 0;
|
||||
|
||||
if (pathAttachment.closed) {
|
||||
const cx1 = world[0];
|
||||
const cy1 = world[1];
|
||||
const cx2 = world[nn - 2];
|
||||
const cy2 = world[nn - 1];
|
||||
|
||||
x2 = world[nn - 4];
|
||||
y2 = world[nn - 3];
|
||||
|
||||
// curve
|
||||
debugDisplayObjects.pathsCurve.moveTo(x1, y1);
|
||||
debugDisplayObjects.pathsCurve.bezierCurveTo(cx1, cy1, cx2, cy2, x2, y2);
|
||||
|
||||
// handle
|
||||
debugDisplayObjects.pathsLine.moveTo(x1, y1);
|
||||
debugDisplayObjects.pathsLine.lineTo(cx1, cy1);
|
||||
debugDisplayObjects.pathsLine.moveTo(x2, y2);
|
||||
debugDisplayObjects.pathsLine.lineTo(cx2, cy2);
|
||||
}
|
||||
nn -= 4;
|
||||
for (let ii = 4; ii < nn; ii += 6) {
|
||||
const cx1 = world[ii];
|
||||
const cy1 = world[ii + 1];
|
||||
const cx2 = world[ii + 2];
|
||||
const cy2 = world[ii + 3];
|
||||
|
||||
x2 = world[ii + 4];
|
||||
y2 = world[ii + 5];
|
||||
// curve
|
||||
debugDisplayObjects.pathsCurve.moveTo(x1, y1);
|
||||
debugDisplayObjects.pathsCurve.bezierCurveTo(cx1, cy1, cx2, cy2, x2, y2);
|
||||
|
||||
// handle
|
||||
debugDisplayObjects.pathsLine.moveTo(x1, y1);
|
||||
debugDisplayObjects.pathsLine.lineTo(cx1, cy1);
|
||||
debugDisplayObjects.pathsLine.moveTo(x2, y2);
|
||||
debugDisplayObjects.pathsLine.lineTo(cx2, cy2);
|
||||
x1 = x2;
|
||||
y1 = y2;
|
||||
}
|
||||
}
|
||||
|
||||
debugDisplayObjects.pathsCurve.stroke({ width: lineWidth, color: this.pathsCurveColor });
|
||||
debugDisplayObjects.pathsLine.stroke({ width: lineWidth, color: this.pathsLineColor });
|
||||
}
|
||||
|
||||
public unregisterSpine (spine: Spine): void {
|
||||
if (!this.registeredSpines.has(spine)) {
|
||||
console.warn('SpineDebugRenderer.unregisterSpine() - spine is not registered, can\'t unregister!', spine);
|
||||
}
|
||||
const debugDisplayObjects = this.registeredSpines.get(spine);
|
||||
|
||||
if (!debugDisplayObjects) {
|
||||
return;
|
||||
}
|
||||
|
||||
spine.state.removeListener(debugDisplayObjects.eventCallback);
|
||||
|
||||
debugDisplayObjects.parentDebugContainer.destroy({ textureSource: true, children: true, texture: true });
|
||||
this.registeredSpines.delete(spine);
|
||||
}
|
||||
}
|
||||
182
spine-ts/spine-pixi-v8/src/SpinePipe.ts
Normal file
@ -0,0 +1,182 @@
|
||||
/** ****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import {
|
||||
collectAllRenderables,
|
||||
extensions, ExtensionType,
|
||||
InstructionSet,
|
||||
type Renderer,
|
||||
type RenderPipe,
|
||||
} from 'pixi.js';
|
||||
import { BatchableSpineSlot } from './BatchableSpineSlot';
|
||||
import { Spine } from './Spine';
|
||||
import { MeshAttachment, RegionAttachment, SkeletonClipping } from '@esotericsoftware/spine-core';
|
||||
|
||||
const spineBlendModeMap = {
|
||||
0: 'normal',
|
||||
1: 'add',
|
||||
2: 'multiply',
|
||||
3: 'screen'
|
||||
};
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
export class SpinePipe implements RenderPipe<Spine> {
|
||||
/** @ignore */
|
||||
static extension = {
|
||||
type: [
|
||||
ExtensionType.WebGLPipes,
|
||||
ExtensionType.WebGPUPipes,
|
||||
ExtensionType.CanvasPipes,
|
||||
],
|
||||
name: 'spine',
|
||||
} as const;
|
||||
|
||||
renderer: Renderer;
|
||||
|
||||
private gpuSpineData: Record<string, any> = {};
|
||||
|
||||
constructor (renderer: Renderer) {
|
||||
this.renderer = renderer;
|
||||
}
|
||||
|
||||
validateRenderable (spine: Spine): boolean {
|
||||
spine._applyState();
|
||||
|
||||
// if pine attachments have changed, we need to rebuild the batch!
|
||||
if (spine.spineAttachmentsDirty) {
|
||||
return true;
|
||||
}
|
||||
// if the textures have changed, we need to rebuild the batch, but only if the texture is not already in the batch
|
||||
else if (spine.spineTexturesDirty) {
|
||||
// loop through and see if the textures have changed..
|
||||
const drawOrder = spine.skeleton.drawOrder;
|
||||
const gpuSpine = this.gpuSpineData[spine.uid];
|
||||
|
||||
for (let i = 0, n = drawOrder.length; i < n; i++) {
|
||||
const slot = drawOrder[i];
|
||||
const attachment = slot.getAttachment();
|
||||
|
||||
if (attachment instanceof RegionAttachment || attachment instanceof MeshAttachment) {
|
||||
const cacheData = spine._getCachedData(slot, attachment);
|
||||
const batchableSpineSlot = gpuSpine.slotBatches[cacheData.id];
|
||||
|
||||
const texture = cacheData.texture;
|
||||
|
||||
if (texture !== batchableSpineSlot.texture) {
|
||||
if (!batchableSpineSlot._batcher.checkAndUpdateTexture(batchableSpineSlot, texture)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
addRenderable (spine: Spine, instructionSet: InstructionSet) {
|
||||
const gpuSpine = this.gpuSpineData[spine.uid] ||= { slotBatches: {} };
|
||||
|
||||
const batcher = this.renderer.renderPipes.batch;
|
||||
|
||||
const drawOrder = spine.skeleton.drawOrder;
|
||||
|
||||
const roundPixels = (this.renderer._roundPixels | spine._roundPixels) as 0 | 1;
|
||||
|
||||
spine._applyState();
|
||||
|
||||
for (let i = 0, n = drawOrder.length; i < n; i++) {
|
||||
const slot = drawOrder[i];
|
||||
const attachment = slot.getAttachment();
|
||||
const blendMode = spineBlendModeMap[slot.data.blendMode];
|
||||
|
||||
if (attachment instanceof RegionAttachment || attachment instanceof MeshAttachment) {
|
||||
const cacheData = spine._getCachedData(slot, attachment);
|
||||
const batchableSpineSlot = gpuSpine.slotBatches[cacheData.id] ||= new BatchableSpineSlot();
|
||||
|
||||
batchableSpineSlot.setData(
|
||||
spine,
|
||||
cacheData,
|
||||
blendMode,
|
||||
roundPixels
|
||||
);
|
||||
|
||||
if (!cacheData.skipRender) {
|
||||
batcher.addToBatch(batchableSpineSlot, instructionSet);
|
||||
}
|
||||
}
|
||||
|
||||
const containerAttachment = spine._slotsObject[slot.data.name];
|
||||
|
||||
if (containerAttachment) {
|
||||
const container = containerAttachment.container;
|
||||
|
||||
container.includeInBuild = true;
|
||||
collectAllRenderables(container, instructionSet, this.renderer);
|
||||
container.includeInBuild = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateRenderable (spine: Spine) {
|
||||
// we assume that spine will always change its verts size..
|
||||
const gpuSpine = this.gpuSpineData[spine.uid];
|
||||
|
||||
spine._applyState();
|
||||
|
||||
const drawOrder = spine.skeleton.drawOrder;
|
||||
|
||||
for (let i = 0, n = drawOrder.length; i < n; i++) {
|
||||
const slot = drawOrder[i];
|
||||
const attachment = slot.getAttachment();
|
||||
|
||||
if (attachment instanceof RegionAttachment || attachment instanceof MeshAttachment) {
|
||||
const cacheData = spine._getCachedData(slot, attachment);
|
||||
|
||||
if (!cacheData.skipRender) {
|
||||
const batchableSpineSlot = gpuSpine.slotBatches[spine._getCachedData(slot, attachment).id];
|
||||
|
||||
batchableSpineSlot._batcher?.updateElement(batchableSpineSlot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destroyRenderable (spine: Spine) {
|
||||
// TODO remove the renderable from the batcher
|
||||
this.gpuSpineData[spine.uid] = null as any;
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.gpuSpineData = null as any;
|
||||
this.renderer = null as any;
|
||||
}
|
||||
}
|
||||
|
||||
extensions.add(SpinePipe);
|
||||
143
spine-ts/spine-pixi-v8/src/SpineTexture.ts
Normal file
@ -0,0 +1,143 @@
|
||||
/** ****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
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';
|
||||
|
||||
export class SpineTexture extends Texture {
|
||||
private static readonly textureMap: Map<TextureSource, SpineTexture> = new Map<TextureSource, SpineTexture>();
|
||||
|
||||
public static from (texture: TextureSource): SpineTexture {
|
||||
if (SpineTexture.textureMap.has(texture)) {
|
||||
return SpineTexture.textureMap.get(texture) as SpineTexture;
|
||||
}
|
||||
|
||||
return new SpineTexture(texture);
|
||||
}
|
||||
|
||||
public readonly texture: PixiTexture;
|
||||
|
||||
private constructor (image: TextureSource) {
|
||||
// Todo: maybe add error handling if you feed a video texture to spine?
|
||||
super(image.resource);
|
||||
this.texture = PixiTexture.from(image);
|
||||
}
|
||||
|
||||
public setFilters (minFilter: TextureFilter, magFilter: TextureFilter): void {
|
||||
const style = this.texture.source.style;
|
||||
|
||||
style.minFilter = SpineTexture.toPixiTextureFilter(minFilter);
|
||||
style.magFilter = SpineTexture.toPixiTextureFilter(magFilter);
|
||||
this.texture.source.autoGenerateMipmaps = SpineTexture.toPixiMipMap(minFilter);
|
||||
this.texture.source.updateMipmaps();
|
||||
}
|
||||
|
||||
public setWraps (uWrap: TextureWrap, vWrap: TextureWrap): void {
|
||||
const style = this.texture.source.style;
|
||||
|
||||
style.addressModeU = SpineTexture.toPixiTextureWrap(uWrap);
|
||||
style.addressModeV = SpineTexture.toPixiTextureWrap(vWrap);
|
||||
}
|
||||
|
||||
public dispose (): void {
|
||||
// I am not entirely sure about this...
|
||||
this.texture.destroy();
|
||||
}
|
||||
|
||||
private static toPixiMipMap (filter: TextureFilter): boolean {
|
||||
switch (filter) {
|
||||
case TextureFilter.Nearest:
|
||||
case TextureFilter.Linear:
|
||||
return false;
|
||||
|
||||
case TextureFilter.MipMapNearestLinear:
|
||||
case TextureFilter.MipMapNearestNearest:
|
||||
case TextureFilter.MipMapLinearLinear: // TextureFilter.MipMapLinearLinear == TextureFilter.MipMap
|
||||
case TextureFilter.MipMapLinearNearest:
|
||||
return true;
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown texture filter: ${String(filter)}`);
|
||||
}
|
||||
}
|
||||
|
||||
private static toPixiTextureFilter (filter: TextureFilter): SCALE_MODE {
|
||||
switch (filter) {
|
||||
case TextureFilter.Nearest:
|
||||
case TextureFilter.MipMapNearestLinear:
|
||||
case TextureFilter.MipMapNearestNearest:
|
||||
return 'nearest';
|
||||
|
||||
case TextureFilter.Linear:
|
||||
case TextureFilter.MipMapLinearLinear: // TextureFilter.MipMapLinearLinear == TextureFilter.MipMap
|
||||
case TextureFilter.MipMapLinearNearest:
|
||||
return 'linear';
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown texture filter: ${String(filter)}`);
|
||||
}
|
||||
}
|
||||
|
||||
private static toPixiTextureWrap (wrap: TextureWrap): WRAP_MODE {
|
||||
switch (wrap) {
|
||||
case TextureWrap.ClampToEdge:
|
||||
return 'clamp-to-edge';
|
||||
|
||||
case TextureWrap.MirroredRepeat:
|
||||
return 'mirror-repeat';
|
||||
|
||||
case TextureWrap.Repeat:
|
||||
return 'repeat';
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown texture wrap: ${String(wrap)}`);
|
||||
}
|
||||
}
|
||||
|
||||
public static toPixiBlending (blend: BlendMode): BLEND_MODES {
|
||||
switch (blend) {
|
||||
case BlendMode.Normal:
|
||||
return 'normal';
|
||||
|
||||
case BlendMode.Additive:
|
||||
return 'add';
|
||||
|
||||
case BlendMode.Multiply:
|
||||
return 'multiply';
|
||||
|
||||
case BlendMode.Screen:
|
||||
return 'screen';
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown blendMode: ${String(blend)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
158
spine-ts/spine-pixi-v8/src/assets/atlasLoader.ts
Normal file
@ -0,0 +1,158 @@
|
||||
/** ****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import {
|
||||
checkExtension,
|
||||
DOMAdapter,
|
||||
extensions,
|
||||
ExtensionType,
|
||||
LoaderParserPriority,
|
||||
path,
|
||||
Resolver,
|
||||
TextureSource
|
||||
} from 'pixi.js';
|
||||
import { SpineTexture } from '../SpineTexture';
|
||||
import { TextureAtlas } from '@esotericsoftware/spine-core';
|
||||
|
||||
import type { AssetExtension, Loader, ResolvedAsset, Texture, UnresolvedAsset } from 'pixi.js';
|
||||
|
||||
type RawAtlas = string;
|
||||
|
||||
const spineTextureAtlasLoader: AssetExtension<RawAtlas | TextureAtlas, ISpineAtlasMetadata> = {
|
||||
extension: ExtensionType.Asset,
|
||||
|
||||
resolver: {
|
||||
test: (value: string): boolean => checkExtension(value, ".atlas"),
|
||||
parse: (value: string): UnresolvedAsset => {
|
||||
const split = value.split('.');
|
||||
|
||||
return {
|
||||
resolution: parseFloat(Resolver.RETINA_PREFIX?.exec(value)?.[1] ?? '1'),
|
||||
format: split[split.length - 2],
|
||||
src: value,
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
loader: {
|
||||
extension: {
|
||||
type: ExtensionType.LoadParser,
|
||||
priority: LoaderParserPriority.Normal,
|
||||
name: 'spineTextureAtlasLoader',
|
||||
},
|
||||
|
||||
test (url: string): boolean {
|
||||
return checkExtension(url, '.atlas');
|
||||
},
|
||||
|
||||
async load (url: string): Promise<RawAtlas> {
|
||||
const response = await DOMAdapter.get().fetch(url);
|
||||
|
||||
const txt = await response.text();
|
||||
|
||||
return txt;
|
||||
},
|
||||
|
||||
testParse (asset: unknown, options: ResolvedAsset): Promise<boolean> {
|
||||
const isExtensionRight = checkExtension(options.src as string, '.atlas');
|
||||
const isString = typeof asset === 'string';
|
||||
|
||||
return Promise.resolve(isExtensionRight && isString);
|
||||
},
|
||||
|
||||
unload (atlas: TextureAtlas) {
|
||||
atlas.dispose();
|
||||
},
|
||||
|
||||
async parse (asset: RawAtlas, options: ResolvedAsset, loader: Loader): Promise<TextureAtlas> {
|
||||
const metadata: ISpineAtlasMetadata = options.data || {};
|
||||
let basePath = path.dirname(options.src as string);
|
||||
|
||||
if (basePath && basePath.lastIndexOf('/') !== basePath.length - 1) {
|
||||
basePath += '/';
|
||||
}
|
||||
|
||||
// Retval is going to be a texture atlas. However we need to wait for it's callback to resolve this promise.
|
||||
const retval = new TextureAtlas(asset);
|
||||
|
||||
// If the user gave me only one texture, that one is assumed to be the "first" texture in the atlas
|
||||
if (metadata.images instanceof TextureSource || typeof metadata.images === 'string') {
|
||||
const pixiTexture = metadata.images;
|
||||
|
||||
metadata.images = {} as Record<string, TextureSource | string>;
|
||||
metadata.images[retval.pages[0].name] = pixiTexture;
|
||||
}
|
||||
|
||||
// we will wait for all promises for the textures at the same time at the end.
|
||||
const textureLoadingPromises: Promise<any>[] = [];
|
||||
|
||||
// fill the pages
|
||||
for (const page of retval.pages) {
|
||||
const pageName = page.name;
|
||||
const providedPage = metadata?.images ? metadata.images[pageName] : undefined;
|
||||
|
||||
if (providedPage instanceof TextureSource) {
|
||||
page.setTexture(SpineTexture.from(providedPage));
|
||||
}
|
||||
else {
|
||||
// eslint-disable-next-line max-len
|
||||
const url: string = providedPage ?? path.normalize([...basePath.split(path.sep), pageName].join(path.sep));
|
||||
|
||||
const assetsToLoadIn = {
|
||||
src: url,
|
||||
data: {
|
||||
...metadata.imageMetadata,
|
||||
alphaMode: page.pma ? 'premultiplied-alpha' : 'premultiply-alpha-on-upload'
|
||||
}
|
||||
};
|
||||
|
||||
const pixiPromise = loader.load<Texture>(assetsToLoadIn).then((texture) => {
|
||||
page.setTexture(SpineTexture.from(texture.source));
|
||||
});
|
||||
|
||||
textureLoadingPromises.push(pixiPromise);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(textureLoadingPromises);
|
||||
|
||||
return retval;
|
||||
},
|
||||
},
|
||||
} as AssetExtension<RawAtlas | TextureAtlas, ISpineAtlasMetadata>;
|
||||
|
||||
extensions.add(spineTextureAtlasLoader);
|
||||
|
||||
export interface ISpineAtlasMetadata {
|
||||
// If you are downloading an .atlas file, this metadata will go to the Texture loader
|
||||
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?: TextureSource | string | Record<string, TextureSource | string>;
|
||||
}
|
||||
81
spine-ts/spine-pixi-v8/src/assets/skeletonLoader.ts
Normal file
@ -0,0 +1,81 @@
|
||||
/** ****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import {
|
||||
type AssetExtension,
|
||||
checkExtension,
|
||||
DOMAdapter,
|
||||
extensions,
|
||||
ExtensionType,
|
||||
LoaderParserPriority,
|
||||
ResolvedAsset
|
||||
} from 'pixi.js';
|
||||
|
||||
type SkeletonJsonAsset = any;
|
||||
type SkeletonBinaryAsset = Uint8Array;
|
||||
|
||||
function isJson (resource: any): resource is SkeletonJsonAsset {
|
||||
return Object.prototype.hasOwnProperty.call(resource, 'bones');
|
||||
}
|
||||
|
||||
function isBuffer (resource: any): resource is SkeletonBinaryAsset {
|
||||
return resource instanceof Uint8Array;
|
||||
}
|
||||
|
||||
const spineLoaderExtension: AssetExtension<SkeletonJsonAsset | SkeletonBinaryAsset> = {
|
||||
extension: ExtensionType.Asset,
|
||||
|
||||
loader: {
|
||||
extension: {
|
||||
type: ExtensionType.LoadParser,
|
||||
priority: LoaderParserPriority.Normal,
|
||||
name: 'spineSkeletonLoader',
|
||||
},
|
||||
|
||||
test (url) {
|
||||
return checkExtension(url, '.skel');
|
||||
},
|
||||
|
||||
async load (url: string): Promise<SkeletonBinaryAsset> {
|
||||
const response = await DOMAdapter.get().fetch(url);
|
||||
|
||||
const buffer = new Uint8Array(await response.arrayBuffer());
|
||||
|
||||
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);
|
||||
|
||||
return Promise.resolve(isJsonSpineModel || isBinarySpineModel);
|
||||
},
|
||||
},
|
||||
} as AssetExtension<SkeletonJsonAsset | SkeletonBinaryAsset>;
|
||||
|
||||
extensions.add(spineLoaderExtension);
|
||||
63
spine-ts/spine-pixi-v8/src/darktint/DarkTintBatchGeometry.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { Buffer, BufferUsage, Geometry } from 'pixi.js';
|
||||
|
||||
const placeHolderBufferData = new Float32Array(1);
|
||||
const placeHolderIndexData = new Uint32Array(1);
|
||||
|
||||
export class DarkTintBatchGeometry extends Geometry {
|
||||
constructor () {
|
||||
const vertexSize = 7;
|
||||
|
||||
const attributeBuffer = new Buffer({
|
||||
data: placeHolderBufferData,
|
||||
label: 'attribute-batch-buffer',
|
||||
usage: BufferUsage.VERTEX | BufferUsage.COPY_DST,
|
||||
shrinkToFit: false,
|
||||
});
|
||||
|
||||
const indexBuffer = new Buffer({
|
||||
data: placeHolderIndexData,
|
||||
label: 'index-batch-buffer',
|
||||
usage: BufferUsage.INDEX | BufferUsage.COPY_DST, // | BufferUsage.STATIC,
|
||||
shrinkToFit: false,
|
||||
});
|
||||
|
||||
const stride = vertexSize * 4;
|
||||
|
||||
super({
|
||||
attributes: {
|
||||
aPosition: {
|
||||
buffer: attributeBuffer,
|
||||
format: 'float32x2',
|
||||
stride,
|
||||
offset: 0,
|
||||
},
|
||||
aUV: {
|
||||
buffer: attributeBuffer,
|
||||
format: 'float32x2',
|
||||
stride,
|
||||
offset: 2 * 4,
|
||||
},
|
||||
aColor: {
|
||||
buffer: attributeBuffer,
|
||||
format: 'unorm8x4',
|
||||
stride,
|
||||
offset: 4 * 4,
|
||||
},
|
||||
aDarkColor: {
|
||||
buffer: attributeBuffer,
|
||||
format: 'unorm8x4',
|
||||
stride,
|
||||
offset: 5 * 4,
|
||||
},
|
||||
aTextureIdAndRound: {
|
||||
buffer: attributeBuffer,
|
||||
format: 'uint16x2',
|
||||
stride,
|
||||
offset: 6 * 4,
|
||||
},
|
||||
},
|
||||
indexBuffer
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
157
spine-ts/spine-pixi-v8/src/darktint/DarkTintBatcher.ts
Normal file
@ -0,0 +1,157 @@
|
||||
import {
|
||||
Batcher,
|
||||
Color,
|
||||
DefaultBatchableMeshElement,
|
||||
DefaultBatchableQuadElement,
|
||||
extensions,
|
||||
ExtensionType,
|
||||
Shader
|
||||
} from 'pixi.js';
|
||||
import { DarkTintBatchGeometry } from './DarkTintBatchGeometry';
|
||||
import { DarkTintShader } from './DarkTintShader';
|
||||
|
||||
let defaultShader: Shader | null = null;
|
||||
|
||||
/** The default batcher is used to batch quads and meshes. */
|
||||
export class DarkTintBatcher extends Batcher {
|
||||
/** @ignore */
|
||||
public static extension = {
|
||||
type: [
|
||||
ExtensionType.Batcher,
|
||||
],
|
||||
name: 'darkTint',
|
||||
} as const;
|
||||
|
||||
public geometry = new DarkTintBatchGeometry();
|
||||
public shader = defaultShader || (defaultShader = new DarkTintShader(this.maxTextures));
|
||||
public name = DarkTintBatcher.extension.name;
|
||||
|
||||
/** The size of one attribute. 1 = 32 bit. x, y, u, v, color, darkColor, textureIdAndRound -> total = 7 */
|
||||
public vertexSize = 7;
|
||||
|
||||
public packAttributes (
|
||||
element: DefaultBatchableMeshElement & { darkColor: number },
|
||||
float32View: Float32Array,
|
||||
uint32View: Uint32Array,
|
||||
index: number,
|
||||
textureId: number
|
||||
) {
|
||||
const textureIdAndRound = (textureId << 16) | (element.roundPixels & 0xFFFF);
|
||||
|
||||
const wt = element.transform;
|
||||
|
||||
const a = wt.a;
|
||||
const b = wt.b;
|
||||
const c = wt.c;
|
||||
const d = wt.d;
|
||||
const tx = wt.tx;
|
||||
const ty = wt.ty;
|
||||
|
||||
const { positions, uvs } = element;
|
||||
|
||||
const argb = element.color;
|
||||
const worldAlpha = ((argb >> 24) & 0xFF) / 255;
|
||||
const darkColor = Color.shared.setValue(element.darkColor).premultiply(worldAlpha, true).toPremultiplied(1, false);
|
||||
|
||||
const offset = element.attributeOffset;
|
||||
const end = offset + element.attributeSize;
|
||||
|
||||
for (let i = offset; i < end; i++) {
|
||||
const i2 = i * 2;
|
||||
|
||||
const x = positions[i2];
|
||||
const y = positions[(i2) + 1];
|
||||
|
||||
float32View[index++] = (a * x) + (c * y) + tx;
|
||||
float32View[index++] = (d * y) + (b * x) + ty;
|
||||
|
||||
float32View[index++] = uvs[i2];
|
||||
float32View[index++] = uvs[(i2) + 1];
|
||||
|
||||
uint32View[index++] = argb;
|
||||
uint32View[index++] = darkColor;
|
||||
|
||||
uint32View[index++] = textureIdAndRound;
|
||||
}
|
||||
}
|
||||
|
||||
public packQuadAttributes (
|
||||
element: DefaultBatchableQuadElement & { darkColor: number },
|
||||
float32View: Float32Array,
|
||||
uint32View: Uint32Array,
|
||||
index: number,
|
||||
textureId: number
|
||||
) {
|
||||
const texture = element.texture;
|
||||
|
||||
const wt = element.transform;
|
||||
|
||||
const a = wt.a;
|
||||
const b = wt.b;
|
||||
const c = wt.c;
|
||||
const d = wt.d;
|
||||
const tx = wt.tx;
|
||||
const ty = wt.ty;
|
||||
|
||||
const bounds = element.bounds;
|
||||
|
||||
const w0 = bounds.maxX;
|
||||
const w1 = bounds.minX;
|
||||
const h0 = bounds.maxY;
|
||||
const h1 = bounds.minY;
|
||||
|
||||
const uvs = texture.uvs;
|
||||
|
||||
// _ _ _ _
|
||||
// a b g r
|
||||
const argb = element.color;
|
||||
const darkColor = element.darkColor;
|
||||
|
||||
const textureIdAndRound = (textureId << 16) | (element.roundPixels & 0xFFFF);
|
||||
|
||||
float32View[index + 0] = (a * w1) + (c * h1) + tx;
|
||||
float32View[index + 1] = (d * h1) + (b * w1) + ty;
|
||||
|
||||
float32View[index + 2] = uvs.x0;
|
||||
float32View[index + 3] = uvs.y0;
|
||||
|
||||
uint32View[index + 4] = argb;
|
||||
uint32View[index + 5] = darkColor;
|
||||
uint32View[index + 6] = textureIdAndRound;
|
||||
|
||||
// xy
|
||||
float32View[index + 7] = (a * w0) + (c * h1) + tx;
|
||||
float32View[index + 8] = (d * h1) + (b * w0) + ty;
|
||||
|
||||
float32View[index + 9] = uvs.x1;
|
||||
float32View[index + 10] = uvs.y1;
|
||||
|
||||
uint32View[index + 11] = argb;
|
||||
uint32View[index + 12] = darkColor;
|
||||
uint32View[index + 13] = textureIdAndRound;
|
||||
|
||||
// xy
|
||||
float32View[index + 14] = (a * w0) + (c * h0) + tx;
|
||||
float32View[index + 15] = (d * h0) + (b * w0) + ty;
|
||||
|
||||
float32View[index + 16] = uvs.x2;
|
||||
float32View[index + 17] = uvs.y2;
|
||||
|
||||
uint32View[index + 18] = argb;
|
||||
uint32View[index + 19] = darkColor;
|
||||
uint32View[index + 20] = textureIdAndRound;
|
||||
|
||||
// xy
|
||||
float32View[index + 21] = (a * w1) + (c * h0) + tx;
|
||||
float32View[index + 22] = (d * h0) + (b * w1) + ty;
|
||||
|
||||
float32View[index + 23] = uvs.x3;
|
||||
float32View[index + 24] = uvs.y3;
|
||||
|
||||
uint32View[index + 25] = argb;
|
||||
uint32View[index + 26] = darkColor;
|
||||
uint32View[index + 27] = textureIdAndRound;
|
||||
}
|
||||
}
|
||||
|
||||
extensions.add(DarkTintBatcher);
|
||||
45
spine-ts/spine-pixi-v8/src/darktint/DarkTintShader.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import {
|
||||
colorBit,
|
||||
colorBitGl,
|
||||
compileHighShaderGlProgram,
|
||||
compileHighShaderGpuProgram,
|
||||
generateTextureBatchBit,
|
||||
generateTextureBatchBitGl,
|
||||
getBatchSamplersUniformGroup,
|
||||
roundPixelsBit,
|
||||
roundPixelsBitGl,
|
||||
Shader
|
||||
} from 'pixi.js';
|
||||
import { darkTintBit, darkTintBitGl } from './darkTintBit';
|
||||
|
||||
export class DarkTintShader extends Shader {
|
||||
constructor (maxTextures: number) {
|
||||
const glProgram = compileHighShaderGlProgram({
|
||||
name: 'dark-tint-batch',
|
||||
bits: [
|
||||
colorBitGl,
|
||||
darkTintBitGl,
|
||||
generateTextureBatchBitGl(maxTextures),
|
||||
roundPixelsBitGl,
|
||||
]
|
||||
});
|
||||
|
||||
const gpuProgram = compileHighShaderGpuProgram({
|
||||
name: 'dark-tint-batch',
|
||||
bits: [
|
||||
colorBit,
|
||||
darkTintBit,
|
||||
generateTextureBatchBit(maxTextures),
|
||||
roundPixelsBit,
|
||||
]
|
||||
});
|
||||
|
||||
super({
|
||||
glProgram,
|
||||
gpuProgram,
|
||||
resources: {
|
||||
batchSamplers: getBatchSamplersUniformGroup(maxTextures),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
49
spine-ts/spine-pixi-v8/src/darktint/darkTintBit.ts
Normal file
@ -0,0 +1,49 @@
|
||||
/* eslint-disable max-len */
|
||||
export const darkTintBit = {
|
||||
name: 'color-bit',
|
||||
vertex: {
|
||||
header: /* wgsl */`
|
||||
@in aDarkColor: vec4<f32>;
|
||||
@out vDarkColor: vec4<f32>;
|
||||
`,
|
||||
main: /* wgsl */`
|
||||
vDarkColor = aDarkColor;
|
||||
`
|
||||
},
|
||||
fragment: {
|
||||
header: /* wgsl */`
|
||||
@in vDarkColor: vec4<f32>;
|
||||
`,
|
||||
end: /* wgsl */`
|
||||
|
||||
let alpha = outColor.a * vColor.a;
|
||||
let rgb = ((outColor.a - 1.0) * vDarkColor.a + 1.0 - outColor.rgb) * vDarkColor.rgb + outColor.rgb * vColor.rgb;
|
||||
|
||||
finalColor = vec4<f32>(rgb, alpha);
|
||||
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
export const darkTintBitGl = {
|
||||
name: 'color-bit',
|
||||
vertex: {
|
||||
header: /* glsl */`
|
||||
in vec4 aDarkColor;
|
||||
out vec4 vDarkColor;
|
||||
`,
|
||||
main: /* glsl */`
|
||||
vDarkColor = aDarkColor;
|
||||
`
|
||||
},
|
||||
fragment: {
|
||||
header: /* glsl */`
|
||||
in vec4 vDarkColor;
|
||||
`,
|
||||
end: /* glsl */`
|
||||
|
||||
finalColor.a = outColor.a * vColor.a;
|
||||
finalColor.rgb = ((outColor.a - 1.0) * vDarkColor.a + 1.0 - outColor.rgb) * vDarkColor.rgb + outColor.rgb * vColor.rgb;
|
||||
`
|
||||
}
|
||||
};
|
||||
43
spine-ts/spine-pixi-v8/src/index.ts
Normal file
@ -0,0 +1,43 @@
|
||||
/** ****************************************************************************
|
||||
* 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'; // Side effects add require pixi.js to global scope
|
||||
import './assets/atlasLoader.js'; // Side effects install the loaders into pixi
|
||||
import './assets/skeletonLoader.js'; // Side effects install the loaders into pixi
|
||||
import './darktint/DarkTintBatcher.js'; // Side effects install the batcher into pixi
|
||||
import './SpinePipe.js';
|
||||
|
||||
export * from './assets/atlasLoader.js';
|
||||
export * from './assets/skeletonLoader.js';
|
||||
export * from './require-shim.js';
|
||||
export * from './Spine.js';
|
||||
export * from './SpineDebugRenderer.js';
|
||||
export * from './SpinePipe.js';
|
||||
export * from './SpineTexture.js';
|
||||
export * from '@esotericsoftware/spine-core';
|
||||
43
spine-ts/spine-pixi-v8/src/require-shim.ts
Normal file
@ -0,0 +1,43 @@
|
||||
/** ****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
declare global {
|
||||
var require: any;
|
||||
var PIXI: any;
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && window.PIXI) {
|
||||
const prevRequire = window.require;
|
||||
(window as any).require = (x: string) => {
|
||||
if (prevRequire) return prevRequire(x);
|
||||
else if (x.startsWith("@pixi/") || x.startsWith("pixi.js")) return window.PIXI;
|
||||
};
|
||||
}
|
||||
|
||||
export { };
|
||||
25
spine-ts/spine-pixi-v8/tsconfig.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@esotericsoftware/spine-core": [
|
||||
"../spine-core/src"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
],
|
||||
"exclude": [
|
||||
"dist/**/*.d.ts",
|
||||
"example/**/*.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../spine-core"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,3 +1,3 @@
|
||||
# spine-ts Pixi.js
|
||||
# spine-ts Pixi.js v7
|
||||
|
||||
Please see the top-level [README.md](../README.md) for more information.
|
||||
|
||||
@ -45,9 +45,9 @@
|
||||
await PIXI.Assets.load(["stretchymanData", "stretchymanAtlas"]);
|
||||
|
||||
// Create the spine display object
|
||||
const stretchyman = spine.Spine.from("stretchymanData", "stretchymanAtlas", {
|
||||
const stretchyman = spine.Spine.from({skeleton: "stretchymanData", atlas: "stretchymanAtlas",
|
||||
scale: 0.75,
|
||||
});
|
||||
});
|
||||
|
||||
// Set the default mix time to use when transitioning
|
||||
// from one animation to the next.
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
await PIXI.Assets.load(["spineboyData", "spineboyAtlas"]);
|
||||
|
||||
// Create the Spine display object
|
||||
const spineboy = spine.Spine.from("spineboyData", "spineboyAtlas", {
|
||||
const spineboy = spine.Spine.from({skeleton: "spineboyData", atlas: "spineboyAtlas",
|
||||
scale: 0.5,
|
||||
});
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
await PIXI.Assets.load(["spineboyData", "spineboyAtlas"]);
|
||||
|
||||
// Create the spine display object
|
||||
const spineboy = spine.Spine.from("spineboyData", "spineboyAtlas", {
|
||||
const spineboy = spine.Spine.from({skeleton: "spineboyData", atlas: "spineboyAtlas",
|
||||
scale: 0.5,
|
||||
});
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
const skeletonData = binaryLoader.readSkeletonData(
|
||||
PIXI.Assets.get("spineboyData")
|
||||
);
|
||||
const spineboy = new spine.Spine(skeletonData);
|
||||
const spineboy = new spine.Spine({ skeletonData });
|
||||
|
||||
// Set the default mix time to use when transitioning
|
||||
// from one animation to the next.
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
await PIXI.Assets.load(["mixAndMatchData", "mixAndMatchAtlas"]);
|
||||
|
||||
// Create the Spine display object
|
||||
const mixAndMatch = spine.Spine.from("mixAndMatchData", "mixAndMatchAtlas", {
|
||||
const mixAndMatch = spine.Spine.from({skeleton: "mixAndMatchData", atlas: "mixAndMatchAtlas",
|
||||
scale: 0.5,
|
||||
});
|
||||
|
||||
@ -36,10 +36,6 @@
|
||||
// from one animation to the next.
|
||||
mixAndMatch.state.data.defaultMix = 0.2;
|
||||
|
||||
// Center the spine object on screen.
|
||||
mixAndMatch.x = window.innerWidth / 2;
|
||||
mixAndMatch.y = window.innerHeight / 2 + mixAndMatch.getBounds().height / 2;
|
||||
|
||||
// Add animations.
|
||||
mixAndMatch.state.setAnimation(0, "walk", true);
|
||||
mixAndMatch.state.addAnimation(0, "dance", true, 1.0);
|
||||
@ -60,6 +56,10 @@
|
||||
mixAndMatch.skeleton.setSkin(skin);
|
||||
mixAndMatch.skeleton.setSlotsToSetupPose();
|
||||
|
||||
// Center the spine object on screen.
|
||||
mixAndMatch.x = window.innerWidth / 2;
|
||||
mixAndMatch.y = window.innerHeight / 2 + mixAndMatch.getBounds().height / 2;
|
||||
|
||||
// Add the display object to the stage.
|
||||
app.stage.addChild(mixAndMatch);
|
||||
})();
|
||||
|
||||