├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── build ├── playcanvas-spine.3.6.js ├── playcanvas-spine.3.6.min.js ├── playcanvas-spine.3.8.js ├── playcanvas-spine.3.8.min.js ├── playcanvas-spine.4.0.js ├── playcanvas-spine.4.0.min.js ├── playcanvas-spine.4.1.js ├── playcanvas-spine.4.1.min.js ├── playcanvas-spine.4.2.js ├── playcanvas-spine.4.2.min.js └── spine.js ├── contrib └── spine-ts │ ├── LICENSE │ └── build │ ├── 3.6 │ ├── spine-core.d.ts │ ├── spine-core.js │ └── spine-core.js.map │ └── 3.8 │ ├── spine-core.d.ts │ ├── spine-core.js │ └── spine-core.js.map ├── examples ├── alien36.html ├── assets │ ├── spine36 │ │ ├── README.md │ │ ├── alien-pma.atlas │ │ ├── alien-pma.png │ │ ├── alien.json │ │ ├── dragon-pma.atlas │ │ ├── dragon-pma.png │ │ ├── dragon-pma2.png │ │ ├── dragon.json │ │ ├── goblins-pma.atlas │ │ ├── goblins-pma.png │ │ ├── goblins-pro.json │ │ ├── hero-mesh.atlas │ │ ├── hero-mesh.json │ │ ├── hero-mesh.png │ │ └── license.txt │ ├── spine38 │ │ ├── README.md │ │ ├── license.txt │ │ ├── spineboy-pro.atlas │ │ ├── spineboy-pro.json │ │ └── spineboy-pro.png │ ├── spine40 │ │ ├── LICENSE │ │ ├── atlas2.atlas │ │ ├── atlas2.png │ │ ├── demos.json │ │ ├── raptor-pro.json │ │ ├── raptor.atlas │ │ └── raptor.png │ └── spine41 │ │ ├── README.md │ │ ├── license.txt │ │ ├── raptor-pro.json │ │ ├── raptor.atlas │ │ └── raptor.png ├── dragon36.html ├── goblins36.html ├── hero36.html ├── owl40.html ├── raptor40.html ├── raptor41.html └── spineboy38.html ├── images └── spine-man.gif ├── package-lock.json ├── package.json ├── rollup.config.mjs ├── src ├── SpinePlugin.js ├── component │ ├── Spine.js │ ├── SpineComponent.js │ ├── SpineComponentData.js │ ├── SpineComponentSystem.js │ ├── SpineTextureWrapper.js │ └── semver │ │ └── index.js ├── wrapper40.js ├── wrapper41.js └── wrapper42.js └── tsconfig.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # This file normalizes line endings stored in the repo to LF (Unix/Git). 2 | # Contributors are still able to use their native line endings locally. 3 | # More info here: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings 4 | 5 | # Set the default behavior, in case people don't have core.autocrlf set. 6 | * text=auto 7 | 8 | # Explicitly declare text files to be normalized on checkin 9 | # and converted back to native line endings on checkout. 10 | *.js text 11 | *.json text 12 | *.mjs text 13 | *.cjs text 14 | *.jsx text 15 | *.ts text 16 | *.txt text 17 | *.tsx text 18 | *.md text 19 | *.html text 20 | *.gltf text 21 | *.glsl text 22 | *.css text 23 | *.mustache text 24 | *.obj text 25 | *.atlas text 26 | *.yaml text 27 | *.babelrc text 28 | 29 | # Denote all files that are truly binary and should therefore not be modified. 30 | *.webp binary 31 | *.png binary 32 | *.jpg binary 33 | *.glb binary 34 | *.fbx binary 35 | *.wasm binary 36 | *.basis binary 37 | *.bin binary 38 | *.dds binary 39 | *.drc binary 40 | *.mp3 binary 41 | *.mp4 binary 42 | *.gz binary 43 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at support@playcanvas.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | This issue tracker is **only** for bug reports and feature requests. For general support/help, visit https://forum.playcanvas.com 2 | 3 | For bug reports, include: 4 | 5 | ### Description 6 | 7 | Provide as much information as possible. Include (where applicable): 8 | 9 | * URL to a simple, reproducible test case that illustrates the problem clearly 10 | * Screenshots 11 | * The platform(s)/browser(s) where the issue is seen 12 | * A screenshot of http://webglreport.com/ (for graphics related issues) 13 | 14 | ### Steps to Reproduce 15 | 16 | 1. 17 | 2. 18 | 3. 19 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # 2 | 3 | I confirm I have read the [contributing guidelines](https://github.com/playcanvas/engine/blob/master/.github/CONTRIBUTING.md) and signed the [Contributor License Agreement](https://docs.google.com/a/playcanvas.com/forms/d/1Ih69zQfJG-QDLIEpHr6CsaAs6fPORNOVnMv5nuo0cjk/viewform). 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, 2 | # lint, build the source code, and run tests across different versions of node. 3 | 4 | # For more information see: 5 | # https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 6 | 7 | name: CI 8 | 9 | on: 10 | workflow_dispatch: 11 | push: 12 | branches: ['main'] 13 | paths-ignore: ['README.md', 'LICENSE'] 14 | pull_request: 15 | branches: ['main'] 16 | paths-ignore: ['README.md', 'LICENSE'] 17 | 18 | concurrency: 19 | group: ci-${{ github.event.pull_request.number || github.ref }} 20 | cancel-in-progress: true 21 | 22 | env: 23 | VITEST_SEGFAULT_RETRY: 3 24 | 25 | jobs: 26 | ci-on-node: 27 | runs-on: ubuntu-latest 28 | 29 | timeout-minutes: 10 30 | 31 | strategy: 32 | matrix: 33 | node-version: [18.x] 34 | 35 | steps: 36 | - uses: actions/checkout@v4 37 | 38 | - name: Use Node.js ${{ matrix.node-version }} 39 | uses: actions/setup-node@v3 40 | with: 41 | node-version: ${{ matrix.node-version }} 42 | cache: 'npm' 43 | 44 | - name: Install 45 | run: npm clean-install --progress=false --no-fund 46 | 47 | - name: Lint 48 | run: npm run lint --if-present 49 | 50 | - name: Build 51 | run: npm run build --if-present 52 | 53 | - name: Test 54 | run: npm run test-run --if-present 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | .idea/ 3 | .vscode/ 4 | coverage 5 | node_modules 6 | npm-debug.log 7 | .npmrc 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2023 PlayCanvas Ltd. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PlayCanvas Spine 2 | 3 | A [Spine](http://esotericsoftware.com/) plugin for the PlayCanvas Engine. 4 | 5 | [![CI][ci-badge]][ci-url] 6 | 7 | ![](images/spine-man.gif) 8 | 9 | Examples such as the Hero above can be found in the `examples` folder. To run them, start a local web server and go to `http://localhost/path/to/examples/hero.html` (the path will depend on your file serving root directory). 10 | 11 | ## Usage 12 | 13 | ### Versions 14 | 15 | The following plugins are available: 16 | 17 | | Spine Editor | PlayCanvas Engine | 18 | | ------------ | ----------------- | 19 | | 3.6 | Up to 1.65 | 20 | | 3.8 | Up to 1.65 | 21 | | 4.0 | 1.27 and later | 22 | | 4.1 | 1.27 and later | 23 | 24 | Each plugin provides both a Component System to PlayCanvas Engine and the corresponding `spine-core` runtime to your scripts as global variable called `spine`. This allows developers to leverage the full spine library. 25 | 26 | ### Editor 27 | 28 | Add the plugin matching the Spine version used to export the animations, i.e `build/playcanvas-spine.X.X.min.js` and the PlayCanvas script `build/spine.js` to your project. 29 | 30 | Create an entity with a script component and add the script `spine` to it. Upload your exported spine resources (atlas, skeleton json file, textures) and attach them to the spine script on your entity. 31 | 32 | Ensure the plugin file is listed before the PlayCanvas script in the [Scripts Loading Order](https://developer.playcanvas.com/en/user-manual/scripting/loading-order/). 33 | 34 | ### Engine-only 35 | 36 | Load the required library script, i.e. `build/playcanvas-spine.X.X.min.js`. Then, add spine components to your entities as follows: 37 | 38 | ```javascript 39 | var entity = new pc.Entity(); 40 | entity.addComponent("spine", { 41 | atlasAsset: atlas, // atlas text asset id 42 | textureAssets: textures, // array of texture asset ids 43 | skeletonAsset: skeleton // skeleton json asset id 44 | }); 45 | ``` 46 | 47 | ## Building 48 | 49 | Prebuilt versions of the PlayCanvas Spine library can be found in the `lib` folder. However, to build them yourself, first install the NPM package dependencies: 50 | 51 | `npm install` 52 | 53 | Then, to build do: 54 | 55 | `npm run build` 56 | 57 | 58 | [ci-badge]: https://github.com/playcanvas/playcanvas-spine/actions/workflows/ci.yml/badge.svg 59 | [ci-url]: https://github.com/playcanvas/playcanvas-spine/actions/workflows/ci.yml 60 | -------------------------------------------------------------------------------- /build/spine.js: -------------------------------------------------------------------------------- 1 | var Spine = pc.createScript("spine"); 2 | 3 | Spine.attributes.add("atlas", { type: "asset", assetType: "text" }); 4 | Spine.attributes.add("skeleton", { type: "asset", assetType: "json" }); 5 | Spine.attributes.add("textures", { type: "asset", array: true, assetType: "texture" }); 6 | Spine.attributes.add("priority", { type: "number", default: 1 }); 7 | 8 | Spine.prototype.initialize = function () { 9 | if (this.atlas && this.textures && this.skeleton) { 10 | // If all assets are present, add the spine component to the entity 11 | this.entity.addComponent("spine", { 12 | atlasAsset: this.atlas.id, 13 | textureAssets: this.textures.map(function (a) { 14 | return a.id; 15 | }), 16 | skeletonAsset: this.skeleton.id 17 | }); 18 | 19 | if (this.entity.spine) { 20 | this.priority = this.priority ? this.priority : 0; 21 | this.entity.spine.spine.priority = this.priority; 22 | } 23 | } 24 | 25 | this.on('attr:priority', function (val) { 26 | if (this.entity.spine) { 27 | this.entity.spine.spine.priority = val; 28 | } 29 | }, this); 30 | }; 31 | -------------------------------------------------------------------------------- /contrib/spine-ts/LICENSE: -------------------------------------------------------------------------------- 1 | Spine Runtimes License Agreement 2 | Last updated May 1, 2019. Replaces all prior versions. 3 | 4 | Copyright (c) 2013-2019, Esoteric Software LLC 5 | 6 | Integration of the Spine Runtimes into software or otherwise creating 7 | derivative works of the Spine Runtimes is permitted under the terms and 8 | conditions of Section 2 of the Spine Editor License Agreement: 9 | http://esotericsoftware.com/spine-editor-license 10 | 11 | Otherwise, it is permitted to integrate the Spine Runtimes into software 12 | or otherwise create derivative works of the Spine Runtimes (collectively, 13 | "Products"), provided that each user of the Products must obtain their own 14 | Spine Editor license and redistribution of the Products in any form must 15 | include this license and copyright notice. 16 | 17 | THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS 18 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 20 | NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS 23 | INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /contrib/spine-ts/build/3.6/spine-core.d.ts: -------------------------------------------------------------------------------- 1 | declare module spine { 2 | class Animation { 3 | name: string; 4 | timelines: Array; 5 | duration: number; 6 | constructor(name: string, timelines: Array, duration: number); 7 | apply(skeleton: Skeleton, lastTime: number, time: number, loop: boolean, events: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 8 | static binarySearch(values: ArrayLike, target: number, step?: number): number; 9 | static linearSearch(values: ArrayLike, target: number, step: number): number; 10 | } 11 | interface Timeline { 12 | apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 13 | getPropertyId(): number; 14 | } 15 | enum MixPose { 16 | setup = 0, 17 | current = 1, 18 | currentLayered = 2, 19 | } 20 | enum MixDirection { 21 | in = 0, 22 | out = 1, 23 | } 24 | enum TimelineType { 25 | rotate = 0, 26 | translate = 1, 27 | scale = 2, 28 | shear = 3, 29 | attachment = 4, 30 | color = 5, 31 | deform = 6, 32 | event = 7, 33 | drawOrder = 8, 34 | ikConstraint = 9, 35 | transformConstraint = 10, 36 | pathConstraintPosition = 11, 37 | pathConstraintSpacing = 12, 38 | pathConstraintMix = 13, 39 | twoColor = 14, 40 | } 41 | abstract class CurveTimeline implements Timeline { 42 | static LINEAR: number; 43 | static STEPPED: number; 44 | static BEZIER: number; 45 | static BEZIER_SIZE: number; 46 | private curves; 47 | abstract getPropertyId(): number; 48 | constructor(frameCount: number); 49 | getFrameCount(): number; 50 | setLinear(frameIndex: number): void; 51 | setStepped(frameIndex: number): void; 52 | getCurveType(frameIndex: number): number; 53 | setCurve(frameIndex: number, cx1: number, cy1: number, cx2: number, cy2: number): void; 54 | getCurvePercent(frameIndex: number, percent: number): number; 55 | abstract apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 56 | } 57 | class RotateTimeline extends CurveTimeline { 58 | static ENTRIES: number; 59 | static PREV_TIME: number; 60 | static PREV_ROTATION: number; 61 | static ROTATION: number; 62 | boneIndex: number; 63 | frames: ArrayLike; 64 | constructor(frameCount: number); 65 | getPropertyId(): number; 66 | setFrame(frameIndex: number, time: number, degrees: number): void; 67 | apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 68 | } 69 | class TranslateTimeline extends CurveTimeline { 70 | static ENTRIES: number; 71 | static PREV_TIME: number; 72 | static PREV_X: number; 73 | static PREV_Y: number; 74 | static X: number; 75 | static Y: number; 76 | boneIndex: number; 77 | frames: ArrayLike; 78 | constructor(frameCount: number); 79 | getPropertyId(): number; 80 | setFrame(frameIndex: number, time: number, x: number, y: number): void; 81 | apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 82 | } 83 | class ScaleTimeline extends TranslateTimeline { 84 | constructor(frameCount: number); 85 | getPropertyId(): number; 86 | apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 87 | } 88 | class ShearTimeline extends TranslateTimeline { 89 | constructor(frameCount: number); 90 | getPropertyId(): number; 91 | apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 92 | } 93 | class ColorTimeline extends CurveTimeline { 94 | static ENTRIES: number; 95 | static PREV_TIME: number; 96 | static PREV_R: number; 97 | static PREV_G: number; 98 | static PREV_B: number; 99 | static PREV_A: number; 100 | static R: number; 101 | static G: number; 102 | static B: number; 103 | static A: number; 104 | slotIndex: number; 105 | frames: ArrayLike; 106 | constructor(frameCount: number); 107 | getPropertyId(): number; 108 | setFrame(frameIndex: number, time: number, r: number, g: number, b: number, a: number): void; 109 | apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 110 | } 111 | class TwoColorTimeline extends CurveTimeline { 112 | static ENTRIES: number; 113 | static PREV_TIME: number; 114 | static PREV_R: number; 115 | static PREV_G: number; 116 | static PREV_B: number; 117 | static PREV_A: number; 118 | static PREV_R2: number; 119 | static PREV_G2: number; 120 | static PREV_B2: number; 121 | static R: number; 122 | static G: number; 123 | static B: number; 124 | static A: number; 125 | static R2: number; 126 | static G2: number; 127 | static B2: number; 128 | slotIndex: number; 129 | frames: ArrayLike; 130 | constructor(frameCount: number); 131 | getPropertyId(): number; 132 | setFrame(frameIndex: number, time: number, r: number, g: number, b: number, a: number, r2: number, g2: number, b2: number): void; 133 | apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 134 | } 135 | class AttachmentTimeline implements Timeline { 136 | slotIndex: number; 137 | frames: ArrayLike; 138 | attachmentNames: Array; 139 | constructor(frameCount: number); 140 | getPropertyId(): number; 141 | getFrameCount(): number; 142 | setFrame(frameIndex: number, time: number, attachmentName: string): void; 143 | apply(skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 144 | } 145 | class DeformTimeline extends CurveTimeline { 146 | slotIndex: number; 147 | attachment: VertexAttachment; 148 | frames: ArrayLike; 149 | frameVertices: Array>; 150 | constructor(frameCount: number); 151 | getPropertyId(): number; 152 | setFrame(frameIndex: number, time: number, vertices: ArrayLike): void; 153 | apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 154 | } 155 | class EventTimeline implements Timeline { 156 | frames: ArrayLike; 157 | events: Array; 158 | constructor(frameCount: number); 159 | getPropertyId(): number; 160 | getFrameCount(): number; 161 | setFrame(frameIndex: number, event: Event): void; 162 | apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 163 | } 164 | class DrawOrderTimeline implements Timeline { 165 | frames: ArrayLike; 166 | drawOrders: Array>; 167 | constructor(frameCount: number); 168 | getPropertyId(): number; 169 | getFrameCount(): number; 170 | setFrame(frameIndex: number, time: number, drawOrder: Array): void; 171 | apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 172 | } 173 | class IkConstraintTimeline extends CurveTimeline { 174 | static ENTRIES: number; 175 | static PREV_TIME: number; 176 | static PREV_MIX: number; 177 | static PREV_BEND_DIRECTION: number; 178 | static MIX: number; 179 | static BEND_DIRECTION: number; 180 | ikConstraintIndex: number; 181 | frames: ArrayLike; 182 | constructor(frameCount: number); 183 | getPropertyId(): number; 184 | setFrame(frameIndex: number, time: number, mix: number, bendDirection: number): void; 185 | apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 186 | } 187 | class TransformConstraintTimeline extends CurveTimeline { 188 | static ENTRIES: number; 189 | static PREV_TIME: number; 190 | static PREV_ROTATE: number; 191 | static PREV_TRANSLATE: number; 192 | static PREV_SCALE: number; 193 | static PREV_SHEAR: number; 194 | static ROTATE: number; 195 | static TRANSLATE: number; 196 | static SCALE: number; 197 | static SHEAR: number; 198 | transformConstraintIndex: number; 199 | frames: ArrayLike; 200 | constructor(frameCount: number); 201 | getPropertyId(): number; 202 | setFrame(frameIndex: number, time: number, rotateMix: number, translateMix: number, scaleMix: number, shearMix: number): void; 203 | apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 204 | } 205 | class PathConstraintPositionTimeline extends CurveTimeline { 206 | static ENTRIES: number; 207 | static PREV_TIME: number; 208 | static PREV_VALUE: number; 209 | static VALUE: number; 210 | pathConstraintIndex: number; 211 | frames: ArrayLike; 212 | constructor(frameCount: number); 213 | getPropertyId(): number; 214 | setFrame(frameIndex: number, time: number, value: number): void; 215 | apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 216 | } 217 | class PathConstraintSpacingTimeline extends PathConstraintPositionTimeline { 218 | constructor(frameCount: number); 219 | getPropertyId(): number; 220 | apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 221 | } 222 | class PathConstraintMixTimeline extends CurveTimeline { 223 | static ENTRIES: number; 224 | static PREV_TIME: number; 225 | static PREV_ROTATE: number; 226 | static PREV_TRANSLATE: number; 227 | static ROTATE: number; 228 | static TRANSLATE: number; 229 | pathConstraintIndex: number; 230 | frames: ArrayLike; 231 | constructor(frameCount: number); 232 | getPropertyId(): number; 233 | setFrame(frameIndex: number, time: number, rotateMix: number, translateMix: number): void; 234 | apply(skeleton: Skeleton, lastTime: number, time: number, firedEvents: Array, alpha: number, pose: MixPose, direction: MixDirection): void; 235 | } 236 | } 237 | declare module spine { 238 | class AnimationState { 239 | static emptyAnimation: Animation; 240 | static SUBSEQUENT: number; 241 | static FIRST: number; 242 | static DIP: number; 243 | static DIP_MIX: number; 244 | data: AnimationStateData; 245 | tracks: TrackEntry[]; 246 | events: Event[]; 247 | listeners: AnimationStateListener2[]; 248 | queue: EventQueue; 249 | propertyIDs: IntSet; 250 | mixingTo: TrackEntry[]; 251 | animationsChanged: boolean; 252 | timeScale: number; 253 | trackEntryPool: Pool; 254 | constructor(data: AnimationStateData); 255 | update(delta: number): void; 256 | updateMixingFrom(to: TrackEntry, delta: number): boolean; 257 | apply(skeleton: Skeleton): boolean; 258 | applyMixingFrom(to: TrackEntry, skeleton: Skeleton, currentPose: MixPose): number; 259 | applyRotateTimeline(timeline: Timeline, skeleton: Skeleton, time: number, alpha: number, pose: MixPose, timelinesRotation: Array, i: number, firstFrame: boolean): void; 260 | queueEvents(entry: TrackEntry, animationTime: number): void; 261 | clearTracks(): void; 262 | clearTrack(trackIndex: number): void; 263 | setCurrent(index: number, current: TrackEntry, interrupt: boolean): void; 264 | setAnimation(trackIndex: number, animationName: string, loop: boolean): TrackEntry; 265 | setAnimationWith(trackIndex: number, animation: Animation, loop: boolean): TrackEntry; 266 | addAnimation(trackIndex: number, animationName: string, loop: boolean, delay: number): TrackEntry; 267 | addAnimationWith(trackIndex: number, animation: Animation, loop: boolean, delay: number): TrackEntry; 268 | setEmptyAnimation(trackIndex: number, mixDuration: number): TrackEntry; 269 | addEmptyAnimation(trackIndex: number, mixDuration: number, delay: number): TrackEntry; 270 | setEmptyAnimations(mixDuration: number): void; 271 | expandToIndex(index: number): TrackEntry; 272 | trackEntry(trackIndex: number, animation: Animation, loop: boolean, last: TrackEntry): TrackEntry; 273 | disposeNext(entry: TrackEntry): void; 274 | _animationsChanged(): void; 275 | getCurrent(trackIndex: number): TrackEntry; 276 | addListener(listener: AnimationStateListener2): void; 277 | removeListener(listener: AnimationStateListener2): void; 278 | clearListeners(): void; 279 | clearListenerNotifications(): void; 280 | } 281 | class TrackEntry { 282 | animation: Animation; 283 | next: TrackEntry; 284 | mixingFrom: TrackEntry; 285 | listener: AnimationStateListener2; 286 | trackIndex: number; 287 | loop: boolean; 288 | eventThreshold: number; 289 | attachmentThreshold: number; 290 | drawOrderThreshold: number; 291 | animationStart: number; 292 | animationEnd: number; 293 | animationLast: number; 294 | nextAnimationLast: number; 295 | delay: number; 296 | trackTime: number; 297 | trackLast: number; 298 | nextTrackLast: number; 299 | trackEnd: number; 300 | timeScale: number; 301 | alpha: number; 302 | mixTime: number; 303 | mixDuration: number; 304 | interruptAlpha: number; 305 | totalAlpha: number; 306 | timelineData: number[]; 307 | timelineDipMix: TrackEntry[]; 308 | timelinesRotation: number[]; 309 | reset(): void; 310 | setTimelineData(to: TrackEntry, mixingToArray: Array, propertyIDs: IntSet): TrackEntry; 311 | hasTimeline(id: number): boolean; 312 | getAnimationTime(): number; 313 | setAnimationLast(animationLast: number): void; 314 | isComplete(): boolean; 315 | resetRotationDirections(): void; 316 | } 317 | class EventQueue { 318 | objects: Array; 319 | drainDisabled: boolean; 320 | animState: AnimationState; 321 | constructor(animState: AnimationState); 322 | start(entry: TrackEntry): void; 323 | interrupt(entry: TrackEntry): void; 324 | end(entry: TrackEntry): void; 325 | dispose(entry: TrackEntry): void; 326 | complete(entry: TrackEntry): void; 327 | event(entry: TrackEntry, event: Event): void; 328 | drain(): void; 329 | clear(): void; 330 | } 331 | enum EventType { 332 | start = 0, 333 | interrupt = 1, 334 | end = 2, 335 | dispose = 3, 336 | complete = 4, 337 | event = 5, 338 | } 339 | interface AnimationStateListener2 { 340 | start(entry: TrackEntry): void; 341 | interrupt(entry: TrackEntry): void; 342 | end(entry: TrackEntry): void; 343 | dispose(entry: TrackEntry): void; 344 | complete(entry: TrackEntry): void; 345 | event(entry: TrackEntry, event: Event): void; 346 | } 347 | abstract class AnimationStateAdapter2 implements AnimationStateListener2 { 348 | start(entry: TrackEntry): void; 349 | interrupt(entry: TrackEntry): void; 350 | end(entry: TrackEntry): void; 351 | dispose(entry: TrackEntry): void; 352 | complete(entry: TrackEntry): void; 353 | event(entry: TrackEntry, event: Event): void; 354 | } 355 | } 356 | declare module spine { 357 | class AnimationStateData { 358 | skeletonData: SkeletonData; 359 | animationToMixTime: Map; 360 | defaultMix: number; 361 | constructor(skeletonData: SkeletonData); 362 | setMix(fromName: string, toName: string, duration: number): void; 363 | setMixWith(from: Animation, to: Animation, duration: number): void; 364 | getMix(from: Animation, to: Animation): number; 365 | } 366 | } 367 | declare module spine { 368 | class AssetManager implements Disposable { 369 | private pathPrefix; 370 | private textureLoader; 371 | private assets; 372 | private errors; 373 | private toLoad; 374 | private loaded; 375 | constructor(textureLoader: (image: HTMLImageElement) => any, pathPrefix?: string); 376 | private static downloadText(url, success, error); 377 | private static downloadBinary(url, success, error); 378 | loadText(path: string, success?: (path: string, text: string) => void, error?: (path: string, error: string) => void): void; 379 | loadTexture(path: string, success?: (path: string, image: HTMLImageElement) => void, error?: (path: string, error: string) => void): void; 380 | loadTextureData(path: string, data: string, success?: (path: string, image: HTMLImageElement) => void, error?: (path: string, error: string) => void): void; 381 | loadTextureAtlas(path: string, success?: (path: string, atlas: TextureAtlas) => void, error?: (path: string, error: string) => void): void; 382 | get(path: string): any; 383 | remove(path: string): void; 384 | removeAll(): void; 385 | isLoadingComplete(): boolean; 386 | getToLoad(): number; 387 | getLoaded(): number; 388 | dispose(): void; 389 | hasErrors(): boolean; 390 | getErrors(): Map; 391 | } 392 | } 393 | declare module spine { 394 | class AtlasAttachmentLoader implements AttachmentLoader { 395 | atlas: TextureAtlas; 396 | constructor(atlas: TextureAtlas); 397 | newRegionAttachment(skin: Skin, name: string, path: string): RegionAttachment; 398 | newMeshAttachment(skin: Skin, name: string, path: string): MeshAttachment; 399 | newBoundingBoxAttachment(skin: Skin, name: string): BoundingBoxAttachment; 400 | newPathAttachment(skin: Skin, name: string): PathAttachment; 401 | newPointAttachment(skin: Skin, name: string): PointAttachment; 402 | newClippingAttachment(skin: Skin, name: string): ClippingAttachment; 403 | } 404 | } 405 | declare module spine { 406 | enum BlendMode { 407 | Normal = 0, 408 | Additive = 1, 409 | Multiply = 2, 410 | Screen = 3, 411 | } 412 | } 413 | declare module spine { 414 | class Bone implements Updatable { 415 | data: BoneData; 416 | skeleton: Skeleton; 417 | parent: Bone; 418 | children: Bone[]; 419 | x: number; 420 | y: number; 421 | rotation: number; 422 | scaleX: number; 423 | scaleY: number; 424 | shearX: number; 425 | shearY: number; 426 | ax: number; 427 | ay: number; 428 | arotation: number; 429 | ascaleX: number; 430 | ascaleY: number; 431 | ashearX: number; 432 | ashearY: number; 433 | appliedValid: boolean; 434 | a: number; 435 | b: number; 436 | worldX: number; 437 | c: number; 438 | d: number; 439 | worldY: number; 440 | sorted: boolean; 441 | constructor(data: BoneData, skeleton: Skeleton, parent: Bone); 442 | update(): void; 443 | updateWorldTransform(): void; 444 | updateWorldTransformWith(x: number, y: number, rotation: number, scaleX: number, scaleY: number, shearX: number, shearY: number): void; 445 | setToSetupPose(): void; 446 | getWorldRotationX(): number; 447 | getWorldRotationY(): number; 448 | getWorldScaleX(): number; 449 | getWorldScaleY(): number; 450 | updateAppliedTransform(): void; 451 | worldToLocal(world: Vector2): Vector2; 452 | localToWorld(local: Vector2): Vector2; 453 | worldToLocalRotation(worldRotation: number): number; 454 | localToWorldRotation(localRotation: number): number; 455 | rotateWorld(degrees: number): void; 456 | } 457 | } 458 | declare module spine { 459 | class BoneData { 460 | index: number; 461 | name: string; 462 | parent: BoneData; 463 | length: number; 464 | x: number; 465 | y: number; 466 | rotation: number; 467 | scaleX: number; 468 | scaleY: number; 469 | shearX: number; 470 | shearY: number; 471 | transformMode: TransformMode; 472 | constructor(index: number, name: string, parent: BoneData); 473 | } 474 | enum TransformMode { 475 | Normal = 0, 476 | OnlyTranslation = 1, 477 | NoRotationOrReflection = 2, 478 | NoScale = 3, 479 | NoScaleOrReflection = 4, 480 | } 481 | } 482 | declare module spine { 483 | interface Constraint extends Updatable { 484 | getOrder(): number; 485 | } 486 | } 487 | declare module spine { 488 | class Event { 489 | data: EventData; 490 | intValue: number; 491 | floatValue: number; 492 | stringValue: string; 493 | time: number; 494 | constructor(time: number, data: EventData); 495 | } 496 | } 497 | declare module spine { 498 | class EventData { 499 | name: string; 500 | intValue: number; 501 | floatValue: number; 502 | stringValue: string; 503 | constructor(name: string); 504 | } 505 | } 506 | declare module spine { 507 | class IkConstraint implements Constraint { 508 | data: IkConstraintData; 509 | bones: Array; 510 | target: Bone; 511 | mix: number; 512 | bendDirection: number; 513 | constructor(data: IkConstraintData, skeleton: Skeleton); 514 | getOrder(): number; 515 | apply(): void; 516 | update(): void; 517 | apply1(bone: Bone, targetX: number, targetY: number, alpha: number): void; 518 | apply2(parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, alpha: number): void; 519 | } 520 | } 521 | declare module spine { 522 | class IkConstraintData { 523 | name: string; 524 | order: number; 525 | bones: BoneData[]; 526 | target: BoneData; 527 | bendDirection: number; 528 | mix: number; 529 | constructor(name: string); 530 | } 531 | } 532 | declare module spine { 533 | class PathConstraint implements Constraint { 534 | static NONE: number; 535 | static BEFORE: number; 536 | static AFTER: number; 537 | static epsilon: number; 538 | data: PathConstraintData; 539 | bones: Array; 540 | target: Slot; 541 | position: number; 542 | spacing: number; 543 | rotateMix: number; 544 | translateMix: number; 545 | spaces: number[]; 546 | positions: number[]; 547 | world: number[]; 548 | curves: number[]; 549 | lengths: number[]; 550 | segments: number[]; 551 | constructor(data: PathConstraintData, skeleton: Skeleton); 552 | apply(): void; 553 | update(): void; 554 | computeWorldPositions(path: PathAttachment, spacesCount: number, tangents: boolean, percentPosition: boolean, percentSpacing: boolean): number[]; 555 | addBeforePosition(p: number, temp: Array, i: number, out: Array, o: number): void; 556 | addAfterPosition(p: number, temp: Array, i: number, out: Array, o: number): void; 557 | addCurvePosition(p: number, x1: number, y1: number, cx1: number, cy1: number, cx2: number, cy2: number, x2: number, y2: number, out: Array, o: number, tangents: boolean): void; 558 | getOrder(): number; 559 | } 560 | } 561 | declare module spine { 562 | class PathConstraintData { 563 | name: string; 564 | order: number; 565 | bones: BoneData[]; 566 | target: SlotData; 567 | positionMode: PositionMode; 568 | spacingMode: SpacingMode; 569 | rotateMode: RotateMode; 570 | offsetRotation: number; 571 | position: number; 572 | spacing: number; 573 | rotateMix: number; 574 | translateMix: number; 575 | constructor(name: string); 576 | } 577 | enum PositionMode { 578 | Fixed = 0, 579 | Percent = 1, 580 | } 581 | enum SpacingMode { 582 | Length = 0, 583 | Fixed = 1, 584 | Percent = 2, 585 | } 586 | enum RotateMode { 587 | Tangent = 0, 588 | Chain = 1, 589 | ChainScale = 2, 590 | } 591 | } 592 | declare module spine { 593 | class SharedAssetManager implements Disposable { 594 | private pathPrefix; 595 | private clientAssets; 596 | private queuedAssets; 597 | private rawAssets; 598 | private errors; 599 | constructor(pathPrefix?: string); 600 | private queueAsset(clientId, textureLoader, path); 601 | loadText(clientId: string, path: string): void; 602 | loadJson(clientId: string, path: string): void; 603 | loadTexture(clientId: string, textureLoader: (image: HTMLImageElement) => any, path: string): void; 604 | get(clientId: string, path: string): any; 605 | private updateClientAssets(clientAssets); 606 | isLoadingComplete(clientId: string): boolean; 607 | dispose(): void; 608 | hasErrors(): boolean; 609 | getErrors(): Map; 610 | } 611 | } 612 | declare module spine { 613 | class Skeleton { 614 | data: SkeletonData; 615 | bones: Array; 616 | slots: Array; 617 | drawOrder: Array; 618 | ikConstraints: Array; 619 | transformConstraints: Array; 620 | pathConstraints: Array; 621 | _updateCache: Updatable[]; 622 | updateCacheReset: Updatable[]; 623 | skin: Skin; 624 | color: Color; 625 | time: number; 626 | flipX: boolean; 627 | flipY: boolean; 628 | x: number; 629 | y: number; 630 | constructor(data: SkeletonData); 631 | updateCache(): void; 632 | sortIkConstraint(constraint: IkConstraint): void; 633 | sortPathConstraint(constraint: PathConstraint): void; 634 | sortTransformConstraint(constraint: TransformConstraint): void; 635 | sortPathConstraintAttachment(skin: Skin, slotIndex: number, slotBone: Bone): void; 636 | sortPathConstraintAttachmentWith(attachment: Attachment, slotBone: Bone): void; 637 | sortBone(bone: Bone): void; 638 | sortReset(bones: Array): void; 639 | updateWorldTransform(): void; 640 | setToSetupPose(): void; 641 | setBonesToSetupPose(): void; 642 | setSlotsToSetupPose(): void; 643 | getRootBone(): Bone; 644 | findBone(boneName: string): Bone; 645 | findBoneIndex(boneName: string): number; 646 | findSlot(slotName: string): Slot; 647 | findSlotIndex(slotName: string): number; 648 | setSkinByName(skinName: string): void; 649 | setSkin(newSkin: Skin): void; 650 | getAttachmentByName(slotName: string, attachmentName: string): Attachment; 651 | getAttachment(slotIndex: number, attachmentName: string): Attachment; 652 | setAttachment(slotName: string, attachmentName: string): void; 653 | findIkConstraint(constraintName: string): IkConstraint; 654 | findTransformConstraint(constraintName: string): TransformConstraint; 655 | findPathConstraint(constraintName: string): PathConstraint; 656 | getBounds(offset: Vector2, size: Vector2, temp: Array): void; 657 | update(delta: number): void; 658 | } 659 | } 660 | declare module spine { 661 | class SkeletonBounds { 662 | minX: number; 663 | minY: number; 664 | maxX: number; 665 | maxY: number; 666 | boundingBoxes: BoundingBoxAttachment[]; 667 | polygons: ArrayLike[]; 668 | private polygonPool; 669 | update(skeleton: Skeleton, updateAabb: boolean): void; 670 | aabbCompute(): void; 671 | aabbContainsPoint(x: number, y: number): boolean; 672 | aabbIntersectsSegment(x1: number, y1: number, x2: number, y2: number): boolean; 673 | aabbIntersectsSkeleton(bounds: SkeletonBounds): boolean; 674 | containsPoint(x: number, y: number): BoundingBoxAttachment; 675 | containsPointPolygon(polygon: ArrayLike, x: number, y: number): boolean; 676 | intersectsSegment(x1: number, y1: number, x2: number, y2: number): BoundingBoxAttachment; 677 | intersectsSegmentPolygon(polygon: ArrayLike, x1: number, y1: number, x2: number, y2: number): boolean; 678 | getPolygon(boundingBox: BoundingBoxAttachment): ArrayLike; 679 | getWidth(): number; 680 | getHeight(): number; 681 | } 682 | } 683 | declare module spine { 684 | class SkeletonClipping { 685 | private triangulator; 686 | private clippingPolygon; 687 | private clipOutput; 688 | clippedVertices: number[]; 689 | clippedTriangles: number[]; 690 | private scratch; 691 | private clipAttachment; 692 | private clippingPolygons; 693 | clipStart(slot: Slot, clip: ClippingAttachment): number; 694 | clipEndWithSlot(slot: Slot): void; 695 | clipEnd(): void; 696 | isClipping(): boolean; 697 | clipTriangles(vertices: ArrayLike, verticesLength: number, triangles: ArrayLike, trianglesLength: number, uvs: ArrayLike, light: Color, dark: Color, twoColor: boolean): void; 698 | clip(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, clippingArea: Array, output: Array): boolean; 699 | static makeClockwise(polygon: ArrayLike): void; 700 | } 701 | } 702 | declare module spine { 703 | class SkeletonData { 704 | name: string; 705 | bones: BoneData[]; 706 | slots: SlotData[]; 707 | skins: Skin[]; 708 | defaultSkin: Skin; 709 | events: EventData[]; 710 | animations: Animation[]; 711 | ikConstraints: IkConstraintData[]; 712 | transformConstraints: TransformConstraintData[]; 713 | pathConstraints: PathConstraintData[]; 714 | width: number; 715 | height: number; 716 | version: string; 717 | hash: string; 718 | fps: number; 719 | imagesPath: string; 720 | findBone(boneName: string): BoneData; 721 | findBoneIndex(boneName: string): number; 722 | findSlot(slotName: string): SlotData; 723 | findSlotIndex(slotName: string): number; 724 | findSkin(skinName: string): Skin; 725 | findEvent(eventDataName: string): EventData; 726 | findAnimation(animationName: string): Animation; 727 | findIkConstraint(constraintName: string): IkConstraintData; 728 | findTransformConstraint(constraintName: string): TransformConstraintData; 729 | findPathConstraint(constraintName: string): PathConstraintData; 730 | findPathConstraintIndex(pathConstraintName: string): number; 731 | } 732 | } 733 | declare module spine { 734 | class SkeletonJson { 735 | attachmentLoader: AttachmentLoader; 736 | scale: number; 737 | private linkedMeshes; 738 | constructor(attachmentLoader: AttachmentLoader); 739 | readSkeletonData(json: string | any): SkeletonData; 740 | readAttachment(map: any, skin: Skin, slotIndex: number, name: string, skeletonData: SkeletonData): Attachment; 741 | readVertices(map: any, attachment: VertexAttachment, verticesLength: number): void; 742 | readAnimation(map: any, name: string, skeletonData: SkeletonData): void; 743 | readCurve(map: any, timeline: CurveTimeline, frameIndex: number): void; 744 | getValue(map: any, prop: string, defaultValue: any): any; 745 | static blendModeFromString(str: string): BlendMode; 746 | static positionModeFromString(str: string): PositionMode; 747 | static spacingModeFromString(str: string): SpacingMode; 748 | static rotateModeFromString(str: string): RotateMode; 749 | static transformModeFromString(str: string): TransformMode; 750 | } 751 | } 752 | declare module spine { 753 | class Skin { 754 | name: string; 755 | attachments: Map[]; 756 | constructor(name: string); 757 | addAttachment(slotIndex: number, name: string, attachment: Attachment): void; 758 | getAttachment(slotIndex: number, name: string): Attachment; 759 | attachAll(skeleton: Skeleton, oldSkin: Skin): void; 760 | } 761 | } 762 | declare module spine { 763 | class Slot { 764 | data: SlotData; 765 | bone: Bone; 766 | color: Color; 767 | darkColor: Color; 768 | private attachment; 769 | private attachmentTime; 770 | attachmentVertices: number[]; 771 | constructor(data: SlotData, bone: Bone); 772 | getAttachment(): Attachment; 773 | setAttachment(attachment: Attachment): void; 774 | setAttachmentTime(time: number): void; 775 | getAttachmentTime(): number; 776 | setToSetupPose(): void; 777 | } 778 | } 779 | declare module spine { 780 | class SlotData { 781 | index: number; 782 | name: string; 783 | boneData: BoneData; 784 | color: Color; 785 | darkColor: Color; 786 | attachmentName: string; 787 | blendMode: BlendMode; 788 | constructor(index: number, name: string, boneData: BoneData); 789 | } 790 | } 791 | declare module spine { 792 | abstract class Texture { 793 | protected _image: HTMLImageElement; 794 | constructor(image: HTMLImageElement); 795 | getImage(): HTMLImageElement; 796 | abstract setFilters(minFilter: TextureFilter, magFilter: TextureFilter): void; 797 | abstract setWraps(uWrap: TextureWrap, vWrap: TextureWrap): void; 798 | abstract dispose(): void; 799 | static filterFromString(text: string): TextureFilter; 800 | static wrapFromString(text: string): TextureWrap; 801 | } 802 | enum TextureFilter { 803 | Nearest = 9728, 804 | Linear = 9729, 805 | MipMap = 9987, 806 | MipMapNearestNearest = 9984, 807 | MipMapLinearNearest = 9985, 808 | MipMapNearestLinear = 9986, 809 | MipMapLinearLinear = 9987, 810 | } 811 | enum TextureWrap { 812 | MirroredRepeat = 33648, 813 | ClampToEdge = 33071, 814 | Repeat = 10497, 815 | } 816 | class TextureRegion { 817 | renderObject: any; 818 | u: number; 819 | v: number; 820 | u2: number; 821 | v2: number; 822 | width: number; 823 | height: number; 824 | rotate: boolean; 825 | offsetX: number; 826 | offsetY: number; 827 | originalWidth: number; 828 | originalHeight: number; 829 | } 830 | class FakeTexture extends spine.Texture { 831 | setFilters(minFilter: spine.TextureFilter, magFilter: spine.TextureFilter): void; 832 | setWraps(uWrap: spine.TextureWrap, vWrap: spine.TextureWrap): void; 833 | dispose(): void; 834 | } 835 | } 836 | declare module spine { 837 | class TextureAtlas implements Disposable { 838 | pages: TextureAtlasPage[]; 839 | regions: TextureAtlasRegion[]; 840 | constructor(atlasText: string, textureLoader: (path: string) => any); 841 | private load(atlasText, textureLoader); 842 | findRegion(name: string): TextureAtlasRegion; 843 | dispose(): void; 844 | } 845 | class TextureAtlasPage { 846 | name: string; 847 | minFilter: TextureFilter; 848 | magFilter: TextureFilter; 849 | uWrap: TextureWrap; 850 | vWrap: TextureWrap; 851 | texture: Texture; 852 | width: number; 853 | height: number; 854 | } 855 | class TextureAtlasRegion extends TextureRegion { 856 | page: TextureAtlasPage; 857 | name: string; 858 | x: number; 859 | y: number; 860 | index: number; 861 | rotate: boolean; 862 | texture: Texture; 863 | } 864 | } 865 | declare module spine { 866 | class TransformConstraint implements Constraint { 867 | data: TransformConstraintData; 868 | bones: Array; 869 | target: Bone; 870 | rotateMix: number; 871 | translateMix: number; 872 | scaleMix: number; 873 | shearMix: number; 874 | temp: Vector2; 875 | constructor(data: TransformConstraintData, skeleton: Skeleton); 876 | apply(): void; 877 | update(): void; 878 | applyAbsoluteWorld(): void; 879 | applyRelativeWorld(): void; 880 | applyAbsoluteLocal(): void; 881 | applyRelativeLocal(): void; 882 | getOrder(): number; 883 | } 884 | } 885 | declare module spine { 886 | class TransformConstraintData { 887 | name: string; 888 | order: number; 889 | bones: BoneData[]; 890 | target: BoneData; 891 | rotateMix: number; 892 | translateMix: number; 893 | scaleMix: number; 894 | shearMix: number; 895 | offsetRotation: number; 896 | offsetX: number; 897 | offsetY: number; 898 | offsetScaleX: number; 899 | offsetScaleY: number; 900 | offsetShearY: number; 901 | relative: boolean; 902 | local: boolean; 903 | constructor(name: string); 904 | } 905 | } 906 | declare module spine { 907 | class Triangulator { 908 | private convexPolygons; 909 | private convexPolygonsIndices; 910 | private indicesArray; 911 | private isConcaveArray; 912 | private triangles; 913 | private polygonPool; 914 | private polygonIndicesPool; 915 | triangulate(verticesArray: ArrayLike): Array; 916 | decompose(verticesArray: Array, triangles: Array): Array>; 917 | private static isConcave(index, vertexCount, vertices, indices); 918 | private static positiveArea(p1x, p1y, p2x, p2y, p3x, p3y); 919 | private static winding(p1x, p1y, p2x, p2y, p3x, p3y); 920 | } 921 | } 922 | declare module spine { 923 | interface Updatable { 924 | update(): void; 925 | } 926 | } 927 | declare module spine { 928 | interface Map { 929 | [key: string]: T; 930 | } 931 | class IntSet { 932 | array: number[]; 933 | add(value: number): boolean; 934 | contains(value: number): boolean; 935 | remove(value: number): void; 936 | clear(): void; 937 | } 938 | interface Disposable { 939 | dispose(): void; 940 | } 941 | interface Restorable { 942 | restore(): void; 943 | } 944 | class Color { 945 | r: number; 946 | g: number; 947 | b: number; 948 | a: number; 949 | static WHITE: Color; 950 | static RED: Color; 951 | static GREEN: Color; 952 | static BLUE: Color; 953 | static MAGENTA: Color; 954 | constructor(r?: number, g?: number, b?: number, a?: number); 955 | set(r: number, g: number, b: number, a: number): this; 956 | setFromColor(c: Color): this; 957 | setFromString(hex: string): this; 958 | add(r: number, g: number, b: number, a: number): this; 959 | clamp(): this; 960 | } 961 | class MathUtils { 962 | static PI: number; 963 | static PI2: number; 964 | static radiansToDegrees: number; 965 | static radDeg: number; 966 | static degreesToRadians: number; 967 | static degRad: number; 968 | static clamp(value: number, min: number, max: number): number; 969 | static cosDeg(degrees: number): number; 970 | static sinDeg(degrees: number): number; 971 | static signum(value: number): number; 972 | static toInt(x: number): number; 973 | static cbrt(x: number): number; 974 | static randomTriangular(min: number, max: number): number; 975 | static randomTriangularWith(min: number, max: number, mode: number): number; 976 | } 977 | abstract class Interpolation { 978 | protected abstract applyInternal(a: number): number; 979 | apply(start: number, end: number, a: number): number; 980 | } 981 | class Pow extends Interpolation { 982 | protected power: number; 983 | constructor(power: number); 984 | applyInternal(a: number): number; 985 | } 986 | class PowOut extends Pow { 987 | constructor(power: number); 988 | applyInternal(a: number): number; 989 | } 990 | class Utils { 991 | static SUPPORTS_TYPED_ARRAYS: boolean; 992 | static arrayCopy(source: ArrayLike, sourceStart: number, dest: ArrayLike, destStart: number, numElements: number): void; 993 | static setArraySize(array: Array, size: number, value?: any): Array; 994 | static ensureArrayCapacity(array: Array, size: number, value?: any): Array; 995 | static newArray(size: number, defaultValue: T): Array; 996 | static newFloatArray(size: number): ArrayLike; 997 | static newShortArray(size: number): ArrayLike; 998 | static toFloatArray(array: Array): number[] | Float32Array; 999 | static toSinglePrecision(value: number): number; 1000 | static webkit602BugfixHelper(alpha: number, pose: MixPose): void; 1001 | } 1002 | class DebugUtils { 1003 | static logBones(skeleton: Skeleton): void; 1004 | } 1005 | class Pool { 1006 | private items; 1007 | private instantiator; 1008 | constructor(instantiator: () => T); 1009 | obtain(): T; 1010 | free(item: T): void; 1011 | freeAll(items: ArrayLike): void; 1012 | clear(): void; 1013 | } 1014 | class Vector2 { 1015 | x: number; 1016 | y: number; 1017 | constructor(x?: number, y?: number); 1018 | set(x: number, y: number): Vector2; 1019 | length(): number; 1020 | normalize(): this; 1021 | } 1022 | class TimeKeeper { 1023 | maxDelta: number; 1024 | framesPerSecond: number; 1025 | delta: number; 1026 | totalTime: number; 1027 | private lastTime; 1028 | private frameCount; 1029 | private frameTime; 1030 | update(): void; 1031 | } 1032 | interface ArrayLike { 1033 | length: number; 1034 | [n: number]: T; 1035 | } 1036 | class WindowedMean { 1037 | values: Array; 1038 | addedValues: number; 1039 | lastValue: number; 1040 | mean: number; 1041 | dirty: boolean; 1042 | constructor(windowSize?: number); 1043 | hasEnoughData(): boolean; 1044 | addValue(value: number): void; 1045 | getMean(): number; 1046 | } 1047 | } 1048 | declare module spine { 1049 | interface VertexEffect { 1050 | begin(skeleton: Skeleton): void; 1051 | transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void; 1052 | end(): void; 1053 | } 1054 | } 1055 | interface Math { 1056 | fround(n: number): number; 1057 | } 1058 | declare module spine { 1059 | abstract class Attachment { 1060 | name: string; 1061 | constructor(name: string); 1062 | } 1063 | abstract class VertexAttachment extends Attachment { 1064 | private static nextID; 1065 | id: number; 1066 | bones: Array; 1067 | vertices: ArrayLike; 1068 | worldVerticesLength: number; 1069 | constructor(name: string); 1070 | computeWorldVertices(slot: Slot, start: number, count: number, worldVertices: ArrayLike, offset: number, stride: number): void; 1071 | applyDeform(sourceAttachment: VertexAttachment): boolean; 1072 | } 1073 | } 1074 | declare module spine { 1075 | interface AttachmentLoader { 1076 | newRegionAttachment(skin: Skin, name: string, path: string): RegionAttachment; 1077 | newMeshAttachment(skin: Skin, name: string, path: string): MeshAttachment; 1078 | newBoundingBoxAttachment(skin: Skin, name: string): BoundingBoxAttachment; 1079 | newPathAttachment(skin: Skin, name: string): PathAttachment; 1080 | newPointAttachment(skin: Skin, name: string): PointAttachment; 1081 | newClippingAttachment(skin: Skin, name: string): ClippingAttachment; 1082 | } 1083 | } 1084 | declare module spine { 1085 | enum AttachmentType { 1086 | Region = 0, 1087 | BoundingBox = 1, 1088 | Mesh = 2, 1089 | LinkedMesh = 3, 1090 | Path = 4, 1091 | Point = 5, 1092 | } 1093 | } 1094 | declare module spine { 1095 | class BoundingBoxAttachment extends VertexAttachment { 1096 | color: Color; 1097 | constructor(name: string); 1098 | } 1099 | } 1100 | declare module spine { 1101 | class ClippingAttachment extends VertexAttachment { 1102 | endSlot: SlotData; 1103 | color: Color; 1104 | constructor(name: string); 1105 | } 1106 | } 1107 | declare module spine { 1108 | class MeshAttachment extends VertexAttachment { 1109 | region: TextureRegion; 1110 | path: string; 1111 | regionUVs: ArrayLike; 1112 | uvs: ArrayLike; 1113 | triangles: Array; 1114 | color: Color; 1115 | hullLength: number; 1116 | private parentMesh; 1117 | inheritDeform: boolean; 1118 | tempColor: Color; 1119 | constructor(name: string); 1120 | updateUVs(): void; 1121 | applyDeform(sourceAttachment: VertexAttachment): boolean; 1122 | getParentMesh(): MeshAttachment; 1123 | setParentMesh(parentMesh: MeshAttachment): void; 1124 | } 1125 | } 1126 | declare module spine { 1127 | class PathAttachment extends VertexAttachment { 1128 | lengths: Array; 1129 | closed: boolean; 1130 | constantSpeed: boolean; 1131 | color: Color; 1132 | constructor(name: string); 1133 | } 1134 | } 1135 | declare module spine { 1136 | class PointAttachment extends VertexAttachment { 1137 | x: number; 1138 | y: number; 1139 | rotation: number; 1140 | color: Color; 1141 | constructor(name: string); 1142 | computeWorldPosition(bone: Bone, point: Vector2): Vector2; 1143 | computeWorldRotation(bone: Bone): number; 1144 | } 1145 | } 1146 | declare module spine { 1147 | class RegionAttachment extends Attachment { 1148 | static OX1: number; 1149 | static OY1: number; 1150 | static OX2: number; 1151 | static OY2: number; 1152 | static OX3: number; 1153 | static OY3: number; 1154 | static OX4: number; 1155 | static OY4: number; 1156 | static X1: number; 1157 | static Y1: number; 1158 | static C1R: number; 1159 | static C1G: number; 1160 | static C1B: number; 1161 | static C1A: number; 1162 | static U1: number; 1163 | static V1: number; 1164 | static X2: number; 1165 | static Y2: number; 1166 | static C2R: number; 1167 | static C2G: number; 1168 | static C2B: number; 1169 | static C2A: number; 1170 | static U2: number; 1171 | static V2: number; 1172 | static X3: number; 1173 | static Y3: number; 1174 | static C3R: number; 1175 | static C3G: number; 1176 | static C3B: number; 1177 | static C3A: number; 1178 | static U3: number; 1179 | static V3: number; 1180 | static X4: number; 1181 | static Y4: number; 1182 | static C4R: number; 1183 | static C4G: number; 1184 | static C4B: number; 1185 | static C4A: number; 1186 | static U4: number; 1187 | static V4: number; 1188 | x: number; 1189 | y: number; 1190 | scaleX: number; 1191 | scaleY: number; 1192 | rotation: number; 1193 | width: number; 1194 | height: number; 1195 | color: Color; 1196 | path: string; 1197 | rendererObject: any; 1198 | region: TextureRegion; 1199 | offset: ArrayLike; 1200 | uvs: ArrayLike; 1201 | tempColor: Color; 1202 | constructor(name: string); 1203 | updateOffset(): void; 1204 | setRegion(region: TextureRegion): void; 1205 | computeWorldVertices(bone: Bone, worldVertices: ArrayLike, offset: number, stride: number): void; 1206 | } 1207 | } 1208 | declare module spine { 1209 | class JitterEffect implements VertexEffect { 1210 | jitterX: number; 1211 | jitterY: number; 1212 | constructor(jitterX: number, jitterY: number); 1213 | begin(skeleton: Skeleton): void; 1214 | transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void; 1215 | end(): void; 1216 | } 1217 | } 1218 | declare module spine { 1219 | class SwirlEffect implements VertexEffect { 1220 | static interpolation: PowOut; 1221 | centerX: number; 1222 | centerY: number; 1223 | radius: number; 1224 | angle: number; 1225 | private worldX; 1226 | private worldY; 1227 | constructor(radius: number); 1228 | begin(skeleton: Skeleton): void; 1229 | transform(position: Vector2, uv: Vector2, light: Color, dark: Color): void; 1230 | end(): void; 1231 | } 1232 | } 1233 | -------------------------------------------------------------------------------- /examples/alien36.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PlayCanvas Spine - Dragon 5 | 6 | 7 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /examples/assets/spine36/README.md: -------------------------------------------------------------------------------- 1 | All the example files in this folder are Copyright Esoteric Software. They are provided as examples and test cases for the PlayCanvas plugin and not for use in projects. 2 | -------------------------------------------------------------------------------- /examples/assets/spine36/alien-pma.atlas: -------------------------------------------------------------------------------- 1 | 2 | alien-pma.png 3 | size: 1024,1024 4 | format: RGBA8888 5 | filter: Linear,Linear 6 | repeat: none 7 | back-foot 8 | rotate: true 9 | xy: 1015, 1014 10 | size: 8, 6 11 | orig: 8, 6 12 | offset: 0, 0 13 | index: -1 14 | back-shin 15 | rotate: false 16 | xy: 209, 24 17 | size: 21, 24 18 | orig: 21, 24 19 | offset: 0, 0 20 | index: -1 21 | back-thigh 22 | rotate: false 23 | xy: 313, 378 24 | size: 24, 24 25 | orig: 24, 24 26 | offset: 0, 0 27 | index: -1 28 | backarmor 29 | rotate: false 30 | xy: 2, 2 31 | size: 81, 91 32 | orig: 81, 91 33 | offset: 0, 0 34 | index: -1 35 | blown-up-neck 36 | rotate: false 37 | xy: 260, 404 38 | size: 77, 52 39 | orig: 77, 52 40 | offset: 0, 0 41 | index: -1 42 | body 43 | rotate: false 44 | xy: 260, 526 45 | size: 98, 118 46 | orig: 98, 118 47 | offset: 0, 0 48 | index: -1 49 | burst01 50 | rotate: true 51 | xy: 706, 650 52 | size: 143, 155 53 | orig: 143, 155 54 | offset: 0, 0 55 | index: -1 56 | burst02 57 | rotate: false 58 | xy: 548, 669 59 | size: 156, 181 60 | orig: 156, 181 61 | offset: 0, 0 62 | index: -1 63 | burst03-bg 64 | rotate: false 65 | xy: 707, 795 66 | size: 276, 227 67 | orig: 276, 227 68 | offset: 0, 0 69 | index: -1 70 | burst03-fg 71 | rotate: false 72 | xy: 2, 723 73 | size: 339, 299 74 | orig: 339, 299 75 | offset: 0, 0 76 | index: -1 77 | eye 78 | rotate: false 79 | xy: 260, 646 80 | size: 75, 75 81 | orig: 75, 75 82 | offset: 0, 0 83 | index: -1 84 | eye-highlight 85 | rotate: false 86 | xy: 394, 665 87 | size: 22, 22 88 | orig: 22, 22 89 | offset: 0, 0 90 | index: -1 91 | eye-pupil 92 | rotate: false 93 | xy: 423, 698 94 | size: 18, 18 95 | orig: 18, 18 96 | offset: 0, 0 97 | index: -1 98 | eye-stalk 99 | rotate: false 100 | xy: 260, 458 101 | size: 79, 66 102 | orig: 79, 66 103 | offset: 0, 0 104 | index: -1 105 | front-foot 106 | rotate: false 107 | xy: 341, 518 108 | size: 10, 6 109 | orig: 10, 6 110 | offset: 0, 0 111 | index: -1 112 | front-lower-arm 113 | rotate: false 114 | xy: 337, 653 115 | size: 55, 63 116 | orig: 55, 63 117 | offset: 0, 0 118 | index: -1 119 | front-shin 120 | rotate: false 121 | xy: 985, 935 122 | size: 25, 28 123 | orig: 25, 28 124 | offset: 0, 0 125 | index: -1 126 | front-thigh 127 | rotate: false 128 | xy: 394, 689 129 | size: 27, 27 130 | orig: 27, 27 131 | offset: 0, 0 132 | index: -1 133 | front-upper-arm 134 | rotate: true 135 | xy: 985, 965 136 | size: 57, 28 137 | orig: 57, 28 138 | offset: 0, 0 139 | index: -1 140 | head 141 | rotate: true 142 | xy: 863, 644 143 | size: 149, 136 144 | orig: 149, 136 145 | offset: 0, 0 146 | index: -1 147 | lower-back-arm 148 | rotate: false 149 | xy: 260, 356 150 | size: 51, 46 151 | orig: 51, 46 152 | offset: 0, 0 153 | index: -1 154 | metaljaw 155 | rotate: false 156 | xy: 85, 7 157 | size: 122, 86 158 | orig: 122, 86 159 | offset: 0, 0 160 | index: -1 161 | splat01 162 | rotate: true 163 | xy: 2, 95 164 | size: 257, 252 165 | orig: 257, 252 166 | offset: 0, 0 167 | index: -1 168 | splat01-fg 169 | rotate: false 170 | xy: 343, 718 171 | size: 203, 132 172 | orig: 203, 132 173 | offset: 0, 0 174 | index: -1 175 | splat02 176 | rotate: true 177 | xy: 2, 354 178 | size: 367, 256 179 | orig: 367, 256 180 | offset: 0, 0 181 | index: -1 182 | splat03 183 | rotate: false 184 | xy: 343, 852 185 | size: 362, 170 186 | orig: 362, 170 187 | offset: 0, 0 188 | index: -1 189 | upper-back-arm 190 | rotate: false 191 | xy: 209, 50 192 | size: 30, 43 193 | orig: 30, 43 194 | offset: 0, 0 195 | index: -1 196 | -------------------------------------------------------------------------------- /examples/assets/spine36/alien-pma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playcanvas/playcanvas-spine/9214f3043d35049f6efaa82c9ca54cd0bfe7917e/examples/assets/spine36/alien-pma.png -------------------------------------------------------------------------------- /examples/assets/spine36/dragon-pma.atlas: -------------------------------------------------------------------------------- 1 | 2 | dragon-pma.png 3 | size: 1024,1024 4 | format: RGBA8888 5 | filter: Linear,Linear 6 | repeat: none 7 | chin 8 | rotate: true 9 | xy: 856, 138 10 | size: 214, 146 11 | orig: 214, 146 12 | offset: 0, 0 13 | index: -1 14 | front-toe-a 15 | rotate: true 16 | xy: 584, 72 17 | size: 29, 50 18 | orig: 29, 50 19 | offset: 0, 0 20 | index: -1 21 | head 22 | rotate: false 23 | xy: 204, 756 24 | size: 296, 260 25 | orig: 296, 260 26 | offset: 0, 0 27 | index: -1 28 | left-front-leg 29 | rotate: false 30 | xy: 636, 355 31 | size: 84, 57 32 | orig: 84, 57 33 | offset: 0, 0 34 | index: -1 35 | left-rear-thigh 36 | rotate: true 37 | xy: 207, 12 38 | size: 91, 149 39 | orig: 91, 149 40 | offset: 0, 0 41 | index: -1 42 | left-wing01 43 | rotate: false 44 | xy: 443, 103 45 | size: 191, 256 46 | orig: 191, 256 47 | offset: 0, 0 48 | index: -1 49 | left-wing02 50 | rotate: true 51 | xy: 502, 616 52 | size: 179, 269 53 | orig: 179, 269 54 | offset: 0, 0 55 | index: -1 56 | left-wing04 57 | rotate: false 58 | xy: 636, 2 59 | size: 188, 135 60 | orig: 188, 135 61 | offset: 0, 0 62 | index: -1 63 | left-wing05 64 | rotate: false 65 | xy: 636, 139 66 | size: 218, 213 67 | orig: 218, 213 68 | offset: 0, 0 69 | index: -1 70 | left-wing06 71 | rotate: false 72 | xy: 2, 317 73 | size: 192, 331 74 | orig: 192, 331 75 | offset: 0, 0 76 | index: -1 77 | left-wing07 78 | rotate: true 79 | xy: 750, 354 80 | size: 159, 255 81 | orig: 159, 255 82 | offset: 0, 0 83 | index: -1 84 | right-rear-thigh 85 | rotate: true 86 | xy: 433, 10 87 | size: 91, 149 88 | orig: 91, 149 89 | offset: 0, 0 90 | index: -1 91 | right-wing01 92 | rotate: true 93 | xy: 502, 797 94 | size: 219, 310 95 | orig: 219, 310 96 | offset: 0, 0 97 | index: -1 98 | right-wing02 99 | rotate: false 100 | xy: 2, 10 101 | size: 203, 305 102 | orig: 203, 305 103 | offset: 0, 0 104 | index: -1 105 | right-wing03 106 | rotate: false 107 | xy: 196, 361 108 | size: 272, 247 109 | orig: 272, 247 110 | offset: 0, 0 111 | index: -1 112 | right-wing04 113 | rotate: false 114 | xy: 204, 610 115 | size: 279, 144 116 | orig: 279, 144 117 | offset: 0, 0 118 | index: -1 119 | right-wing05 120 | rotate: true 121 | xy: 773, 515 122 | size: 251, 229 123 | orig: 251, 229 124 | offset: 0, 0 125 | index: -1 126 | right-wing06 127 | rotate: false 128 | xy: 2, 650 129 | size: 200, 366 130 | orig: 200, 366 131 | offset: 0, 0 132 | index: -1 133 | right-wing07 134 | rotate: true 135 | xy: 485, 414 136 | size: 200, 263 137 | orig: 200, 263 138 | offset: 0, 0 139 | index: -1 140 | right-wing08 141 | rotate: false 142 | xy: 207, 105 143 | size: 234, 254 144 | orig: 234, 254 145 | offset: 0, 0 146 | index: -1 147 | right-wing09 148 | rotate: true 149 | xy: 814, 768 150 | size: 248, 204 151 | orig: 248, 204 152 | offset: 0, 0 153 | index: -1 154 | tail01 155 | rotate: true 156 | xy: 826, 16 157 | size: 120, 153 158 | orig: 120, 153 159 | offset: 0, 0 160 | index: -1 161 | tail03 162 | rotate: false 163 | xy: 358, 11 164 | size: 73, 92 165 | orig: 73, 92 166 | offset: 0, 0 167 | index: -1 168 | 169 | dragon-pma2.png 170 | size: 1024,512 171 | format: RGBA8888 172 | filter: Linear,Linear 173 | repeat: none 174 | back 175 | rotate: false 176 | xy: 396, 290 177 | size: 190, 185 178 | orig: 190, 185 179 | offset: 0, 0 180 | index: -1 181 | chest 182 | rotate: false 183 | xy: 754, 386 184 | size: 136, 122 185 | orig: 136, 122 186 | offset: 0, 0 187 | index: -1 188 | front-toe-b 189 | rotate: false 190 | xy: 934, 341 191 | size: 56, 57 192 | orig: 56, 57 193 | offset: 0, 0 194 | index: -1 195 | left-front-thigh 196 | rotate: true 197 | xy: 105, 7 198 | size: 84, 72 199 | orig: 84, 72 200 | offset: 0, 0 201 | index: -1 202 | left-rear-leg 203 | rotate: true 204 | xy: 2, 93 205 | size: 206, 177 206 | orig: 206, 177 207 | offset: 0, 0 208 | index: -1 209 | left-wing03 210 | rotate: false 211 | xy: 2, 301 212 | size: 186, 207 213 | orig: 186, 207 214 | offset: 0, 0 215 | index: -1 216 | left-wing08 217 | rotate: false 218 | xy: 588, 327 219 | size: 164, 181 220 | orig: 164, 181 221 | offset: 0, 0 222 | index: -1 223 | left-wing09 224 | rotate: false 225 | xy: 190, 308 226 | size: 204, 167 227 | orig: 204, 167 228 | offset: 0, 0 229 | index: -1 230 | right-front-leg 231 | rotate: false 232 | xy: 2, 2 233 | size: 101, 89 234 | orig: 101, 89 235 | offset: 0, 0 236 | index: -1 237 | right-front-thigh 238 | rotate: false 239 | xy: 892, 400 240 | size: 108, 108 241 | orig: 108, 108 242 | offset: 0, 0 243 | index: -1 244 | right-rear-leg 245 | rotate: false 246 | xy: 588, 225 247 | size: 116, 100 248 | orig: 116, 100 249 | offset: 0, 0 250 | index: -1 251 | right-rear-toe 252 | rotate: false 253 | xy: 706, 210 254 | size: 109, 77 255 | orig: 109, 77 256 | offset: 0, 0 257 | index: -1 258 | tail02 259 | rotate: true 260 | xy: 754, 289 261 | size: 95, 120 262 | orig: 95, 120 263 | offset: 0, 0 264 | index: -1 265 | tail04 266 | rotate: false 267 | xy: 876, 313 268 | size: 56, 71 269 | orig: 56, 71 270 | offset: 0, 0 271 | index: -1 272 | tail05 273 | rotate: true 274 | xy: 934, 287 275 | size: 52, 59 276 | orig: 52, 59 277 | offset: 0, 0 278 | index: -1 279 | tail06 280 | rotate: true 281 | xy: 817, 192 282 | size: 95, 68 283 | orig: 95, 68 284 | offset: 0, 0 285 | index: -1 286 | thiagobrayner 287 | rotate: false 288 | xy: 190, 477 289 | size: 350, 31 290 | orig: 350, 31 291 | offset: 0, 0 292 | index: -1 293 | -------------------------------------------------------------------------------- /examples/assets/spine36/dragon-pma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playcanvas/playcanvas-spine/9214f3043d35049f6efaa82c9ca54cd0bfe7917e/examples/assets/spine36/dragon-pma.png -------------------------------------------------------------------------------- /examples/assets/spine36/dragon-pma2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playcanvas/playcanvas-spine/9214f3043d35049f6efaa82c9ca54cd0bfe7917e/examples/assets/spine36/dragon-pma2.png -------------------------------------------------------------------------------- /examples/assets/spine36/dragon.json: -------------------------------------------------------------------------------- 1 | { 2 | "skeleton": { "hash": "fU1LyLo9U9jF7UZJrm+dkPTXbAY", "spine": "3.6.32", "width": 660.39, "height": 604.09, "images": "./images/" }, 3 | "bones": [ 4 | { "name": "root", "y": -176.12 }, 5 | { "name": "center", "parent": "root", "y": 176.12, "color": "ffe300ff" }, 6 | { "name": "back", "parent": "center", "length": 115.38, "rotation": 151.83, "x": 16.04, "y": 27.94, "color": "ffe400ff" }, 7 | { "name": "chest", "parent": "center", "length": 31.24, "rotation": 161.7, "x": 52.53, "y": 15.35, "color": "ffe400ff" }, 8 | { "name": "neck", "parent": "center", "length": 41.37, "rotation": 39.06, "x": 64.76, "y": 11.98, "color": "ffe400ff" }, 9 | { "name": "chin", "parent": "neck", "length": 153.16, "rotation": -69.07, "x": 64.63, "y": -6.99, "color": "ffe400ff" }, 10 | { "name": "head", "parent": "neck", "length": 188.84, "rotation": 8.07, "x": 69.96, "y": 2.5, "color": "ffe400ff" }, 11 | { "name": "left-front-thigh", "parent": "chest", "length": 67.42, "rotation": 138.94, "x": -45.59, "y": 7.93, "color": "ff0000ff" }, 12 | { 13 | "name": "left-front-leg", 14 | "parent": "left-front-thigh", 15 | "length": 51.58, 16 | "rotation": 43.36, 17 | "x": 67.42, 18 | "y": 0.03, 19 | "color": "ff0000ff" 20 | }, 21 | { 22 | "name": "left-front-toe1", 23 | "parent": "left-front-leg", 24 | "length": 51.45, 25 | "rotation": -98.01, 26 | "x": 45.54, 27 | "y": 2.43, 28 | "color": "ff0000ff" 29 | }, 30 | { 31 | "name": "left-front-toe2", 32 | "parent": "left-front-leg", 33 | "length": 61.98, 34 | "rotation": -55.26, 35 | "x": 51.58, 36 | "y": -0.13, 37 | "color": "ff0000ff" 38 | }, 39 | { 40 | "name": "left-front-toe3", 41 | "parent": "left-front-leg", 42 | "length": 45.65, 43 | "rotation": -11.14, 44 | "x": 54.19, 45 | "y": 0.6, 46 | "scaleX": 1.135, 47 | "color": "ff0000ff" 48 | }, 49 | { 50 | "name": "left-front-toe4", 51 | "parent": "left-front-leg", 52 | "length": 53.47, 53 | "rotation": 19.43, 54 | "x": 50.61, 55 | "y": 7.09, 56 | "scaleX": 1.135, 57 | "color": "ff0000ff" 58 | }, 59 | { "name": "right-rear-thigh", "parent": "back", "length": 123.47, "rotation": 104.88, "x": 65.31, "y": 59.89, "color": "29ff00ff" }, 60 | { 61 | "name": "left-rear-thigh", 62 | "parent": "right-rear-thigh", 63 | "length": 88.06, 64 | "rotation": 28.35, 65 | "x": -8.59, 66 | "y": 30.19, 67 | "color": "ff0000ff" 68 | }, 69 | { 70 | "name": "left-rear-leg", 71 | "parent": "left-rear-thigh", 72 | "length": 103.74, 73 | "rotation": -122.41, 74 | "x": 96.04, 75 | "y": -0.97, 76 | "color": "ff0000ff" 77 | }, 78 | { "name": "left-wing", "parent": "chest", "length": 301.12, "rotation": -75.51, "x": -7.25, "y": -24.66, "color": "ff0000ff" }, 79 | { 80 | "name": "right-front-thigh", 81 | "parent": "chest", 82 | "length": 81.64, 83 | "rotation": 67.97, 84 | "x": -10.89, 85 | "y": 28.25, 86 | "color": "29ff00ff" 87 | }, 88 | { 89 | "name": "right-front-leg", 90 | "parent": "right-front-thigh", 91 | "length": 66.53, 92 | "rotation": 92.7, 93 | "x": 83.05, 94 | "y": -0.31, 95 | "color": "29ff00ff" 96 | }, 97 | { 98 | "name": "right-front-toe1", 99 | "parent": "right-front-leg", 100 | "length": 46.66, 101 | "rotation": 8.59, 102 | "x": 70.03, 103 | "y": 5.31, 104 | "color": "29ff00ff" 105 | }, 106 | { 107 | "name": "right-front-toe2", 108 | "parent": "right-front-leg", 109 | "length": 53.67, 110 | "rotation": -35.02, 111 | "x": 66.53, 112 | "y": 0.34, 113 | "color": "29ff00ff" 114 | }, 115 | { 116 | "name": "right-front-toe3", 117 | "parent": "right-front-leg", 118 | "length": 58.39, 119 | "rotation": -74.67, 120 | "x": 62.1, 121 | "y": -0.79, 122 | "color": "29ff00ff" 123 | }, 124 | { 125 | "name": "right-rear-leg", 126 | "parent": "right-rear-thigh", 127 | "length": 91.06, 128 | "rotation": -129.04, 129 | "x": 123.47, 130 | "y": -0.27, 131 | "color": "29ff00ff" 132 | }, 133 | { 134 | "name": "right-rear-toe1", 135 | "parent": "right-rear-leg", 136 | "length": 95, 137 | "rotation": 141.98, 138 | "x": 90.07, 139 | "y": 2.12, 140 | "color": "29ff00ff" 141 | }, 142 | { 143 | "name": "right-rear-toe2", 144 | "parent": "right-rear-leg", 145 | "length": 99.29, 146 | "rotation": 125.32, 147 | "x": 89.6, 148 | "y": 1.52, 149 | "color": "29ff00ff" 150 | }, 151 | { 152 | "name": "right-rear-toe3", 153 | "parent": "right-rear-leg", 154 | "length": 103.46, 155 | "rotation": 112.27, 156 | "x": 91.06, 157 | "y": -0.35, 158 | "color": "29ff00ff" 159 | }, 160 | { "name": "right-wing", "parent": "head", "length": 359.5, "rotation": 83.21, "x": -74.68, "y": 20.91, "color": "29ff00ff" }, 161 | { "name": "tail1", "parent": "back", "length": 65.65, "rotation": 44.32, "x": 115.38, "y": -0.2, "color": "ffe400ff" }, 162 | { "name": "tail2", "parent": "tail1", "length": 54.5, "rotation": 12, "x": 65.65, "y": 0.23, "color": "ffe400ff" }, 163 | { "name": "tail3", "parent": "tail2", "length": 41.78, "rotation": 1.8, "x": 54.5, "y": 0.37, "color": "ffe400ff" }, 164 | { "name": "tail4", "parent": "tail3", "length": 34.19, "rotation": -1.8, "x": 41.78, "y": 0.16, "color": "ffe400ff" }, 165 | { "name": "tail5", "parent": "tail4", "length": 32.33, "rotation": -3.15, "x": 34.19, "y": -0.19, "color": "ffe400ff" }, 166 | { "name": "tail6", "parent": "tail5", "length": 80.08, "rotation": -29.55, "x": 32.33, "y": -0.23, "color": "ffe400ff" } 167 | ], 168 | "slots": [ 169 | { "name": "left-rear-leg", "bone": "left-rear-leg", "attachment": "left-rear-leg" }, 170 | { "name": "left-rear-thigh", "bone": "left-rear-thigh", "attachment": "left-rear-thigh" }, 171 | { "name": "left-wing", "bone": "left-wing", "attachment": "left-wing01" }, 172 | { "name": "tail6", "bone": "tail6", "attachment": "tail06" }, 173 | { "name": "tail5", "bone": "tail5", "attachment": "tail05" }, 174 | { "name": "tail4", "bone": "tail4", "attachment": "tail04" }, 175 | { "name": "tail3", "bone": "tail3", "attachment": "tail03" }, 176 | { "name": "tail2", "bone": "tail2", "attachment": "tail02" }, 177 | { "name": "tail1", "bone": "tail1", "attachment": "tail01" }, 178 | { "name": "back", "bone": "back", "attachment": "back" }, 179 | { "name": "left-front-thigh", "bone": "left-front-thigh", "attachment": "left-front-thigh" }, 180 | { "name": "left-front-leg", "bone": "left-front-leg", "attachment": "left-front-leg" }, 181 | { "name": "left-front-toe1", "bone": "left-front-toe1", "attachment": "front-toe-a" }, 182 | { "name": "left-front-toe4", "bone": "left-front-toe4", "attachment": "front-toe-b" }, 183 | { "name": "left-front-toe3", "bone": "left-front-toe3", "attachment": "front-toe-b" }, 184 | { "name": "left-front-toe2", "bone": "left-front-toe2", "attachment": "front-toe-b" }, 185 | { "name": "chest", "bone": "chest", "attachment": "chest" }, 186 | { "name": "right-rear-toe1", "bone": "right-rear-toe1", "attachment": "right-rear-toe" }, 187 | { "name": "right-rear-toe2", "bone": "right-rear-toe2", "attachment": "right-rear-toe" }, 188 | { "name": "right-rear-toe3", "bone": "right-rear-toe3", "attachment": "right-rear-toe" }, 189 | { "name": "right-rear-leg", "bone": "right-rear-leg", "attachment": "right-rear-leg" }, 190 | { "name": "right-rear-thigh", "bone": "right-rear-thigh", "attachment": "right-rear-thigh" }, 191 | { "name": "right-front-toe1", "bone": "right-front-toe1", "attachment": "front-toe-b" }, 192 | { "name": "right-front-thigh", "bone": "right-front-thigh", "attachment": "right-front-thigh" }, 193 | { "name": "right-front-leg", "bone": "right-front-leg", "attachment": "right-front-leg" }, 194 | { "name": "right-front-toe2", "bone": "right-front-toe2", "attachment": "front-toe-b" }, 195 | { "name": "right-front-toe3", "bone": "right-front-toe3", "attachment": "front-toe-b" }, 196 | { "name": "chin", "bone": "chin", "attachment": "chin" }, 197 | { "name": "right-wing", "bone": "right-wing", "attachment": "right-wing01" }, 198 | { "name": "head", "bone": "head", "attachment": "head" }, 199 | { "name": "thiagobrayner", "bone": "root", "attachment": "thiagobrayner" } 200 | ], 201 | "skins": { 202 | "default": { 203 | "back": { 204 | "back": { "x": 35.85, "y": 19.99, "rotation": -151.83, "width": 190, "height": 185 } 205 | }, 206 | "chest": { 207 | "chest": { "x": -14.6, "y": 24.79, "rotation": -161.7, "width": 136, "height": 122 } 208 | }, 209 | "chin": { 210 | "chin": { "x": 66.55, "y": 7.32, "rotation": 30.01, "width": 214, "height": 146 } 211 | }, 212 | "head": { 213 | "head": { "x": 76.69, "y": 32.21, "rotation": -47.13, "width": 296, "height": 260 } 214 | }, 215 | "left-front-leg": { 216 | "left-front-leg": { "x": 14.69, "y": 0.49, "rotation": 16, "width": 84, "height": 57 } 217 | }, 218 | "left-front-thigh": { 219 | "left-front-thigh": { "x": 27.66, "y": -11.59, "rotation": 58.66, "width": 84, "height": 72 } 220 | }, 221 | "left-front-toe1": { 222 | "front-toe-a": { "x": 31.93, "y": 0.61, "rotation": 109.56, "width": 29, "height": 50 } 223 | }, 224 | "left-front-toe2": { 225 | "front-toe-b": { "x": 26.84, "y": -4.95, "rotation": 109.51, "width": 56, "height": 57 } 226 | }, 227 | "left-front-toe3": { 228 | "front-toe-b": { "x": 18.22, "y": -7.22, "scaleX": 0.881, "scaleY": 0.941, "rotation": 99.71, "width": 56, "height": 57 } 229 | }, 230 | "left-front-toe4": { 231 | "front-toe-b": { "x": 23.21, "y": -11.69, "scaleX": 0.881, "rotation": 79.89, "width": 56, "height": 57 } 232 | }, 233 | "left-rear-leg": { 234 | "left-rear-leg": { "x": 67.29, "y": 12.63, "rotation": -162.65, "width": 206, "height": 177 } 235 | }, 236 | "left-rear-thigh": { 237 | "left-rear-thigh": { "x": 56.03, "y": 27.39, "rotation": 74.94, "width": 91, "height": 149 } 238 | }, 239 | "left-wing": { 240 | "left-wing01": { "x": 129.21, "y": -45.49, "rotation": -83.7, "width": 191, "height": 256 }, 241 | "left-wing02": { "x": 126.38, "y": -31.69, "rotation": -86.19, "width": 179, "height": 269 }, 242 | "left-wing03": { "x": 110.27, "y": -90.89, "rotation": -86.19, "width": 186, "height": 207 }, 243 | "left-wing04": { "x": -61.62, "y": -83.27, "rotation": -86.19, "width": 188, "height": 135 }, 244 | "left-wing05": { "x": -90.02, "y": -78.14, "rotation": -86.19, "width": 218, "height": 213 }, 245 | "left-wing06": { "x": -143.77, "y": -83.72, "rotation": -86.19, "width": 192, "height": 331 }, 246 | "left-wing07": { "x": -133.05, "y": -33.9, "rotation": -86.19, "width": 159, "height": 255 }, 247 | "left-wing08": { "x": 50.15, "y": -15.71, "rotation": -86.19, "width": 164, "height": 181 }, 248 | "left-wing09": { "x": 85.94, "y": -11.33, "rotation": -86.19, "width": 204, "height": 167 } 249 | }, 250 | "right-front-leg": { 251 | "right-front-leg": { "x": 17.8, "y": 4.23, "rotation": 37.63, "width": 101, "height": 89 } 252 | }, 253 | "right-front-thigh": { 254 | "right-front-thigh": { "x": 35.29, "y": 2.11, "rotation": 130.33, "width": 108, "height": 108 } 255 | }, 256 | "right-front-toe1": { 257 | "front-toe-b": { "x": 24.5, "y": -2.61, "rotation": 104.18, "width": 56, "height": 57 } 258 | }, 259 | "right-front-toe2": { 260 | "front-toe-b": { "x": 26.39, "y": 1.17, "rotation": 104.58, "width": 56, "height": 57 } 261 | }, 262 | "right-front-toe3": { 263 | "front-toe-b": { "x": 30.67, "y": -0.07, "rotation": 112.3, "width": 56, "height": 57 } 264 | }, 265 | "right-rear-leg": { 266 | "right-rear-leg": { "x": 60.88, "y": -5.73, "rotation": -127.67, "width": 116, "height": 100 } 267 | }, 268 | "right-rear-thigh": { 269 | "right-rear-thigh": { "x": 53.25, "y": 12.58, "rotation": 103.29, "width": 91, "height": 149 } 270 | }, 271 | "right-rear-toe1": { 272 | "right-rear-toe": { "x": 54.76, "y": -5.72, "rotation": 134.79, "width": 109, "height": 77 } 273 | }, 274 | "right-rear-toe2": { 275 | "right-rear-toe": { "x": 57.03, "y": -7.23, "rotation": 134.43, "width": 109, "height": 77 } 276 | }, 277 | "right-rear-toe3": { 278 | "right-rear-toe": { "x": 47.46, "y": -7.64, "rotation": 134.34, "width": 109, "height": 77 } 279 | }, 280 | "right-wing": { 281 | "right-wing01": { "x": 170.08, "y": -23.68, "rotation": -130.34, "width": 219, "height": 310 }, 282 | "right-wing02": { "x": 171.15, "y": -19.33, "rotation": -130.34, "width": 203, "height": 305 }, 283 | "right-wing03": { "x": 166.46, "y": 29.24, "rotation": -130.34, "width": 272, "height": 247 }, 284 | "right-wing04": { "x": 42.94, "y": 134.06, "rotation": -130.34, "width": 279, "height": 144 }, 285 | "right-wing05": { "x": -8.84, "y": 142.59, "rotation": -130.34, "width": 251, "height": 229 }, 286 | "right-wing06": { "x": -123.33, "y": 111.22, "rotation": -130.34, "width": 200, "height": 366 }, 287 | "right-wing07": { "x": -40.17, "y": 118.03, "rotation": -130.34, "width": 200, "height": 263 }, 288 | "right-wing08": { "x": 48.02, "y": 28.76, "rotation": -130.34, "width": 234, "height": 254 }, 289 | "right-wing09": { "x": 128.1, "y": 21.13, "rotation": -130.34, "width": 248, "height": 204 } 290 | }, 291 | "tail1": { 292 | "tail01": { "x": 22.6, "y": -4.5, "rotation": 163.85, "width": 120, "height": 153 } 293 | }, 294 | "tail2": { 295 | "tail02": { "x": 18.12, "y": -1.75, "rotation": 151.85, "width": 95, "height": 120 } 296 | }, 297 | "tail3": { 298 | "tail03": { "x": 16.94, "y": -2.01, "rotation": 150.04, "width": 73, "height": 92 } 299 | }, 300 | "tail4": { 301 | "tail04": { "x": 15.35, "y": -2.18, "rotation": 151.85, "width": 56, "height": 71 } 302 | }, 303 | "tail5": { 304 | "tail05": { "x": 15.06, "y": -3.57, "rotation": 155, "width": 52, "height": 59 } 305 | }, 306 | "tail6": { 307 | "tail06": { "x": 28.02, "y": -16.83, "rotation": -175.45, "width": 95, "height": 68 } 308 | }, 309 | "thiagobrayner": { 310 | "thiagobrayner": { "y": -95, "width": 350, "height": 31 } 311 | } 312 | } 313 | }, 314 | "animations": { 315 | "flying": { 316 | "slots": { 317 | "left-wing": { 318 | "attachment": [ 319 | { "time": 0.0667, "name": "left-wing02" }, 320 | { "time": 0.1333, "name": "left-wing03" }, 321 | { "time": 0.2, "name": "left-wing04" }, 322 | { "time": 0.2667, "name": "left-wing05" }, 323 | { "time": 0.3333, "name": "left-wing06" }, 324 | { "time": 0.4, "name": "left-wing07" }, 325 | { "time": 0.4667, "name": "left-wing08" }, 326 | { "time": 0.5333, "name": "left-wing09" }, 327 | { "time": 0.6, "name": "left-wing01" }, 328 | { "time": 0.7333, "name": "left-wing02" }, 329 | { "time": 0.8, "name": "left-wing03" }, 330 | { "time": 0.8333, "name": "left-wing04" }, 331 | { "time": 0.8667, "name": "left-wing05" }, 332 | { "time": 0.9, "name": "left-wing06" }, 333 | { "time": 0.9333, "name": "left-wing07" }, 334 | { "time": 0.9667, "name": "left-wing08" }, 335 | { "time": 1, "name": "left-wing01" } 336 | ] 337 | }, 338 | "right-wing": { 339 | "attachment": [ 340 | { "time": 0.0667, "name": "right-wing02" }, 341 | { "time": 0.1333, "name": "right-wing03" }, 342 | { "time": 0.2, "name": "right-wing04" }, 343 | { "time": 0.2667, "name": "right-wing05" }, 344 | { "time": 0.3333, "name": "right-wing06" }, 345 | { "time": 0.4, "name": "right-wing07" }, 346 | { "time": 0.4667, "name": "right-wing08" }, 347 | { "time": 0.5333, "name": "right-wing09" }, 348 | { "time": 0.6, "name": "right-wing01" }, 349 | { "time": 0.7333, "name": "right-wing02" }, 350 | { "time": 0.8, "name": "right-wing03" }, 351 | { "time": 0.8333, "name": "right-wing04" }, 352 | { "time": 0.8667, "name": "right-wing05" }, 353 | { "time": 0.9, "name": "right-wing06" }, 354 | { "time": 0.9333, "name": "right-wing07" }, 355 | { "time": 0.9667, "name": "right-wing08" }, 356 | { "time": 1, "name": "right-wing01" } 357 | ] 358 | } 359 | }, 360 | "bones": { 361 | "back": { 362 | "rotate": [ 363 | { "time": 0, "angle": 0 }, 364 | { "time": 0.1667, "angle": 17.39 }, 365 | { "time": 0.5, "angle": 0 }, 366 | { "time": 0.8333, "angle": 7.01 }, 367 | { "time": 1, "angle": 0 } 368 | ] 369 | }, 370 | "neck": { 371 | "rotate": [ 372 | { "time": 0, "angle": 0 }, 373 | { "time": 0.1667, "angle": -8.18 }, 374 | { "time": 0.3333, "angle": -23.16 }, 375 | { "time": 0.5, "angle": -18.02 }, 376 | { "time": 1, "angle": 0 } 377 | ] 378 | }, 379 | "tail1": { 380 | "rotate": [ 381 | { "time": 0, "angle": 0 }, 382 | { "time": 0.1667, "angle": -2.42 }, 383 | { "time": 0.3333, "angle": -26.21 }, 384 | { "time": 0.5, "angle": -29.66 }, 385 | { "time": 0.6667, "angle": -23.15 }, 386 | { "time": 0.8333, "angle": -55.46 }, 387 | { "time": 1, "angle": 0 } 388 | ] 389 | }, 390 | "tail2": { 391 | "rotate": [ 392 | { "time": 0, "angle": 0 }, 393 | { "time": 0.1667, "angle": -1.13 }, 394 | { "time": 0.3333, "angle": 10.48 }, 395 | { "time": 0.5, "angle": 7.89 }, 396 | { "time": 0.8333, "angle": -10.39 }, 397 | { "time": 1, "angle": 0 } 398 | ] 399 | }, 400 | "tail3": { 401 | "rotate": [ 402 | { "time": 0, "angle": 0 }, 403 | { "time": 0.1667, "angle": 8.25 }, 404 | { "time": 0.3333, "angle": 15.21 }, 405 | { "time": 0.5, "angle": 14.85 }, 406 | { "time": 0.8333, "angle": -18.91 }, 407 | { "time": 1, "angle": 0 } 408 | ] 409 | }, 410 | "tail4": { 411 | "rotate": [ 412 | { "time": 0, "angle": 0 }, 413 | { "time": 0.1667, "angle": 17.47 }, 414 | { "time": 0.3333, "angle": 22.15 }, 415 | { "time": 0.5, "angle": 22.76 }, 416 | { "time": 0.8333, "angle": -4.37 }, 417 | { "time": 1, "angle": 0 } 418 | ] 419 | }, 420 | "tail5": { 421 | "rotate": [ 422 | { "time": 0, "angle": 0 }, 423 | { "time": 0.1667, "angle": 7.4 }, 424 | { "time": 0.3333, "angle": 28.51 }, 425 | { "time": 0.5, "angle": 21.33 }, 426 | { "time": 0.8333, "angle": -1.28 }, 427 | { "time": 1, "angle": 0 } 428 | ] 429 | }, 430 | "tail6": { 431 | "rotate": [ 432 | { "time": 0, "angle": 0 }, 433 | { "time": 0.1667, "angle": 46 }, 434 | { "time": 0.4, "angle": 43.53 }, 435 | { "time": 0.5, "angle": 61.79 }, 436 | { "time": 0.8333, "angle": 13.28 }, 437 | { "time": 1, "angle": 0 } 438 | ] 439 | }, 440 | "right-rear-leg": { 441 | "rotate": [ 442 | { "time": 0, "angle": 0 }, 443 | { "time": 0.1667, "angle": -14.22 }, 444 | { "time": 0.5, "angle": 47.18 }, 445 | { "time": 1, "angle": 0 } 446 | ] 447 | }, 448 | "right-rear-toe3": { 449 | "rotate": [ 450 | { "time": 0, "angle": 0 }, 451 | { "time": 0.5, "angle": -36.06 }, 452 | { "time": 1, "angle": 0 } 453 | ] 454 | }, 455 | "right-rear-toe2": { 456 | "rotate": [ 457 | { "time": 0, "angle": 0 }, 458 | { "time": 0.5, "angle": -20.32 }, 459 | { "time": 1, "angle": 0 } 460 | ] 461 | }, 462 | "right-rear-toe1": { 463 | "rotate": [ 464 | { "time": 0, "angle": 0 }, 465 | { "time": 0.5, "angle": -18.71 }, 466 | { "time": 1, "angle": 0 } 467 | ] 468 | }, 469 | "head": { 470 | "rotate": [ 471 | { 472 | "time": 0, 473 | "angle": 0, 474 | "curve": [ 0.408, 1.37, 0.675, 1.43 ] 475 | }, 476 | { "time": 0.5, "angle": 1.04 }, 477 | { "time": 1, "angle": 0 } 478 | ] 479 | }, 480 | "chin": { 481 | "rotate": [ 482 | { 483 | "time": 0, 484 | "angle": 0, 485 | "curve": [ 0.417, 1.15, 0.494, 1.28 ] 486 | }, 487 | { "time": 0.3333, "angle": -5.15 }, 488 | { "time": 0.5, "angle": 9.8 }, 489 | { "time": 0.6667, "angle": 18.95 }, 490 | { "time": 1, "angle": 0 } 491 | ] 492 | }, 493 | "left-front-thigh": { 494 | "rotate": [ 495 | { "time": 0, "angle": 0 }, 496 | { "time": 0.1667, "angle": -19.19 }, 497 | { "time": 0.3333, "angle": -32.02 }, 498 | { "time": 0.5, "angle": -19.62 }, 499 | { "time": 1, "angle": 0 } 500 | ] 501 | }, 502 | "right-front-thigh": { 503 | "rotate": [ 504 | { "time": 0, "angle": 0 }, 505 | { "time": 0.1667, "angle": -12.96 }, 506 | { "time": 0.5, "angle": 16.2 }, 507 | { "time": 1, "angle": 0 } 508 | ] 509 | }, 510 | "left-front-leg": { 511 | "rotate": [ 512 | { "time": 0, "angle": 0 }, 513 | { "time": 0.1667, "angle": 37.77 }, 514 | { "time": 0.5, "angle": 0 } 515 | ] 516 | }, 517 | "left-front-toe1": { 518 | "rotate": [ 519 | { "time": 0, "angle": 0 }, 520 | { "time": 0.1667, "angle": -16.09 }, 521 | { "time": 0.5, "angle": 0 } 522 | ] 523 | }, 524 | "left-front-toe2": { 525 | "scale": [ 526 | { "time": 0, "x": 1, "y": 1 }, 527 | { "time": 0.5, "x": 1.331, "y": 1.029 }, 528 | { "time": 1, "x": 1, "y": 1 } 529 | ] 530 | }, 531 | "left-front-toe4": { 532 | "rotate": [ 533 | { "time": 0, "angle": 0 }, 534 | { "time": 0.5, "angle": 26.52 }, 535 | { "time": 1, "angle": 0 } 536 | ], 537 | "scale": [ 538 | { "time": 0, "x": 1, "y": 1 }, 539 | { "time": 0.5, "x": 1.211, "y": 0.993 }, 540 | { "time": 1, "x": 1, "y": 1 } 541 | ] 542 | }, 543 | "left-front-toe3": { 544 | "rotate": [ 545 | { "time": 0, "angle": 0 }, 546 | { "time": 0.5, "angle": 16.99 }, 547 | { "time": 1, "angle": 0 } 548 | ], 549 | "scale": [ 550 | { "time": 0, "x": 1, "y": 1 }, 551 | { "time": 0.5, "x": 1.355, "y": 1.008 }, 552 | { "time": 1, "x": 1, "y": 1 } 553 | ] 554 | }, 555 | "right-front-leg": { 556 | "rotate": [ 557 | { "time": 0, "angle": 0 }, 558 | { "time": 0.1667, "angle": 26.07 }, 559 | { "time": 0.5, "angle": -21.6 }, 560 | { "time": 1, "angle": 0 } 561 | ] 562 | }, 563 | "right-front-toe1": { 564 | "rotate": [ 565 | { "time": 0, "angle": 0 }, 566 | { "time": 0.1667, "angle": 29.24 }, 567 | { "time": 0.5, "angle": 34.84 }, 568 | { "time": 1, "angle": 0 } 569 | ], 570 | "scale": [ 571 | { "time": 0, "x": 1, "y": 1 }, 572 | { "time": 0.5, "x": 1.412, "y": 1 }, 573 | { "time": 1, "x": 1, "y": 1 } 574 | ] 575 | }, 576 | "right-front-toe2": { 577 | "rotate": [ 578 | { "time": 0, "angle": 0 }, 579 | { "time": 0.1667, "angle": 24.9 }, 580 | { "time": 0.5, "angle": 23.16 }, 581 | { "time": 1, "angle": 0 } 582 | ], 583 | "scale": [ 584 | { "time": 0, "x": 1, "y": 1 }, 585 | { "time": 0.5, "x": 1.407, "y": 1.058 }, 586 | { "time": 1, "x": 1, "y": 1 } 587 | ] 588 | }, 589 | "right-front-toe3": { 590 | "rotate": [ 591 | { "time": 0, "angle": 0 }, 592 | { "time": 0.1667, "angle": 11.01 }, 593 | { "time": 0.5, "angle": 0 } 594 | ], 595 | "scale": [ 596 | { "time": 0, "x": 1, "y": 1 }, 597 | { "time": 0.5, "x": 1.33, "y": 1.182 }, 598 | { "time": 1, "x": 1, "y": 1 } 599 | ] 600 | }, 601 | "left-rear-leg": { 602 | "rotate": [ 603 | { "time": 0, "angle": 0 }, 604 | { "time": 0.3667, "angle": 25.19 }, 605 | { "time": 0.6667, "angle": -15.65 }, 606 | { "time": 1, "angle": 0 } 607 | ] 608 | }, 609 | "center": { 610 | "rotate": [ 611 | { 612 | "time": 0, 613 | "angle": 0, 614 | "curve": [ 0.457, 0.2, 0.422, 1.07 ] 615 | }, 616 | { "time": 0.3333, "angle": 23.93 }, 617 | { 618 | "time": 0.6667, 619 | "angle": 337.8, 620 | "curve": [ 0.411, 0, 0.888, 0.75 ] 621 | }, 622 | { "time": 1, "angle": 0 } 623 | ], 624 | "translate": [ 625 | { 626 | "time": 0, 627 | "x": 0, 628 | "y": 0, 629 | "curve": [ 0.33, 1, 0.816, 1.34 ] 630 | }, 631 | { 632 | "time": 0.5, 633 | "x": -0.01, 634 | "y": 113.01, 635 | "curve": [ 0.397, 0, 0.71, 2.03 ] 636 | }, 637 | { "time": 1, "x": 0, "y": 0 } 638 | ] 639 | } 640 | } 641 | } 642 | } 643 | } -------------------------------------------------------------------------------- /examples/assets/spine36/goblins-pma.atlas: -------------------------------------------------------------------------------- 1 | 2 | goblins-pma.png 3 | size: 1024,128 4 | format: RGBA8888 5 | filter: Linear,Linear 6 | repeat: none 7 | dagger 8 | rotate: true 9 | xy: 372, 100 10 | size: 26, 108 11 | orig: 26, 108 12 | offset: 0, 0 13 | index: -1 14 | goblin/eyes-closed 15 | rotate: false 16 | xy: 2, 7 17 | size: 34, 12 18 | orig: 34, 12 19 | offset: 0, 0 20 | index: -1 21 | goblin/head 22 | rotate: false 23 | xy: 107, 36 24 | size: 103, 66 25 | orig: 103, 66 26 | offset: 0, 0 27 | index: -1 28 | goblin/left-arm 29 | rotate: false 30 | xy: 901, 56 31 | size: 37, 35 32 | orig: 37, 35 33 | offset: 0, 0 34 | index: -1 35 | goblin/left-foot 36 | rotate: false 37 | xy: 929, 95 38 | size: 65, 31 39 | orig: 65, 31 40 | offset: 0, 0 41 | index: -1 42 | goblin/left-hand 43 | rotate: false 44 | xy: 452, 2 45 | size: 36, 41 46 | orig: 36, 41 47 | offset: 0, 0 48 | index: -1 49 | goblin/left-lower-leg 50 | rotate: true 51 | xy: 713, 93 52 | size: 33, 70 53 | orig: 33, 70 54 | offset: 0, 0 55 | index: -1 56 | goblin/left-shoulder 57 | rotate: false 58 | xy: 610, 44 59 | size: 29, 44 60 | orig: 29, 44 61 | offset: 0, 0 62 | index: -1 63 | goblin/left-upper-leg 64 | rotate: true 65 | xy: 638, 93 66 | size: 33, 73 67 | orig: 33, 73 68 | offset: 0, 0 69 | index: -1 70 | goblin/neck 71 | rotate: false 72 | xy: 490, 2 73 | size: 36, 41 74 | orig: 36, 41 75 | offset: 0, 0 76 | index: -1 77 | goblin/pelvis 78 | rotate: false 79 | xy: 482, 45 80 | size: 62, 43 81 | orig: 62, 43 82 | offset: 0, 0 83 | index: -1 84 | goblin/right-arm 85 | rotate: true 86 | xy: 690, 2 87 | size: 23, 50 88 | orig: 23, 50 89 | offset: 0, 0 90 | index: -1 91 | goblin/right-foot 92 | rotate: false 93 | xy: 771, 58 94 | size: 63, 33 95 | orig: 63, 33 96 | offset: 0, 0 97 | index: -1 98 | goblin/right-hand 99 | rotate: false 100 | xy: 940, 56 101 | size: 36, 37 102 | orig: 36, 37 103 | offset: 0, 0 104 | index: -1 105 | goblin/right-lower-leg 106 | rotate: true 107 | xy: 482, 90 108 | size: 36, 76 109 | orig: 36, 76 110 | offset: 0, 0 111 | index: -1 112 | goblin/right-shoulder 113 | rotate: true 114 | xy: 602, 3 115 | size: 39, 45 116 | orig: 39, 45 117 | offset: 0, 0 118 | index: -1 119 | goblin/right-upper-leg 120 | rotate: true 121 | xy: 641, 57 122 | size: 34, 63 123 | orig: 34, 63 124 | offset: 0, 0 125 | index: -1 126 | goblin/torso 127 | rotate: true 128 | xy: 212, 34 129 | size: 68, 96 130 | orig: 68, 96 131 | offset: 0, 0 132 | index: -1 133 | goblin/undie-straps 134 | rotate: false 135 | xy: 380, 5 136 | size: 55, 19 137 | orig: 55, 19 138 | offset: 0, 0 139 | index: -1 140 | goblin/undies 141 | rotate: false 142 | xy: 174, 5 143 | size: 36, 29 144 | orig: 36, 29 145 | offset: 0, 0 146 | index: -1 147 | goblingirl/eyes-closed 148 | rotate: false 149 | xy: 269, 11 150 | size: 37, 21 151 | orig: 37, 21 152 | offset: 0, 0 153 | index: -1 154 | goblingirl/head 155 | rotate: false 156 | xy: 2, 21 157 | size: 103, 81 158 | orig: 103, 81 159 | offset: 0, 0 160 | index: -1 161 | goblingirl/left-arm 162 | rotate: true 163 | xy: 978, 56 164 | size: 37, 35 165 | orig: 37, 35 166 | offset: 0, 0 167 | index: -1 168 | goblingirl/left-foot 169 | rotate: false 170 | xy: 107, 3 171 | size: 65, 31 172 | orig: 65, 31 173 | offset: 0, 0 174 | index: -1 175 | goblingirl/left-hand 176 | rotate: false 177 | xy: 565, 2 178 | size: 35, 40 179 | orig: 35, 40 180 | offset: 0, 0 181 | index: -1 182 | goblingirl/left-lower-leg 183 | rotate: true 184 | xy: 785, 93 185 | size: 33, 70 186 | orig: 33, 70 187 | offset: 0, 0 188 | index: -1 189 | goblingirl/left-shoulder 190 | rotate: true 191 | xy: 690, 27 192 | size: 28, 46 193 | orig: 28, 46 194 | offset: 0, 0 195 | index: -1 196 | goblingirl/left-upper-leg 197 | rotate: true 198 | xy: 857, 93 199 | size: 33, 70 200 | orig: 33, 70 201 | offset: 0, 0 202 | index: -1 203 | goblingirl/neck 204 | rotate: false 205 | xy: 528, 2 206 | size: 35, 41 207 | orig: 35, 41 208 | offset: 0, 0 209 | index: -1 210 | goblingirl/pelvis 211 | rotate: false 212 | xy: 546, 45 213 | size: 62, 43 214 | orig: 62, 43 215 | offset: 0, 0 216 | index: -1 217 | goblingirl/right-arm 218 | rotate: false 219 | xy: 452, 48 220 | size: 28, 50 221 | orig: 28, 50 222 | offset: 0, 0 223 | index: -1 224 | goblingirl/right-foot 225 | rotate: false 226 | xy: 836, 58 227 | size: 63, 33 228 | orig: 63, 33 229 | offset: 0, 0 230 | index: -1 231 | goblingirl/right-hand 232 | rotate: true 233 | xy: 771, 20 234 | size: 36, 37 235 | orig: 36, 37 236 | offset: 0, 0 237 | index: -1 238 | goblingirl/right-lower-leg 239 | rotate: true 240 | xy: 560, 90 241 | size: 36, 76 242 | orig: 36, 76 243 | offset: 0, 0 244 | index: -1 245 | goblingirl/right-shoulder 246 | rotate: false 247 | xy: 649, 10 248 | size: 39, 45 249 | orig: 39, 45 250 | offset: 0, 0 251 | index: -1 252 | goblingirl/right-upper-leg 253 | rotate: true 254 | xy: 706, 57 255 | size: 34, 63 256 | orig: 34, 63 257 | offset: 0, 0 258 | index: -1 259 | goblingirl/torso 260 | rotate: false 261 | xy: 310, 2 262 | size: 68, 96 263 | orig: 68, 96 264 | offset: 0, 0 265 | index: -1 266 | goblingirl/undie-straps 267 | rotate: false 268 | xy: 212, 13 269 | size: 55, 19 270 | orig: 55, 19 271 | offset: 0, 0 272 | index: -1 273 | goblingirl/undies 274 | rotate: false 275 | xy: 810, 27 276 | size: 36, 29 277 | orig: 36, 29 278 | offset: 0, 0 279 | index: -1 280 | shield 281 | rotate: false 282 | xy: 380, 26 283 | size: 70, 72 284 | orig: 70, 72 285 | offset: 0, 0 286 | index: -1 287 | spear 288 | rotate: true 289 | xy: 2, 104 290 | size: 22, 368 291 | orig: 22, 368 292 | offset: 0, 0 293 | index: -1 294 | -------------------------------------------------------------------------------- /examples/assets/spine36/goblins-pma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playcanvas/playcanvas-spine/9214f3043d35049f6efaa82c9ca54cd0bfe7917e/examples/assets/spine36/goblins-pma.png -------------------------------------------------------------------------------- /examples/assets/spine36/hero-mesh.atlas: -------------------------------------------------------------------------------- 1 | 2 | hero-mesh.png 3 | size: 1024,256 4 | format: RGBA8888 5 | filter: Linear,Linear 6 | repeat: none 7 | body 8 | rotate: false 9 | xy: 324, 81 10 | size: 97, 95 11 | orig: 97, 95 12 | offset: 0, 0 13 | index: -1 14 | cape 15 | rotate: false 16 | xy: 176, 88 17 | size: 146, 159 18 | orig: 146, 159 19 | offset: 0, 0 20 | index: -1 21 | eyes 22 | rotate: false 23 | xy: 604, 216 24 | size: 82, 31 25 | orig: 82, 31 26 | offset: 0, 0 27 | index: -1 28 | fingers 29 | rotate: true 30 | xy: 643, 183 31 | size: 31, 33 32 | orig: 31, 33 33 | offset: 0, 0 34 | index: -1 35 | foot1 36 | rotate: false 37 | xy: 863, 205 38 | size: 50, 42 39 | orig: 50, 42 40 | offset: 0, 0 41 | index: -1 42 | foot2 43 | rotate: false 44 | xy: 915, 209 45 | size: 53, 38 46 | orig: 53, 38 47 | offset: 0, 0 48 | index: -1 49 | forearm1 50 | rotate: true 51 | xy: 970, 206 52 | size: 41, 49 53 | orig: 41, 49 54 | offset: 0, 0 55 | index: -1 56 | forearm2 57 | rotate: true 58 | xy: 423, 79 59 | size: 31, 32 60 | orig: 31, 32 61 | offset: 0, 0 62 | index: -1 63 | hand1 64 | rotate: false 65 | xy: 140, 24 66 | size: 37, 48 67 | orig: 37, 48 68 | offset: 0, 0 69 | index: -1 70 | hand2 71 | rotate: true 72 | xy: 604, 183 73 | size: 31, 37 74 | orig: 31, 37 75 | offset: 0, 0 76 | index: -1 77 | head 78 | rotate: false 79 | xy: 2, 74 80 | size: 172, 173 81 | orig: 172, 173 82 | offset: 0, 0 83 | index: -1 84 | mantles 85 | rotate: false 86 | xy: 2, 17 87 | size: 136, 55 88 | orig: 136, 55 89 | offset: 0, 0 90 | index: -1 91 | mouth 92 | rotate: false 93 | xy: 2, 2 94 | size: 61, 13 95 | orig: 61, 13 96 | offset: 0, 0 97 | index: -1 98 | shin1 99 | rotate: false 100 | xy: 482, 119 101 | size: 53, 57 102 | orig: 53, 57 103 | offset: 0, 0 104 | index: -1 105 | shin2 106 | rotate: true 107 | xy: 807, 196 108 | size: 51, 54 109 | orig: 51, 54 110 | offset: 0, 0 111 | index: -1 112 | sword 113 | rotate: false 114 | xy: 324, 178 115 | size: 216, 69 116 | orig: 216, 69 117 | offset: 0, 0 118 | index: -1 119 | thigh1 120 | rotate: false 121 | xy: 542, 184 122 | size: 60, 63 123 | orig: 60, 63 124 | offset: 0, 0 125 | index: -1 126 | thigh2 127 | rotate: false 128 | xy: 423, 112 129 | size: 57, 64 130 | orig: 57, 64 131 | offset: 0, 0 132 | index: -1 133 | upper-arm1 134 | rotate: true 135 | xy: 749, 197 136 | size: 50, 56 137 | orig: 50, 56 138 | offset: 0, 0 139 | index: -1 140 | upper-arm2 141 | rotate: true 142 | xy: 688, 208 143 | size: 39, 59 144 | orig: 39, 59 145 | offset: 0, 0 146 | index: -1 147 | -------------------------------------------------------------------------------- /examples/assets/spine36/hero-mesh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playcanvas/playcanvas-spine/9214f3043d35049f6efaa82c9ca54cd0bfe7917e/examples/assets/spine36/hero-mesh.png -------------------------------------------------------------------------------- /examples/assets/spine36/license.txt: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2013, Esoteric Software 3 | 4 | The images in this project may be redistributed as long as they are accompanied 5 | by this license file. The images may not be used for commercial use of any 6 | kind. 7 | 8 | The project file is released into the public domain. It may be used as the basis 9 | for derivative work. 10 | -------------------------------------------------------------------------------- /examples/assets/spine38/README.md: -------------------------------------------------------------------------------- 1 | All the example files in this folder are Copyright Esoteric Software. They are provided as examples and test cases for the PlayCanvas plugin and not for use in projects. 2 | -------------------------------------------------------------------------------- /examples/assets/spine38/license.txt: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2013, Esoteric Software 3 | 4 | The images in this project may be redistributed as long as they are accompanied 5 | by this license file. The images may not be used for commercial use of any 6 | kind. 7 | 8 | The project file is released into the public domain. It may be used as the basis 9 | for derivative work. 10 | -------------------------------------------------------------------------------- /examples/assets/spine38/spineboy-pro.atlas: -------------------------------------------------------------------------------- 1 | 2 | spineboy-pro.png 3 | size: 1534,529 4 | format: RGBA8888 5 | filter: Linear,Linear 6 | repeat: none 7 | crosshair 8 | rotate: false 9 | xy: 449, 18 10 | size: 89, 89 11 | orig: 89, 89 12 | offset: 0, 0 13 | index: -1 14 | eye-indifferent 15 | rotate: false 16 | xy: 695, 10 17 | size: 93, 89 18 | orig: 93, 89 19 | offset: 0, 0 20 | index: -1 21 | eye-surprised 22 | rotate: true 23 | xy: 985, 178 24 | size: 93, 89 25 | orig: 93, 89 26 | offset: 0, 0 27 | index: -1 28 | front-bracer 29 | rotate: true 30 | xy: 1407, 103 31 | size: 58, 80 32 | orig: 58, 80 33 | offset: 0, 0 34 | index: -1 35 | front-fist-closed 36 | rotate: true 37 | xy: 1208, 203 38 | size: 75, 82 39 | orig: 75, 82 40 | offset: 0, 0 41 | index: -1 42 | front-fist-open 43 | rotate: false 44 | xy: 989, 89 45 | size: 86, 87 46 | orig: 86, 87 47 | offset: 0, 0 48 | index: -1 49 | front-foot 50 | rotate: false 51 | xy: 1077, 58 52 | size: 126, 69 53 | orig: 126, 69 54 | offset: 0, 0 55 | index: -1 56 | front-shin 57 | rotate: true 58 | xy: 803, 89 59 | size: 82, 184 60 | orig: 82, 184 61 | offset: 0, 0 62 | index: -1 63 | front-thigh 64 | rotate: true 65 | xy: 1062, 11 66 | size: 45, 112 67 | orig: 45, 112 68 | offset: 0, 0 69 | index: -1 70 | front-upper-arm 71 | rotate: true 72 | xy: 1205, 33 73 | size: 46, 97 74 | orig: 46, 97 75 | offset: 0, 0 76 | index: -1 77 | goggles 78 | rotate: false 79 | xy: 540, 101 80 | size: 261, 166 81 | orig: 261, 166 82 | offset: 0, 0 83 | index: -1 84 | gun 85 | rotate: false 86 | xy: 1301, 324 87 | size: 209, 203 88 | orig: 210, 203 89 | offset: 0, 0 90 | index: -1 91 | head 92 | rotate: false 93 | xy: 2, 75 94 | size: 271, 298 95 | orig: 271, 298 96 | offset: 0, 0 97 | index: -1 98 | hoverboard-board 99 | rotate: false 100 | xy: 2, 375 101 | size: 492, 152 102 | orig: 492, 152 103 | offset: 0, 0 104 | index: -1 105 | hoverboard-thruster 106 | rotate: false 107 | xy: 1472, 38 108 | size: 60, 63 109 | orig: 60, 64 110 | offset: 0, 0 111 | index: -1 112 | hoverglow-small 113 | rotate: false 114 | xy: 2, 2 115 | size: 258, 71 116 | orig: 274, 75 117 | offset: 7, 2 118 | index: -1 119 | mouth-grind 120 | rotate: false 121 | xy: 1203, 142 122 | size: 93, 59 123 | orig: 93, 59 124 | offset: 0, 0 125 | index: -1 126 | mouth-oooo 127 | rotate: false 128 | xy: 1205, 81 129 | size: 93, 59 130 | orig: 93, 59 131 | offset: 0, 0 132 | index: -1 133 | mouth-smile 134 | rotate: false 135 | xy: 1300, 98 136 | size: 93, 59 137 | orig: 93, 59 138 | offset: 0, 0 139 | index: -1 140 | muzzle-glow 141 | rotate: false 142 | xy: 496, 485 143 | size: 42, 42 144 | orig: 50, 50 145 | offset: 4, 4 146 | index: -1 147 | muzzle-ring 148 | rotate: true 149 | xy: 1301, 276 150 | size: 46, 206 151 | orig: 49, 209 152 | offset: 1, 2 153 | index: -1 154 | muzzle01 155 | rotate: false 156 | xy: 1077, 129 157 | size: 124, 74 158 | orig: 133, 79 159 | offset: 3, 2 160 | index: -1 161 | muzzle02 162 | rotate: false 163 | xy: 934, 12 164 | size: 126, 75 165 | orig: 135, 84 166 | offset: 4, 5 167 | index: -1 168 | muzzle03 169 | rotate: false 170 | xy: 540, 6 171 | size: 153, 93 172 | orig: 166, 106 173 | offset: 7, 7 174 | index: -1 175 | muzzle04 176 | rotate: false 177 | xy: 790, 5 178 | size: 142, 82 179 | orig: 149, 90 180 | offset: 4, 4 181 | index: -1 182 | muzzle05 183 | rotate: false 184 | xy: 1076, 205 185 | size: 130, 73 186 | orig: 135, 75 187 | offset: 2, 1 188 | index: -1 189 | neck 190 | rotate: false 191 | xy: 1489, 120 192 | size: 35, 41 193 | orig: 36, 41 194 | offset: 0, 0 195 | index: -1 196 | portal-bg 197 | rotate: false 198 | xy: 275, 109 199 | size: 263, 264 200 | orig: 266, 266 201 | offset: 2, 1 202 | index: -1 203 | portal-flare1 204 | rotate: false 205 | xy: 1407, 163 206 | size: 103, 54 207 | orig: 111, 60 208 | offset: 4, 3 209 | index: -1 210 | portal-flare2 211 | rotate: false 212 | xy: 1407, 219 213 | size: 107, 55 214 | orig: 114, 61 215 | offset: 4, 3 216 | index: -1 217 | portal-flare3 218 | rotate: false 219 | xy: 1298, 159 220 | size: 107, 53 221 | orig: 115, 59 222 | offset: 5, 3 223 | index: -1 224 | portal-shade 225 | rotate: false 226 | xy: 540, 269 227 | size: 258, 258 228 | orig: 266, 266 229 | offset: 4, 4 230 | index: -1 231 | portal-streaks1 232 | rotate: false 233 | xy: 800, 273 234 | size: 249, 254 235 | orig: 252, 256 236 | offset: 1, 1 237 | index: -1 238 | portal-streaks2 239 | rotate: false 240 | xy: 1051, 280 241 | size: 248, 247 242 | orig: 250, 249 243 | offset: 1, 1 244 | index: -1 245 | rear-bracer 246 | rotate: true 247 | xy: 1400, 46 248 | size: 55, 70 249 | orig: 56, 72 250 | offset: 0, 2 251 | index: -1 252 | rear-foot 253 | rotate: false 254 | xy: 1292, 214 255 | size: 113, 60 256 | orig: 113, 60 257 | offset: 0, 0 258 | index: -1 259 | rear-shin 260 | rotate: true 261 | xy: 275, 33 262 | size: 74, 172 263 | orig: 75, 178 264 | offset: 1, 4 265 | index: -1 266 | rear-thigh 267 | rotate: true 268 | xy: 1304, 41 269 | size: 55, 94 270 | orig: 55, 94 271 | offset: 0, 0 272 | index: -1 273 | rear-upper-arm 274 | rotate: false 275 | xy: 496, 396 276 | size: 40, 87 277 | orig: 40, 87 278 | offset: 0, 0 279 | index: -1 280 | torso 281 | rotate: true 282 | xy: 803, 173 283 | size: 98, 180 284 | orig: 98, 180 285 | offset: 0, 0 286 | index: -1 287 | -------------------------------------------------------------------------------- /examples/assets/spine38/spineboy-pro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playcanvas/playcanvas-spine/9214f3043d35049f6efaa82c9ca54cd0bfe7917e/examples/assets/spine38/spineboy-pro.png -------------------------------------------------------------------------------- /examples/assets/spine40/LICENSE: -------------------------------------------------------------------------------- 1 | Spine Runtimes License Agreement 2 | Last updated May 1, 2019. Replaces all prior versions. 3 | 4 | Copyright (c) 2013-2019, Esoteric Software LLC 5 | 6 | Integration of the Spine Runtimes into software or otherwise creating 7 | derivative works of the Spine Runtimes is permitted under the terms and 8 | conditions of Section 2 of the Spine Editor License Agreement: 9 | http://esotericsoftware.com/spine-editor-license 10 | 11 | Otherwise, it is permitted to integrate the Spine Runtimes into software 12 | or otherwise create derivative works of the Spine Runtimes (collectively, 13 | "Products"), provided that each user of the Products must obtain their own 14 | Spine Editor license and redistribution of the Products in any form must 15 | include this license and copyright notice. 16 | 17 | THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS 18 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 20 | NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS 23 | INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /examples/assets/spine40/atlas2.atlas: -------------------------------------------------------------------------------- 1 | atlas2.png 2 | size:1024,1024 3 | filter:Linear,Linear 4 | pma:true 5 | armorgirl/Layer 100 6 | bounds:991,253,31,40 7 | armorgirl/Layer 101 8 | bounds:847,410,53,24 9 | armorgirl/Layer 102 10 | bounds:986,583,60,33 11 | rotate:90 12 | armorgirl/Layer 103 13 | bounds:690,396,57,93 14 | armorgirl/Layer 104 15 | bounds:991,295,31,42 16 | armorgirl/Layer 106 17 | bounds:885,281,38,53 18 | rotate:90 19 | armorgirl/Layer 107 20 | bounds:327,122,70,127 21 | armorgirl/Layer 108 22 | bounds:749,335,92,94 23 | rotate:90 24 | armorgirl/Layer 109 25 | bounds:839,110,20,32 26 | rotate:90 27 | armorgirl/Layer 110 28 | bounds:388,20,84,29 29 | rotate:90 30 | armorgirl/Layer 111 31 | bounds:903,499,87,62 32 | armorgirl/Layer 112 33 | bounds:991,339,31,46 34 | armorgirl/Layer 113 35 | bounds:536,121,33,32 36 | rotate:90 37 | armorgirl/Layer 114 38 | bounds:920,157,20,26 39 | armorgirl/Layer 115 40 | bounds:873,617,26,25 41 | rotate:90 42 | armorgirl/Layer 116 43 | bounds:570,107,40,45 44 | rotate:90 45 | armorgirl/Layer 117 46 | bounds:942,99,23,31 47 | rotate:90 48 | armorgirl/Layer 118 49 | bounds:940,261,40,49 50 | rotate:90 51 | armorgirl/Layer 66 52 | bounds:444,257,118,84 53 | rotate:90 54 | armorgirl/Layer 68 55 | bounds:987,220,31,35 56 | rotate:90 57 | armorgirl/Layer 69 58 | bounds:419,2,40,59 59 | rotate:90 60 | armorgirl/Layer 70 61 | bounds:902,399,57,26 62 | armorgirl/Layer 72 63 | bounds:912,124,31,25 64 | rotate:90 65 | armorgirl/Layer 73 66 | bounds:201,8,43,90 67 | rotate:90 68 | armorgirl/Layer 74 69 | bounds:534,359,51,66 70 | armorgirl/Layer 75 71 | bounds:808,201,41,32 72 | armorgirl/Layer 76 73 | bounds:690,335,33,15 74 | armorgirl/Layer 77 75 | bounds:987,191,35,27 76 | armorgirl/Layer 78 77 | bounds:488,131,23,46 78 | rotate:90 79 | armorgirl/Layer 79 80 | bounds:399,128,17,82 81 | rotate:90 82 | armorgirl/Layer 80 83 | bounds:808,169,30,41 84 | rotate:90 85 | armorgirl/Layer 81 86 | bounds:588,491,8,9 87 | rotate:90 88 | armorgirl/Layer 82 89 | bounds:854,222,29,15 90 | armorgirl/Layer 84 91 | bounds:388,2,29,16 92 | armorgirl/Layer 85 93 | bounds:725,335,15,12 94 | rotate:90 95 | armorgirl/Layer 851 96 | bounds:388,463,15,12 97 | rotate:90 98 | armorgirl/Layer 86 99 | bounds:873,774,13,4 100 | rotate:90 101 | armorgirl/Layer 87 102 | bounds:940,309,9,10 103 | armorgirl/Layer 871 104 | bounds:717,210,9,10 105 | armorgirl/Layer 88 106 | bounds:715,681,7,6 107 | armorgirl/Layer 89 108 | bounds:951,388,9,8 109 | rotate:90 110 | armorgirl/Layer 90 111 | bounds:905,885,66,111 112 | rotate:90 113 | armorgirl/Layer 91 114 | bounds:854,258,20,29 115 | rotate:90 116 | armorgirl/Layer 92 117 | bounds:612,491,134,130 118 | armorgirl/Layer 93 119 | bounds:744,631,56,98 120 | rotate:90 121 | armorgirl/Layer 94 122 | bounds:505,60,17,23 123 | rotate:90 124 | armorgirl/Layer 95 125 | bounds:727,110,30,42 126 | armorgirl/Layer 96 127 | bounds:617,110,28,37 128 | armorgirl/Layer 97 129 | bounds:878,128,27,32 130 | rotate:90 131 | armorgirl/Layer 98 132 | bounds:673,89,17,20 133 | rotate:90 134 | armorgirl/Layer 99 135 | bounds:851,165,19,35 136 | rotate:90 137 | greengirl/Layer 17 138 | bounds:388,501,222,120 139 | greengirl/Layer 20 140 | bounds:198,645,190,377 141 | greengirl/Layer 21 142 | bounds:717,222,43,55 143 | greengirl/Layer 22 144 | bounds:920,185,18,38 145 | greengirl/Layer 23 146 | bounds:505,106,23,23 147 | greengirl/Layer 24 148 | bounds:800,111,20,37 149 | rotate:90 150 | greengirl/Layer 25 151 | bounds:854,239,17,29 152 | rotate:90 153 | greengirl/Layer 26 154 | bounds:991,98,19,26 155 | rotate:90 156 | greengirl/Layer 27 157 | bounds:530,101,18,25 158 | rotate:90 159 | greengirl/Layer 28 160 | bounds:130,9,18,30 161 | greengirl/Layer 29 162 | bounds:972,124,17,30 163 | greengirl/Layer 30 164 | bounds:728,154,17,33 165 | rotate:90 166 | greengirl/Layer 32 167 | bounds:763,164,19,43 168 | rotate:90 169 | greengirl/Layer 33 170 | bounds:530,257,16,31 171 | rotate:90 172 | greengirl/Layer 34 173 | bounds:873,109,17,33 174 | rotate:90 175 | greengirl/Layer 35 176 | bounds:991,119,16,30 177 | rotate:90 178 | greengirl/Layer 36 179 | bounds:991,137,20,31 180 | rotate:90 181 | greengirl/Layer 37 182 | bounds:759,111,22,39 183 | rotate:90 184 | greengirl/Layer 38 185 | bounds:332,251,110,100 186 | greengirl/Layer 39 187 | bounds:658,335,30,63 188 | greengirl/Layer 40 189 | bounds:992,521,60,30 190 | rotate:90 191 | greengirl/Layer 41 192 | bounds:844,617,70,27 193 | rotate:90 194 | greengirl/Layer 42 195 | bounds:712,279,56,54 196 | greengirl/Layer 43 197 | bounds:55,300,27,10 198 | greengirl/Layer 44 199 | bounds:739,341,9,8 200 | rotate:90 201 | greengirl/Layer 45 202 | bounds:908,102,32,20 203 | greengirl/Layer 46 204 | bounds:717,87,16,19 205 | greengirl/Layer 47 206 | bounds:388,480,19,13 207 | rotate:90 208 | greengirl/Layer 48 209 | bounds:888,157,31,30 210 | rotate:90 211 | greengirl/Layer 49 212 | bounds:902,427,70,79 213 | rotate:90 214 | greengirl/Layer 50 215 | bounds:690,352,57,42 216 | greengirl/Layer 51 217 | bounds:841,527,88,60 218 | rotate:90 219 | greengirl/Layer 52 220 | bounds:715,689,98,156 221 | rotate:90 222 | greengirl/Layer 53 223 | bounds:213,111,140,78 224 | rotate:90 225 | greengirl/Layer 54 226 | bounds:903,563,81,80 227 | greengirl/Layer 55 228 | bounds:992,499,20,30 229 | rotate:90 230 | orangegirl/Layer 14 231 | bounds:202,456,187,184 232 | rotate:90 233 | orangegirl/Layer 15 234 | bounds:588,400,89,100 235 | rotate:90 236 | orangegirl/Layer 16 237 | bounds:942,156,28,32 238 | rotate:90 239 | orangegirl/Layer 17 240 | bounds:505,79,20,25 241 | orangegirl/Layer 18 242 | bounds:975,103,19,14 243 | rotate:90 244 | orangegirl/Layer 19 245 | bounds:293,2,13,7 246 | orangegirl/Layer 20 247 | bounds:527,80,24,19 248 | orangegirl/Layer 21 249 | bounds:488,156,99,80 250 | rotate:90 251 | orangegirl/Layer 22 252 | bounds:419,44,50,82 253 | orangegirl/Layer 23 254 | bounds:847,436,53,89 255 | orangegirl/Layer 24 256 | bounds:55,312,35,33 257 | orangegirl/Layer 25 258 | bounds:640,200,60,29 259 | rotate:90 260 | orangegirl/Layer 26 261 | bounds:647,147,51,29 262 | rotate:90 263 | orangegirl/Layer 27 264 | bounds:530,275,56,82 265 | orangegirl/Layer 28 266 | bounds:570,149,41,75 267 | rotate:90 268 | orangegirl/Layer 29 269 | bounds:894,321,55,33 270 | orangegirl/Layer 30 271 | bounds:213,53,56,78 272 | rotate:90 273 | orangegirl/Layer 31 274 | bounds:710,789,88,167 275 | rotate:90 276 | orangegirl/Layer 32 277 | bounds:845,323,32,47 278 | rotate:90 279 | orangegirl/Layer 33 280 | bounds:879,766,111,143 281 | rotate:90 282 | orangegirl/Layer 34 283 | bounds:770,282,56,51 284 | orangegirl/Layer 35 285 | bounds:588,262,66,69 286 | rotate:90 287 | orangegirl/Layer 36 288 | bounds:748,527,91,102 289 | orangegirl/Layer 37 290 | bounds:92,41,56,172 291 | orangegirl/Layer 38 292 | bounds:2,2,38,85 293 | rotate:90 294 | orangegirl/Layer 39 295 | bounds:845,357,47,51 296 | orangegirl/Layer 40 297 | bounds:983,387,42,39 298 | rotate:90 299 | orangegirl/Layer 41 300 | bounds:202,370,84,199 301 | rotate:90 302 | orangegirl/Layer 42 303 | bounds:403,377,48,129 304 | rotate:90 305 | orangegirl/Layer 43 306 | bounds:961,386,20,39 307 | orangegirl/Layer 44 308 | bounds:150,68,61,145 309 | orangegirl/Layer 45 310 | bounds:150,2,64,49 311 | rotate:90 312 | orangegirl/Layer 46 313 | bounds:89,2,39,37 314 | orangegirl/Layer 47 315 | bounds:844,132,32,31 316 | orangegirl/Layer 48 317 | bounds:659,267,51,66 318 | orangegirl/Layer 49 319 | bounds:92,215,112,130 320 | owl/L_eye-closed 321 | bounds:940,216,45,43 322 | owl/L_eye-iris 323 | bounds:762,232,45,43 324 | rotate:90 325 | owl/L_eye-light 326 | bounds:201,55,11,10 327 | rotate:90 328 | owl/L_eye-pupil 329 | bounds:939,124,31,30 330 | owl/L_foot 331 | bounds:479,94,32,24 332 | rotate:90 333 | owl/L_wing 334 | bounds:894,356,41,55 335 | rotate:90 336 | owl/R_eye-closed 337 | bounds:763,185,45,43 338 | rotate:90 339 | owl/R_eye-iris 340 | bounds:807,235,45,43 341 | owl/R_eye-light 342 | bounds:828,323,11,10 343 | owl/R_eye-pupil 344 | bounds:976,159,31,30 345 | owl/R_foot 346 | bounds:479,60,32,24 347 | rotate:90 348 | owl/R_wing 349 | bounds:828,280,41,55 350 | rotate:90 351 | owl/beak 352 | bounds:530,59,19,21 353 | rotate:90 354 | owl/beak-down 355 | bounds:695,88,18,20 356 | rotate:90 357 | owl/body 358 | bounds:206,253,124,98 359 | owl/feather-1 360 | bounds:647,115,29,30 361 | owl/feather-2 362 | bounds:887,190,31,33 363 | owl/feather-3 364 | bounds:951,303,28,38 365 | rotate:90 366 | owl/head-base 367 | bounds:873,645,149,119 368 | owl/leaf-1 369 | bounds:951,333,38,51 370 | owl/leaf-2 371 | bounds:728,173,33,47 372 | owl/leaf-3 373 | bounds:983,431,66,39 374 | rotate:90 375 | owl/leaf-4 376 | bounds:885,225,45,25 377 | owl/leaf-5 378 | bounds:885,252,27,53 379 | rotate:90 380 | owl/leaf-6 381 | bounds:671,207,44,58 382 | owl/leaf-7 383 | bounds:763,135,27,43 384 | rotate:90 385 | owl/wood 386 | bounds:390,870,309,152 387 | stretchyman/back arm 388 | bounds:701,879,72,202 389 | rotate:90 390 | stretchyman/back leg 391 | bounds:390,768,100,318 392 | rotate:90 393 | stretchyman/body 394 | bounds:55,570,141,452 395 | stretchyman/front arm 396 | bounds:55,347,145,221 397 | stretchyman/head 398 | bounds:399,147,87,102 399 | tank/antenna 400 | bounds:471,50,6,76 401 | tank/cannon 402 | bounds:202,353,233,15 403 | tank/cannonConnector 404 | bounds:940,186,28,34 405 | rotate:90 406 | tank/ground 407 | bounds:2,42,256,88 408 | rotate:90 409 | tank/guntower 410 | bounds:403,427,183,72 411 | tank/machinegun 412 | bounds:327,106,83,14 413 | tank/machinegun-mount 414 | bounds:647,95,18,24 415 | rotate:90 416 | tank/rock 417 | bounds:293,106,145,32 418 | rotate:90 419 | tank/tankBottom 420 | bounds:701,953,321,69 421 | tank/tankBottom-shadow 422 | bounds:390,681,323,85 423 | tank/tankTop 424 | bounds:390,623,352,56 425 | tank/tread 426 | bounds:1009,165,24,8 427 | rotate:90 428 | tank/tread-inside 429 | bounds:894,401,6,7 430 | tank/wheel-big 431 | bounds:678,157,48,48 432 | tank/wheel-big-overlay 433 | bounds:678,108,47,47 434 | tank/wheel-mid 435 | bounds:851,186,34,34 436 | tank/wheel-mid-overlay 437 | bounds:808,133,34,34 438 | tank/wheel-small 439 | bounds:735,90,18,18 440 | transforms/wheel-big 441 | bounds:749,429,96,96 442 | transforms/wheel-big-overlay 443 | bounds:293,11,93,93 444 | transforms/wheel-mid 445 | bounds:570,192,68,68 446 | transforms/wheel-mid-overlay 447 | bounds:588,330,68,68 448 | vine/vine 449 | bounds:2,300,51,722 450 | -------------------------------------------------------------------------------- /examples/assets/spine40/atlas2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playcanvas/playcanvas-spine/9214f3043d35049f6efaa82c9ca54cd0bfe7917e/examples/assets/spine40/atlas2.png -------------------------------------------------------------------------------- /examples/assets/spine40/raptor.atlas: -------------------------------------------------------------------------------- 1 | raptor.png 2 | size: 1024, 512 3 | filter: Linear, Linear 4 | scale: 0.5 5 | back-arm 6 | bounds: 829, 88, 46, 25 7 | rotate: 90 8 | back-bracer 9 | bounds: 195, 238, 39, 28 10 | rotate: 90 11 | back-hand 12 | bounds: 724, 140, 36, 34 13 | rotate: 90 14 | back-knee 15 | bounds: 760, 131, 49, 67 16 | rotate: 90 17 | back-thigh 18 | bounds: 225, 238, 39, 24 19 | rotate: 90 20 | eyes-open 21 | bounds: 975, 204, 47, 45 22 | front-arm 23 | bounds: 969, 112, 48, 26 24 | front-bracer 25 | bounds: 724, 97, 41, 29 26 | rotate: 90 27 | front-hand 28 | bounds: 251, 239, 41, 38 29 | front-open-hand 30 | bounds: 856, 76, 43, 44 31 | rotate: 90 32 | front-thigh 33 | bounds: 729, 178, 57, 29 34 | rotate: 90 35 | gun 36 | bounds: 894, 251, 107, 103 37 | gun-nohand 38 | bounds: 764, 241, 105, 102 39 | head 40 | bounds: 756, 345, 136, 149 41 | lower-leg 42 | bounds: 475, 237, 73, 98 43 | rotate: 90 44 | mouth-grind 45 | bounds: 975, 172, 47, 30 46 | mouth-smile 47 | bounds: 975, 140, 47, 30 48 | neck 49 | bounds: 366, 282, 18, 21 50 | raptor-back-arm 51 | bounds: 636, 97, 82, 86 52 | rotate: 90 53 | raptor-body 54 | bounds: 2, 2, 632, 233 55 | raptor-front-arm 56 | bounds: 871, 168, 81, 102 57 | rotate: 90 58 | raptor-front-leg 59 | bounds: 2, 237, 191, 257 60 | raptor-hindleg-back 61 | bounds: 195, 279, 169, 215 62 | raptor-horn 63 | bounds: 431, 312, 182, 80 64 | rotate: 90 65 | raptor-horn-back 66 | bounds: 513, 318, 176, 77 67 | rotate: 90 68 | raptor-jaw 69 | bounds: 894, 356, 126, 138 70 | raptor-jaw-tooth 71 | bounds: 294, 240, 37, 48 72 | rotate: 90 73 | raptor-mouth-inside 74 | bounds: 344, 241, 36, 41 75 | rotate: 90 76 | raptor-saddle-strap-back 77 | bounds: 575, 242, 54, 74 78 | raptor-saddle-strap-front 79 | bounds: 764, 182, 57, 95 80 | rotate: 90 81 | raptor-saddle-w-shadow 82 | bounds: 592, 323, 162, 171 83 | raptor-tail-shadow 84 | bounds: 366, 305, 189, 63 85 | rotate: 90 86 | raptor-tongue 87 | bounds: 387, 239, 86, 64 88 | stirrup-back 89 | bounds: 829, 136, 44, 35 90 | rotate: 90 91 | stirrup-front 92 | bounds: 866, 121, 45, 50 93 | rotate: 90 94 | stirrup-strap 95 | bounds: 918, 120, 49, 46 96 | torso 97 | bounds: 636, 181, 54, 91 98 | rotate: 90 99 | visor 100 | bounds: 631, 237, 131, 84 101 | -------------------------------------------------------------------------------- /examples/assets/spine40/raptor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playcanvas/playcanvas-spine/9214f3043d35049f6efaa82c9ca54cd0bfe7917e/examples/assets/spine40/raptor.png -------------------------------------------------------------------------------- /examples/assets/spine41/README.md: -------------------------------------------------------------------------------- 1 | All the example files in this folder are Copyright Esoteric Software. They are provided as examples and test cases for the PlayCanvas plugin and not for use in projects. 2 | -------------------------------------------------------------------------------- /examples/assets/spine41/license.txt: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2013, Esoteric Software 3 | 4 | The images in this project may be redistributed as long as they are accompanied 5 | by this license file. The images may not be used for commercial use of any 6 | kind. 7 | 8 | The project file is released into the public domain. It may be used as the basis 9 | for derivative work. 10 | -------------------------------------------------------------------------------- /examples/assets/spine41/raptor.atlas: -------------------------------------------------------------------------------- 1 | raptor.png 2 | size: 1024, 512 3 | filter: Linear, Linear 4 | scale: 0.5 5 | back-arm 6 | bounds: 829, 88, 46, 25 7 | rotate: 90 8 | back-bracer 9 | bounds: 195, 238, 39, 28 10 | rotate: 90 11 | back-hand 12 | bounds: 724, 140, 36, 34 13 | rotate: 90 14 | back-knee 15 | bounds: 760, 131, 49, 67 16 | rotate: 90 17 | back-thigh 18 | bounds: 225, 238, 39, 24 19 | rotate: 90 20 | eyes-open 21 | bounds: 975, 204, 47, 45 22 | front-arm 23 | bounds: 969, 112, 48, 26 24 | front-bracer 25 | bounds: 724, 97, 41, 29 26 | rotate: 90 27 | front-hand 28 | bounds: 251, 239, 41, 38 29 | front-open-hand 30 | bounds: 856, 76, 43, 44 31 | rotate: 90 32 | front-thigh 33 | bounds: 729, 178, 57, 29 34 | rotate: 90 35 | gun 36 | bounds: 894, 251, 107, 103 37 | gun-nohand 38 | bounds: 764, 241, 105, 102 39 | head 40 | bounds: 756, 345, 136, 149 41 | lower-leg 42 | bounds: 475, 237, 73, 98 43 | rotate: 90 44 | mouth-grind 45 | bounds: 975, 172, 47, 30 46 | mouth-smile 47 | bounds: 975, 140, 47, 30 48 | neck 49 | bounds: 366, 282, 18, 21 50 | raptor-back-arm 51 | bounds: 636, 97, 82, 86 52 | rotate: 90 53 | raptor-body 54 | bounds: 2, 2, 632, 233 55 | raptor-front-arm 56 | bounds: 871, 168, 81, 102 57 | rotate: 90 58 | raptor-front-leg 59 | bounds: 2, 237, 191, 257 60 | raptor-hindleg-back 61 | bounds: 195, 279, 169, 215 62 | raptor-horn 63 | bounds: 431, 312, 182, 80 64 | rotate: 90 65 | raptor-horn-back 66 | bounds: 513, 318, 176, 77 67 | rotate: 90 68 | raptor-jaw 69 | bounds: 894, 356, 126, 138 70 | raptor-jaw-tooth 71 | bounds: 294, 240, 37, 48 72 | rotate: 90 73 | raptor-mouth-inside 74 | bounds: 344, 241, 36, 41 75 | rotate: 90 76 | raptor-saddle-strap-back 77 | bounds: 575, 242, 54, 74 78 | raptor-saddle-strap-front 79 | bounds: 764, 182, 57, 95 80 | rotate: 90 81 | raptor-saddle-w-shadow 82 | bounds: 592, 323, 162, 171 83 | raptor-tail-shadow 84 | bounds: 366, 305, 189, 63 85 | rotate: 90 86 | raptor-tongue 87 | bounds: 387, 239, 86, 64 88 | stirrup-back 89 | bounds: 829, 136, 44, 35 90 | rotate: 90 91 | stirrup-front 92 | bounds: 866, 121, 45, 50 93 | rotate: 90 94 | stirrup-strap 95 | bounds: 918, 120, 49, 46 96 | torso 97 | bounds: 636, 181, 54, 91 98 | rotate: 90 99 | visor 100 | bounds: 631, 237, 131, 84 101 | -------------------------------------------------------------------------------- /examples/assets/spine41/raptor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playcanvas/playcanvas-spine/9214f3043d35049f6efaa82c9ca54cd0bfe7917e/examples/assets/spine41/raptor.png -------------------------------------------------------------------------------- /examples/dragon36.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PlayCanvas Spine - Dragon 5 | 6 | 7 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /examples/goblins36.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PlayCanvas Spine - Goblins 5 | 6 | 7 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /examples/hero36.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PlayCanvas Spine - Hero 5 | 6 | 7 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /examples/owl40.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PlayCanvas Spine 4.0 - Owl 6 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /examples/raptor40.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PlayCanvas Spine 4.0 - Raptor 6 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /examples/raptor41.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PlayCanvas Spine 4.1 - Raptor 5 | 6 | 7 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /examples/spineboy38.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PlayCanvas Spine - SpineBoy 5 | 6 | 7 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /images/spine-man.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playcanvas/playcanvas-spine/9214f3043d35049f6efaa82c9ca54cd0bfe7917e/images/spine-man.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playcanvas-spine", 3 | "version": "1.0.2", 4 | "author": "PlayCanvas ", 5 | "homepage": "https://playcanvas.com", 6 | "description": "Spine plugin for PlayCanvas Engine", 7 | "keywords": [ 8 | "playcanvas", 9 | "spine", 10 | "animation", 11 | "webgl", 12 | "webgl2", 13 | "2d" 14 | ], 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/playcanvas/playcanvas-spine/issues" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/playcanvas/playcanvas-spine.git" 22 | }, 23 | "eslintConfig": { 24 | "extends": "@playcanvas/eslint-config", 25 | "settings": { 26 | "import/core-modules": [ 27 | "spine-core-import" 28 | ] 29 | } 30 | }, 31 | "devDependencies": { 32 | "@babel/preset-env": "^7.24.5", 33 | "@playcanvas/eslint-config": "^1.7.1", 34 | "@rollup/plugin-alias": "^5.1.0", 35 | "@rollup/plugin-babel": "^6.0.4", 36 | "@rollup/plugin-commonjs": "^25.0.7", 37 | "@rollup/plugin-node-resolve": "^15.2.3", 38 | "@rollup/plugin-terser": "^0.4.4", 39 | "eslint": "^8.49.0", 40 | "playcanvas": "^1.70.2", 41 | "rollup": "^4.17.2", 42 | "semver": "^7.6.1", 43 | "serve": "^14.2.3", 44 | "spine40": "npm:@esotericsoftware/spine-core@4.0", 45 | "spine41": "npm:@esotericsoftware/spine-core@4.1", 46 | "spine42": "npm:@esotericsoftware/spine-core@4.2" 47 | }, 48 | "scripts": { 49 | "build": "rollup -c", 50 | "lint": "eslint src", 51 | "serve": "serve ./ -l 8080 --no-request-logging" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | // rollup.config.mjs 2 | 3 | import commonjs from '@rollup/plugin-commonjs'; 4 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 5 | import terser from '@rollup/plugin-terser'; 6 | import alias from '@rollup/plugin-alias'; 7 | import { babel } from '@rollup/plugin-babel'; 8 | 9 | /** @typedef {import('rollup').RollupOptions} RollupOptions */ 10 | /** @typedef {import('rollup').Plugin} Plugin */ 11 | /** @typedef {import('rollup').OutputOptions} OutputOptions */ 12 | /** @typedef {import('@rollup/plugin-babel').RollupBabelInputPluginOptions} RollupBabelInputPluginOptions */ 13 | 14 | const banner = `/* Copyright 2015-${new Date().getFullYear()} PlayCanvas Ltd */\n`; 15 | 16 | /** 17 | * @example 18 | * { 19 | * name: 'playcanvas-spine.4.0', 20 | * lib: 'spine40' // replacement for import alias 21 | * } 22 | */ 23 | const builds = [ 24 | { 25 | name: 'playcanvas-spine.3.6', 26 | lib: 'contrib/spine-ts/build/3.6/spine-core.js' 27 | }, 28 | { 29 | name: 'playcanvas-spine.3.8', 30 | lib: 'contrib/spine-ts/build/3.8/spine-core.js' 31 | }, 32 | { 33 | name: 'playcanvas-spine.4.0', 34 | lib: 'src/wrapper40.js' 35 | }, 36 | { 37 | name: 'playcanvas-spine.4.1', 38 | lib: 'src/wrapper41.js' 39 | }, 40 | { 41 | name: 'playcanvas-spine.4.2', 42 | lib: 'src/wrapper42.js' 43 | } 44 | ]; 45 | 46 | /** 47 | * The ES5 options for babel(...) plugin. 48 | * 49 | * @param {'debug'|'release'|'profiler'|'min'} buildType - Use 'debug' for comments 50 | * @returns {RollupBabelInputPluginOptions} The babel options. 51 | */ 52 | const es5Options = buildType => ({ 53 | babelHelpers: 'bundled', 54 | babelrc: false, 55 | comments: buildType === 'debug', 56 | compact: false, 57 | minified: false, 58 | presets: [ 59 | [ 60 | '@babel/preset-env', { 61 | modules: false, 62 | targets: { 63 | ie: '11' 64 | } 65 | } 66 | ] 67 | ] 68 | }); 69 | 70 | /** 71 | * Return all the build targets. 72 | * 73 | * @returns {RollupOptions | RollupOptions[]} Build targets. 74 | */ 75 | export default [ 76 | ...builds.map(buildDefinition => buildTarget(buildDefinition)) 77 | ]; 78 | 79 | /** 80 | * Return a target that rollup is supposed to build. 81 | * 82 | * @param {{ name, lib }} buildDefinition - The build definition. 83 | * @returns {RollupOptions} One rollup target. 84 | */ 85 | function buildTarget({ name, lib }) { 86 | 87 | const outputDir = './build/'; 88 | 89 | const outputPlugins = { 90 | release: [], 91 | min: [terser()] 92 | }; 93 | 94 | const outputGlobals = { 95 | playcanvas: 'pc' 96 | }; 97 | 98 | // spine-core-import is an alias 99 | const entries = { 'spine-core-import': lib }; 100 | 101 | const buildPlugins = [alias({ entries }), commonjs(), nodeResolve(), babel(es5Options('release'))]; 102 | 103 | return { 104 | external: ['playcanvas'], 105 | input: 'src/SpinePlugin.js', 106 | context: 'this', // remove when using ESM libs 107 | output: [ 108 | { 109 | file: `${outputDir + name}.js`, 110 | format: 'iife', 111 | name: 'spine', 112 | banner: banner, 113 | indent: '\t', 114 | preserveModules: false, 115 | globals: outputGlobals, 116 | generatedCode: 'es5', // refers to rollup wrappers 117 | plugins: outputPlugins.release 118 | }, 119 | { 120 | file: `${outputDir + name}.min.js`, 121 | format: 'iife', 122 | name: 'spine', 123 | banner: banner, 124 | preserveModules: false, 125 | globals: outputGlobals, 126 | generatedCode: 'es5', // refers to rollup wrappers 127 | plugins: outputPlugins.min 128 | } 129 | ], 130 | plugins: buildPlugins 131 | }; 132 | } 133 | -------------------------------------------------------------------------------- /src/SpinePlugin.js: -------------------------------------------------------------------------------- 1 | import * as pc from 'playcanvas'; 2 | import { spine } from 'spine-core-import'; // spine-core-import is an alias 3 | import { SpineComponentSystem } from './component/SpineComponentSystem.js'; 4 | 5 | /** 6 | * This plugin provides a Component System to PlayCanvas Engine 7 | * and a spine-core runtime to user scripts as global called spine. 8 | * 9 | * For users following spine examples, this plugin is equivalent to 10 | * the including the standard spine-core iife runtime in the editor. 11 | */ 12 | 13 | // register the plugin with playcanvas 14 | (function () { 15 | const app = pc.Application.getApplication(); 16 | if (!app) { 17 | console.warn("No Application found. An Application or AppBase must be instantiated before `playcanvas-spine`."); 18 | return; 19 | } 20 | const system = new SpineComponentSystem(app); 21 | app.systems.add(system); 22 | }()); 23 | 24 | // we export spine for compatibility and to allow developers 25 | // to access the full spine library in their code. 26 | // See line 132 of the hero demo. 27 | export default spine; 28 | -------------------------------------------------------------------------------- /src/component/Spine.js: -------------------------------------------------------------------------------- 1 | import * as pc from 'playcanvas'; 2 | import { spine } from 'spine-core-import'; // spine-core-import is an alias 3 | import { SpineTextureWrapper } from './SpineTextureWrapper.js'; 4 | import semver from './semver/index.js'; 5 | 6 | const ATTACHMENT_TYPE = { 7 | NULL: 0, 8 | MESH: 1, 9 | REGION: 2 10 | }; 11 | 12 | // index data for region (quad) type of attachment 13 | const QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0]; 14 | 15 | /** 16 | * A Spine animation object. 17 | */ 18 | class Spine { 19 | /** 20 | * Determines whether the Spine object calls skeleton.updateWorldTransform in the update loop. 21 | * Default is true. 22 | */ 23 | autoUpdate = true; 24 | 25 | /** 26 | * The Skeleton object. 27 | * 28 | * @type {spine.Skeleton} 29 | */ 30 | skeleton; 31 | 32 | /** 33 | * A list of all AnimationState objects. 34 | * 35 | * @type {spine.AnimationState[]} 36 | */ 37 | states; 38 | 39 | /** 40 | * Contains the skeleton and animation states as detailed in the Spine Runtime documentation. 41 | * 42 | * @param {pc.AppBase} app - The application that will manage this Spine object. 43 | * @param {string} atlasData - Text data loaded from the atlas file. 44 | * @param {object} skeletonData - JSON data loaded from the skeleton file. 45 | * @param {object} textureData - Texture initialization data. An object where the key is the 46 | * texture filename and the value is the pc.Texture resource. 47 | */ 48 | constructor(app, atlasData, skeletonData, textureData) { 49 | this._app = app; 50 | 51 | this._position = new pc.Vec3(); 52 | 53 | let atlas; 54 | 55 | // EP: As instructed: https://github.com/playcanvas/playcanvas-spine/pull/73 56 | // 57 | // API differs before we can know which which file version this is supposed to support. 58 | // The data file opened will determine which code paths are used. This file may 59 | // or may not be concatenated with the spine library that supports it. 60 | // 61 | // use the length of the function signatures to guess which library has been concatenated. 62 | if (spine.TextureAtlas.length === 1) { 63 | // spine 4.1 64 | atlas = new spine.TextureAtlas(atlasData); 65 | for (const page of atlas.pages) { 66 | page.setTexture( 67 | // @ts-ignore 68 | new SpineTextureWrapper(textureData[page.name]) 69 | ); 70 | } 71 | } else { 72 | // spine 3.6 and 3.8 73 | atlas = new spine.TextureAtlas( 74 | atlasData, 75 | // @ts-ignore 76 | (path) => { 77 | return new SpineTextureWrapper(textureData[path]); 78 | } 79 | ); 80 | } 81 | 82 | const json = new spine.SkeletonJson( 83 | new spine.AtlasAttachmentLoader(atlas) 84 | ); 85 | json.scale *= 0.01; 86 | const _skeletonData = json.readSkeletonData(skeletonData); 87 | 88 | // Compatibility queries 89 | this.skeletonVersion = semver.valid( 90 | semver.coerce(_skeletonData.version) 91 | ); 92 | this._spine_3_6_0 = semver.satisfies(this.skeletonVersion, '<=3.6.0'); // version 3.6.0 or below 93 | this._spine_3_7_99 = semver.satisfies(this.skeletonVersion, '<=3.7.99'); // version 3.7.99 or below 94 | this._spine_4_0_X = semver.satisfies(this.skeletonVersion, '~4.0.0'); // version 4.0 family (4.0.31 - 4.0.79-beta) 95 | this._spine_4_1_X = semver.satisfies(this.skeletonVersion, '~4.1.23'); // version 4.1 family 96 | 97 | this.skeleton = new spine.Skeleton(_skeletonData); 98 | this.skeleton.updateWorldTransform(); 99 | 100 | this.stateData = new spine.AnimationStateData(this.skeleton.data); 101 | this.states = [new spine.AnimationState(this.stateData)]; 102 | this.clipper = new spine.SkeletonClipping(); 103 | 104 | this._node = new pc.GraphNode(); 105 | this._meshes = []; 106 | this._meshInstances = []; 107 | this._materials = {}; 108 | this._tint = {}; 109 | 110 | this._aabb = new pc.BoundingBox(); 111 | this._aabbTempArray = []; 112 | this._aabbTempOffset = new pc.Vec2(); 113 | this._aabbTempSize = new pc.Vec2(); 114 | 115 | this._renderCounts = { vertexCount: 0, indexCount: 0 }; 116 | this._vertexFormat = null; 117 | this._vertexBuffer = null; 118 | this._indexBuffer = null; 119 | 120 | this._priority = 0; 121 | this._timeScale = 1; 122 | this._layers = [pc.LAYERID_UI]; 123 | 124 | this.init(); 125 | 126 | this._hidden = false; 127 | } 128 | 129 | destroy() { 130 | this.removeFromLayers(); 131 | 132 | for (let i = 0; i < this._meshInstances.length; i++) { 133 | this._meshInstances[i].mesh.vertexBuffer = null; 134 | this._meshInstances[i].mesh.indexBuffer.length = 0; 135 | this._meshInstances[i].material = null; 136 | } 137 | 138 | if (this._vertexBuffer) { 139 | this._vertexBuffer.destroy(); 140 | this._vertexBuffer = null; 141 | } 142 | 143 | if (this._indexBuffer) { 144 | this._indexBuffer.destroy(); 145 | this._indexBuffer = null; 146 | } 147 | 148 | this._meshInstances = []; 149 | this.skeleton = null; 150 | this.stateData = null; 151 | this._materials = {}; 152 | this._node = null; 153 | } 154 | 155 | hide() { 156 | if (this._hidden) return; 157 | 158 | for (let i = 0, n = this._meshInstances.length; i < n; i++) { 159 | this._meshInstances[i].visible = false; 160 | } 161 | 162 | this._hidden = true; 163 | } 164 | 165 | show() { 166 | if (!this._hidden) return; 167 | 168 | for (let i = 0, n = this._meshInstances.length; i < n; i++) { 169 | this._meshInstances[i].visible = true; 170 | } 171 | 172 | this._hidden = false; 173 | } 174 | 175 | init() { 176 | // vertex format 177 | this._vertexFormat = new pc.VertexFormat(this._app.graphicsDevice, [ 178 | { 179 | semantic: pc.SEMANTIC_POSITION, 180 | components: 2, 181 | type: pc.TYPE_FLOAT32 182 | }, 183 | { 184 | semantic: pc.SEMANTIC_NORMAL, 185 | components: 4, 186 | type: pc.TYPE_UINT8, 187 | normalize: true 188 | }, 189 | { 190 | semantic: pc.SEMANTIC_TEXCOORD0, 191 | components: 2, 192 | type: pc.TYPE_FLOAT32 193 | }, 194 | { 195 | semantic: pc.SEMANTIC_COLOR, 196 | components: 4, 197 | type: pc.TYPE_UINT8, 198 | normalize: true 199 | } 200 | ]); 201 | 202 | // init slots 203 | const drawOrder = this.skeleton.drawOrder; 204 | for (let i = 0, n = drawOrder.length; i < n; i++) { 205 | this.initSlot(drawOrder[i]); 206 | } 207 | } 208 | 209 | initSlot(slot) { 210 | slot.positions = []; // vec2 world space positions 211 | slot.uvs = []; // vec2 uv coordinates 212 | slot.indices = []; // triangle indices 213 | slot.vertexColor = {}; // rgba color of the slot 214 | 215 | // last update name and type, used to detect when attachment changes on a slot 216 | slot._active = { name: '', type: ATTACHMENT_TYPE.NULL }; 217 | 218 | this.initAttachment(slot); 219 | } 220 | 221 | createMaterial(texture) { 222 | const material = new pc.StandardMaterial(); 223 | 224 | material.emissiveMap = texture; 225 | material.emissiveVertexColor = true; 226 | material.emissive = pc.Color.WHITE; 227 | 228 | material.opacityMap = texture; 229 | material.opacityVertexColor = true; 230 | 231 | material.depthWrite = false; 232 | material.cull = pc.CULLFACE_NONE; 233 | material.blendType = pc.BLEND_PREMULTIPLIED; 234 | 235 | if (this._spine_3_6_0) { 236 | // override premultiplied chunk because images are already premultiplied however the opacity is not premultiplied by slot alpha 237 | const alphaPremul = [ 238 | 'gl_FragColor.rgb *= vVertexColor.a;', 239 | 'gl_FragColor.a = dAlpha;' 240 | ].join('\n'); 241 | material.chunks.outputAlphaPremulPS = alphaPremul; 242 | } 243 | 244 | material.update(); 245 | return material; 246 | } 247 | 248 | initAttachment(slot) { 249 | const attachment = slot.attachment; 250 | if (attachment) { 251 | slot._active.name = attachment.name; 252 | if (attachment instanceof spine.RegionAttachment) { 253 | slot._active.type = ATTACHMENT_TYPE.REGION; 254 | } else if (attachment instanceof spine.MeshAttachment) { 255 | slot._active.type = ATTACHMENT_TYPE.MESH; 256 | } 257 | 258 | let texture = null; 259 | 260 | // search for texture property if it exists 261 | if (attachment.region) { 262 | if (attachment.region.texture) { 263 | texture = attachment.region.texture.pcTexture; 264 | } 265 | if (attachment.region.page && attachment.region.page.texture) { 266 | texture = attachment.region.page.texture.pcTexture; 267 | } 268 | } 269 | 270 | // create / assign material 271 | if (texture) { 272 | if (texture instanceof pc.StandardMaterial) { 273 | this._materials[texture.name] = texture; 274 | slot.material = texture.name; 275 | } else { 276 | // get a unique key for the texture 277 | let key = null; 278 | if (texture.name) { 279 | key = texture.name; // texture name might not be unique - should be resolved with content 280 | } else if (texture.getSource() instanceof Image) { 281 | key = texture.getSource().getAttribute('src'); 282 | } 283 | if (key) { 284 | // create a new material if required 285 | if (this._materials[key] === undefined) { 286 | const material = this.createMaterial(texture); 287 | this._materials[key] = material; 288 | } 289 | slot.material = key; 290 | } 291 | } 292 | } 293 | } 294 | } 295 | 296 | updateSlot(slot, clipper) { 297 | const attachment = slot.attachment; 298 | const name = attachment.name; 299 | 300 | // attachment can change on the slot 301 | // prettier-ignore 302 | const type = 303 | attachment instanceof spine.RegionAttachment ? 304 | ATTACHMENT_TYPE.REGION : 305 | attachment instanceof spine.MeshAttachment ? 306 | ATTACHMENT_TYPE.MESH : 307 | ATTACHMENT_TYPE.NULL; 308 | 309 | if (slot._active.name !== name || slot._active.type !== type) { 310 | this.initAttachment(slot); 311 | } 312 | 313 | // convert vertices to world space 314 | slot.positions.length = 0; 315 | if (attachment instanceof spine.RegionAttachment) { 316 | if (this._spine_4_1_X) { 317 | attachment.computeWorldVertices(slot, slot.positions, 0, 2); 318 | } else { 319 | attachment.computeWorldVertices( 320 | slot.bone, 321 | slot.positions, 322 | 0, 323 | 2 324 | ); 325 | } 326 | } else if (attachment instanceof spine.MeshAttachment) { 327 | attachment.computeWorldVertices( 328 | slot, 329 | 0, 330 | attachment.worldVerticesLength, 331 | slot.positions, 332 | 0, 333 | 2 334 | ); 335 | } 336 | 337 | // mesh vertex color 338 | const tint = this._tint[name]; 339 | slot.vertexColor = { 340 | r: Math.round(255 * slot.color.r * (tint ? tint.r : 1)), 341 | g: Math.round(255 * slot.color.g * (tint ? tint.g : 1)), 342 | b: Math.round(255 * slot.color.b * (tint ? tint.b : 1)), 343 | a: Math.round(255 * slot.color.a * (tint ? tint.a : 1)) 344 | }; 345 | 346 | // indices 347 | const srcTriangles = attachment.triangles || QUAD_TRIANGLES; 348 | 349 | let i; 350 | let count; 351 | 352 | if (clipper.isClipping()) { 353 | // clip triangles on CPU 354 | const twoColorTint = false; 355 | clipper.clipTriangles( 356 | slot.positions, 357 | 0, 358 | srcTriangles, 359 | srcTriangles.length, 360 | attachment.uvs, 361 | spine.Color.WHITE, 362 | spine.Color.WHITE, 363 | twoColorTint 364 | ); 365 | 366 | // copy clipped vertex data 367 | slot.positions.length = 0; 368 | slot.uvs.length = 0; 369 | const vertexSize = twoColorTint ? 12 : 8; // clipper output format 370 | count = clipper.clippedVertices.length; 371 | for (i = 0; i < count; i += vertexSize) { 372 | slot.positions.push( 373 | clipper.clippedVertices[i], 374 | clipper.clippedVertices[i + 1] 375 | ); 376 | slot.uvs.push( 377 | clipper.clippedVertices[i + 6], 378 | 1 - clipper.clippedVertices[i + 7] 379 | ); 380 | } 381 | 382 | // copy clipped index data 383 | slot.indices = clipper.clippedTriangles.slice(); 384 | } else { 385 | // copy vertex data (uvs only, position was filled in already) 386 | slot.uvs.length = 0; 387 | count = slot.positions.length; 388 | for (i = 0; i < count; i += 2) { 389 | slot.uvs.push(attachment.uvs[i], 1 - attachment.uvs[i + 1]); 390 | } 391 | 392 | // reference index data 393 | slot.indices = srcTriangles; 394 | } 395 | 396 | // update overall counts 397 | this._renderCounts.vertexCount += slot.positions.length / 2; 398 | this._renderCounts.indexCount += slot.indices.length; 399 | } 400 | 401 | updateSkeleton(dt) { 402 | // count vertices and triangles 403 | this._renderCounts.vertexCount = 0; 404 | this._renderCounts.indexCount = 0; 405 | 406 | // handle clipping start / end / range 407 | const clipper = this.clipper; 408 | const slotRangeStart = -1; 409 | const slotRangeEnd = -1; 410 | let inRange = false; 411 | if (slotRangeStart === -1) inRange = true; 412 | 413 | const drawOrder = this.skeleton.drawOrder; 414 | const count = drawOrder.length; 415 | for (let i = 0; i < count; i++) { 416 | const slot = drawOrder[i]; 417 | 418 | if (!this._spine_3_7_99) { 419 | if (!slot.bone.active) { 420 | clipper.clipEndWithSlot(slot); 421 | continue; 422 | } 423 | } 424 | 425 | if (slotRangeStart >= 0 && slotRangeStart === slot.data.index) { 426 | inRange = true; 427 | } 428 | 429 | if (!inRange) { 430 | clipper.clipEndWithSlot(slot); 431 | continue; 432 | } 433 | 434 | if (slotRangeEnd >= 0 && slotRangeEnd === slot.data.index) { 435 | inRange = false; 436 | } 437 | 438 | const attachment = slot.getAttachment(); 439 | if (attachment instanceof spine.ClippingAttachment) { 440 | clipper.clipStart(slot, attachment); 441 | continue; 442 | } else if ( 443 | !(attachment instanceof spine.RegionAttachment) && 444 | !(attachment instanceof spine.MeshAttachment) 445 | ) { 446 | if (!this._spine_3_7_99) clipper.clipEndWithSlot(slot); 447 | continue; 448 | } 449 | 450 | // update slot geometry 451 | this.updateSlot(slot, clipper); 452 | } 453 | } 454 | 455 | render() { 456 | // remove materials from meshInstances as they keep references to meshInstances not allowing them to be GC'd 457 | this._meshInstances.forEach((instance) => { 458 | instance.material = null; 459 | }); 460 | 461 | this.removeFromLayers(); 462 | this._meshes = []; 463 | this._meshInstances.length = 0; 464 | 465 | // any vertices / triangles to render 466 | if ( 467 | this._renderCounts.indexCount > 0 && 468 | this._renderCounts.vertexCount > 0 469 | ) { 470 | // update aabb 471 | this.skeleton.getBounds( 472 | this._aabbTempOffset, 473 | this._aabbTempSize, 474 | this._aabbTempArray 475 | ); 476 | this._aabb.center = new pc.Vec3( 477 | this._aabbTempOffset.x, 478 | this._aabbTempOffset.y, 479 | 0 480 | ); 481 | this._aabb.halfExtents = new pc.Vec3( 482 | 0.5 * this._aabbTempSize.x, 483 | 0.5 * this._aabbTempSize.y, 484 | 0 485 | ); 486 | 487 | // make vertex buffer at least required size 488 | if ( 489 | !this._vertexBuffer || 490 | this._vertexBuffer.getNumVertices() < 491 | this._renderCounts.vertexCount 492 | ) { 493 | if (this._vertexBuffer) this._vertexBuffer.destroy(); 494 | 495 | this._vertexBuffer = new pc.VertexBuffer( 496 | this._app.graphicsDevice, 497 | this._vertexFormat, 498 | this._renderCounts.vertexCount 499 | ); 500 | } 501 | 502 | // make index buffer at least required size 503 | if ( 504 | !this._indexBuffer || 505 | this._indexBuffer.getNumIndices() < 506 | this._renderCounts.indexCount 507 | ) { 508 | if (this._indexBuffer) this._indexBuffer.destroy(); 509 | 510 | this._indexBuffer = new pc.IndexBuffer( 511 | this._app.graphicsDevice, 512 | pc.INDEXFORMAT_UINT16, 513 | this._renderCounts.indexCount 514 | ); 515 | } 516 | 517 | // batching start 518 | let currentMaterialKey = null; 519 | let batchStartIndex = 0; 520 | let batchIndexCount = 0; 521 | 522 | // vertex / index buffer access 523 | const dstVertices = new pc.VertexIterator(this._vertexBuffer); 524 | const dstIndices = new Uint16Array(this._indexBuffer.lock()); 525 | let dstIndexOffset = 0; 526 | let dstVertexOffset = 0; 527 | 528 | const drawOrder = this.skeleton.drawOrder; 529 | const count = drawOrder.length; 530 | for (let i = 0; i < count; i++) { 531 | const slot = drawOrder[i]; 532 | 533 | if ( 534 | slot.attachment && 535 | slot.material && 536 | slot.positions.length > 0 && 537 | slot.indices.length > 0 538 | ) { 539 | // material switch 540 | if ( 541 | currentMaterialKey && 542 | currentMaterialKey !== slot.material 543 | ) { 544 | this.SubmitBatch( 545 | batchStartIndex, 546 | batchIndexCount, 547 | currentMaterialKey 548 | ); 549 | 550 | // prepare next batch 551 | currentMaterialKey = slot.material; 552 | batchStartIndex = dstIndexOffset; 553 | batchIndexCount = 0; 554 | } 555 | currentMaterialKey = slot.material; 556 | 557 | // write vertex data 558 | const positions = slot.positions; 559 | const r = slot.vertexColor.r; 560 | const g = slot.vertexColor.g; 561 | const b = slot.vertexColor.b; 562 | const a = slot.vertexColor.a; 563 | const uvs = slot.uvs; 564 | let j; 565 | const posCount = positions.length / 2; 566 | 567 | for (j = 0; j < posCount; j++) { 568 | dstVertices.element[pc.SEMANTIC_POSITION].set( 569 | positions[j * 2], 570 | positions[j * 2 + 1] 571 | ); 572 | dstVertices.element[pc.SEMANTIC_NORMAL].set( 573 | 0, 574 | 255, 575 | 0, 576 | 0 577 | ); // 0,1,0 normal stored in 8 bit per component 578 | dstVertices.element[pc.SEMANTIC_COLOR].set(r, g, b, a); 579 | dstVertices.element[pc.SEMANTIC_TEXCOORD0].set( 580 | uvs[j * 2], 581 | 1.0 - uvs[j * 2 + 1] 582 | ); 583 | dstVertices.next(); 584 | } 585 | 586 | // write index data 587 | const indices = slot.indices; 588 | const indCount = indices.length; 589 | for (j = 0; j < indCount; j++) 590 | dstIndices[dstIndexOffset + j] = 591 | indices[j] + dstVertexOffset; 592 | 593 | batchIndexCount += indCount; 594 | dstIndexOffset += indCount; 595 | dstVertexOffset += posCount; 596 | } 597 | } 598 | 599 | dstVertices.end(); 600 | this._indexBuffer.unlock(); 601 | 602 | // final batch 603 | this.SubmitBatch( 604 | batchStartIndex, 605 | batchIndexCount, 606 | currentMaterialKey 607 | ); 608 | } 609 | 610 | // add all instances to layers 611 | this.addToLayers(); 612 | } 613 | 614 | SubmitBatch(indexBase, indexCount, materialKey) { 615 | if (indexCount > 0) { 616 | const mesh = new pc.Mesh(this._app.graphicsDevice); 617 | 618 | mesh.vertexBuffer = this._vertexBuffer; 619 | mesh.indexBuffer[0] = this._indexBuffer; 620 | mesh.primitive[0].type = pc.PRIMITIVE_TRIANGLES; 621 | mesh.primitive[0].base = indexBase; 622 | mesh.primitive[0].count = indexCount; 623 | mesh.primitive[0].indexed = true; 624 | mesh.aabb = this._aabb; 625 | this._meshes.push(mesh); 626 | 627 | const mi = new pc.MeshInstance( 628 | mesh, 629 | this._materials[materialKey], 630 | this._node 631 | ); 632 | mi.drawOrder = this.priority + this._meshInstances.length; 633 | mi.visible = !this._hidden; 634 | this._meshInstances.push(mi); 635 | } 636 | } 637 | 638 | update(dt) { 639 | if (this._hidden) return; 640 | 641 | dt *= this._timeScale; 642 | 643 | let i; 644 | const n = this.states.length; 645 | for (i = 0; i < n; i++) { 646 | this.states[i].update(dt); 647 | } 648 | 649 | for (i = 0; i < n; i++) { 650 | this.states[i].apply(this.skeleton); 651 | } 652 | 653 | if (this.autoUpdate) { 654 | this.skeleton.updateWorldTransform(); 655 | } 656 | 657 | this.updateSkeleton(); 658 | this.render(); 659 | } 660 | 661 | setPosition(p) { 662 | this._position.copy(p); 663 | } 664 | 665 | setTint(name, color) { 666 | this._tint[name] = color; 667 | } 668 | 669 | removeFromLayers() { 670 | if (this._meshInstances.length) { 671 | for (let i = 0; i < this._layers.length; i++) { 672 | const layer = this._app.scene.layers.getLayerById( 673 | this._layers[i] 674 | ); 675 | if (layer) layer.removeMeshInstances(this._meshInstances); 676 | } 677 | } 678 | } 679 | 680 | addToLayers() { 681 | if (this._meshInstances.length) { 682 | for (let i = 0; i < this._layers.length; i++) { 683 | const layer = this._app.scene.layers.getLayerById( 684 | this._layers[i] 685 | ); 686 | if (layer) layer.addMeshInstances(this._meshInstances); 687 | } 688 | } 689 | } 690 | 691 | /** 692 | * The first AnimationState object. There is always one AnimationState. 693 | * 694 | * @type {spine.AnimationState} 695 | */ 696 | get state() { 697 | return this.states[0]; 698 | } 699 | 700 | /** 701 | * An integer value which determines when the spine mesh is rendered relative to other Spine 702 | * meshes. Lower numbers are rendered first. 703 | * 704 | * @type {number} 705 | */ 706 | set priority(value) { 707 | this._priority = value; 708 | } 709 | 710 | get priority() { 711 | return this._priority; 712 | } 713 | 714 | set timeScale(value) { 715 | this._timeScale = value; 716 | } 717 | 718 | get timeScale() { 719 | return this._timeScale; 720 | } 721 | 722 | set layers(value) { 723 | this.removeFromLayers(); 724 | this._layers = value || []; 725 | this.addToLayers(); 726 | } 727 | 728 | get layers() { 729 | return this._layers; 730 | } 731 | } 732 | 733 | export { Spine }; 734 | -------------------------------------------------------------------------------- /src/component/SpineComponent.js: -------------------------------------------------------------------------------- 1 | import { Asset, Component, path as resourcePath } from 'playcanvas'; 2 | 3 | import { Spine } from './Spine.js'; 4 | 5 | class SpineComponent extends Component { 6 | constructor(system, entity) { 7 | super(system, entity); 8 | 9 | this.on('set_atlasAsset', this.onSetAsset, this); 10 | this.on('set_textureAssets', this.onSetAssets, this); 11 | this.on('set_skeletonAsset', this.onSetAsset, this); 12 | 13 | this.on('set_atlasData', this.onSetResource, this); 14 | this.on('set_textures', this.onSetResource, this); 15 | this.on('set_skeletonData', this.onSetResource, this); 16 | } 17 | 18 | _createSpine() { 19 | if (this.data.spine) { 20 | this.data.spine.destroy(); 21 | this.data.spine = null; 22 | } 23 | 24 | const textureData = {}; 25 | for (let i = 0, n = this.textureAssets.length; i < n; i++) { 26 | const asset = this.system.app.assets.get(this.textureAssets[i]); 27 | 28 | let path = asset.name ? 29 | asset.name : 30 | asset.file ? 31 | asset.file.filename : 32 | null; 33 | 34 | // Fallback if filename doesn't exist 35 | if (!path) { 36 | path = resourcePath.getBasename(asset.file.url); 37 | } 38 | 39 | const query = path.indexOf('?'); 40 | if (query !== -1) path = path.substring(0, query); 41 | 42 | textureData[path] = asset.resource; 43 | } 44 | 45 | this.data.spine = new Spine( 46 | this.system.app, 47 | this.atlasData, 48 | this.skeletonData, 49 | textureData 50 | ); 51 | 52 | this.state = this.data.spine.state; 53 | this.states = this.data.spine.states; 54 | this.skeleton = this.data.spine.skeleton; 55 | 56 | this.entity.addChild(this.data.spine._node); 57 | } 58 | 59 | _onAssetReady({ type, resource }) { 60 | if (type === 'texture') { 61 | this.textures.push(resource); 62 | } 63 | if (type === 'json') { 64 | this.skeletonData = resource; 65 | } 66 | if (type === 'text') { 67 | this.atlasData = resource; 68 | } 69 | } 70 | 71 | _onAssetAdd(asset) { 72 | asset.off('change', this.onAssetChanged, this); 73 | asset.on('change', this.onAssetChanged, this); 74 | 75 | asset.off('remove', this.onAssetRemoved, this); 76 | asset.on('remove', this.onAssetRemoved, this); 77 | 78 | asset.ready(this._onAssetReady, this); 79 | this.system.app.assets.load(asset); 80 | } 81 | 82 | onSetResource() { 83 | if ( 84 | this.data.atlasData && 85 | this.data.textures.length && 86 | this.data.skeletonData 87 | ) { 88 | this._createSpine(); 89 | } 90 | } 91 | 92 | onSetAsset(name, oldValue, newValue) { 93 | const registry = this.system.app.assets; 94 | let asset = null; 95 | if (oldValue) { 96 | asset = registry.get(oldValue); 97 | if (asset) { 98 | asset.off('change', this.onAssetChanged); 99 | asset.off('remove', this.onAssetRemoved); 100 | } 101 | } 102 | 103 | if (newValue) { 104 | let id = newValue; 105 | if (newValue instanceof Asset) { 106 | id = newValue.id; 107 | this.data[name] = id; 108 | } 109 | asset = registry.get(id); 110 | if (asset) { 111 | this._onAssetAdd(asset); 112 | } else { 113 | registry.on(`add:${id}`); 114 | } 115 | } 116 | } 117 | 118 | onSetAssets(name, oldValue, newValue) { 119 | const registry = this.system.app.assets; 120 | let asset = null; 121 | let i; 122 | let n; 123 | if (oldValue.length) { 124 | for (i = 0, n = oldValue.length; i < n; i++) { 125 | asset = registry.get(oldValue[i]); 126 | if (asset) { 127 | asset.off('change', this.onAssetChanged); 128 | asset.off('remove', this.onAssetRemoved); 129 | } 130 | } 131 | } 132 | 133 | if (newValue && newValue.length) { 134 | const ids = newValue.map((v) => { 135 | if (v instanceof Asset) { 136 | return v.id; 137 | } 138 | return v; 139 | }); 140 | 141 | for (i = 0, n = newValue.length; i < n; i++) { 142 | asset = registry.get(ids[i]); 143 | if (asset) { 144 | this._onAssetAdd(asset); 145 | } else { 146 | registry.on(`add:${ids[i]}`); 147 | } 148 | } 149 | } 150 | } 151 | 152 | onAssetChanged(asset, attribute, newValue, oldValue) {} 153 | 154 | onAssetRemoved(asset) {} 155 | 156 | onEnable() { 157 | Component.prototype.onEnable.call(this); 158 | 159 | const spine = this.data.spine; 160 | if (spine) { 161 | spine.addToLayers(); 162 | } 163 | } 164 | 165 | onDisable() { 166 | Component.prototype.onDisable.call(this); 167 | 168 | const spine = this.data.spine; 169 | if (spine) { 170 | spine.removeFromLayers(); 171 | } 172 | } 173 | 174 | hide() { 175 | if (this.data.spine) { 176 | this.data.spine.hide(); 177 | } 178 | } 179 | 180 | show() { 181 | if (this.data.spine) { 182 | this.data.spine.show(); 183 | } 184 | } 185 | 186 | removeComponent() { 187 | let asset; 188 | 189 | if (this.atlasAsset) { 190 | asset = this.system.app.assets.get(this.atlasAsset); 191 | if (asset) { 192 | asset.off('change', this.onAssetChanged); 193 | asset.off('remove', this.onAssetRemoved); 194 | } 195 | } 196 | 197 | if (this.skeletonAsset) { 198 | asset = this.system.app.assets.get(this.skeletonAsset); 199 | if (asset) { 200 | asset.off('change', this.onAssetChanged); 201 | asset.off('remove', this.onAssetRemoved); 202 | } 203 | } 204 | 205 | if (this.textureAssets && this.textureAssets.length) { 206 | for (let i = 0; i < this.textureAssets.length; i++) { 207 | asset = this.system.app.assets.get(this.textureAssets[i]); 208 | if (asset) { 209 | asset.off('change', this.onAssetChanged); 210 | asset.off('remove', this.onAssetRemoved); 211 | } 212 | } 213 | } 214 | } 215 | } 216 | 217 | export { SpineComponent }; 218 | -------------------------------------------------------------------------------- /src/component/SpineComponentData.js: -------------------------------------------------------------------------------- 1 | class SpineComponentData { 2 | constructor() { 3 | // serialized 4 | this.enabled = true; 5 | this.atlasAsset = null; 6 | this.textureAssets = []; 7 | this.skeletonAsset = null; 8 | this.speed = 1; 9 | 10 | // non-serialized 11 | this.spine = null; 12 | this.atlasData = null; 13 | this.textures = []; 14 | this.skeletonData = null; 15 | } 16 | } 17 | 18 | export { SpineComponentData }; 19 | -------------------------------------------------------------------------------- /src/component/SpineComponentSystem.js: -------------------------------------------------------------------------------- 1 | import { ComponentSystem } from 'playcanvas'; 2 | 3 | import { SpineComponent } from './SpineComponent.js'; 4 | import { SpineComponentData } from './SpineComponentData.js'; 5 | 6 | class SpineComponentSystem extends ComponentSystem { 7 | constructor(app) { 8 | super(app); 9 | 10 | this.id = 'spine'; 11 | 12 | this.ComponentType = SpineComponent; 13 | this.DataType = SpineComponentData; 14 | 15 | this.schema = [ 16 | 'enabled', 17 | 'atlasAsset', 18 | 'textureAssets', 19 | 'skeletonAsset', 20 | 'atlasData', 21 | 'textures', 22 | 'skeletonData', 23 | 'speed', 24 | 'spine' 25 | ]; 26 | 27 | this.on('beforeremove', this.onBeforeRemove, this); 28 | this.app.systems.on('update', this.onUpdate, this); 29 | } 30 | 31 | initializeComponentData(component, data, properties) { 32 | properties = [ 33 | 'enabled', 34 | 'atlasAsset', 35 | 'textureAssets', 36 | 'skeletonAsset', 37 | 'atlasData', 38 | 'textures', 39 | 'skeletonData', 40 | 'spine' 41 | ]; 42 | 43 | super.initializeComponentData( 44 | component, 45 | data, 46 | properties 47 | ); 48 | } 49 | 50 | /** 51 | * @param {pc.Entity} entity - The entity with spine script attached. 52 | * @param {pc.Component} component - The pc component 53 | */ 54 | onBeforeRemove(entity, component) { 55 | const data = entity.spine.data; 56 | if (data.spine) { 57 | data.spine.destroy(); 58 | } 59 | 60 | entity.spine.removeComponent(); 61 | } 62 | 63 | /** 64 | * @param {Number} dt - delta time 65 | */ 66 | onUpdate(dt) { 67 | const components = this.store; 68 | 69 | for (const id in components) { 70 | if (components.hasOwnProperty(id)) { 71 | const component = components[id]; 72 | const componentData = component.data; 73 | if (componentData.enabled && component.entity.enabled) { 74 | if (componentData.spine) { 75 | componentData.spine.setPosition( 76 | component.entity.getPosition() 77 | ); 78 | componentData.spine.update(componentData.speed * dt); 79 | } 80 | } 81 | } 82 | } 83 | } 84 | } 85 | 86 | export { SpineComponentSystem }; 87 | -------------------------------------------------------------------------------- /src/component/SpineTextureWrapper.js: -------------------------------------------------------------------------------- 1 | import { 2 | FILTER_NEAREST, 3 | FILTER_LINEAR, 4 | FILTER_NEAREST_MIPMAP_NEAREST, 5 | FILTER_LINEAR_MIPMAP_NEAREST, 6 | FILTER_NEAREST_MIPMAP_LINEAR, 7 | FILTER_LINEAR_MIPMAP_LINEAR, 8 | ADDRESS_MIRRORED_REPEAT, 9 | ADDRESS_CLAMP_TO_EDGE, 10 | ADDRESS_REPEAT 11 | } from 'playcanvas'; 12 | 13 | const TO_TEXTURE_FILTER = { 14 | 9728: FILTER_NEAREST, 15 | 9729: FILTER_LINEAR, 16 | 9984: FILTER_NEAREST_MIPMAP_NEAREST, 17 | 9985: FILTER_LINEAR_MIPMAP_NEAREST, 18 | 9986: FILTER_NEAREST_MIPMAP_LINEAR, 19 | 9987: FILTER_LINEAR_MIPMAP_LINEAR 20 | }; 21 | 22 | const TO_UV_WRAP_MODE = { 23 | 33648: ADDRESS_MIRRORED_REPEAT, 24 | 33071: ADDRESS_CLAMP_TO_EDGE, 25 | 10487: ADDRESS_REPEAT 26 | }; 27 | 28 | class SpineTextureWrapper { 29 | constructor(texture) { 30 | this._image = { 31 | width: texture.width, 32 | height: texture.height 33 | }; 34 | 35 | this.pcTexture = texture; 36 | } 37 | 38 | setFilters(minFilter, magFilter) { 39 | this.pcTexture.minFilter = TO_TEXTURE_FILTER[minFilter]; 40 | this.pcTexture.magFilter = TO_TEXTURE_FILTER[magFilter]; 41 | } 42 | 43 | setWraps(uWrap, vWrap) { 44 | this.pcTexture.addressU = TO_UV_WRAP_MODE[uWrap]; 45 | this.pcTexture.addressV = TO_UV_WRAP_MODE[vWrap]; 46 | } 47 | 48 | getImage() { 49 | return this._image; 50 | } 51 | 52 | dispose() { 53 | // spine 4.1 54 | this.pcTexture.destroy(); 55 | } 56 | } 57 | 58 | export { SpineTextureWrapper }; 59 | -------------------------------------------------------------------------------- /src/component/semver/index.js: -------------------------------------------------------------------------------- 1 | // custom semver export as described in semver readme 2 | 3 | // load just what is used to minimize footprint 4 | import constants from 'semver/internal/constants.js'; 5 | import valid from 'semver/functions/valid.js'; 6 | import coerce from 'semver/functions/coerce.js'; 7 | import satisfies from 'semver/functions/satisfies.js'; 8 | 9 | export default { 10 | valid, 11 | coerce, 12 | satisfies, 13 | SEMVER_SPEC_VERSION: constants.SEMVER_SPEC_VERSION 14 | }; 15 | -------------------------------------------------------------------------------- /src/wrapper40.js: -------------------------------------------------------------------------------- 1 | // spine 4.0 requires a wrapper to match import signature of other versions 2 | export * as spine from "spine40"; 3 | -------------------------------------------------------------------------------- /src/wrapper41.js: -------------------------------------------------------------------------------- 1 | // spine 4.1 requires a wrapper to match import signature of other versions 2 | export * as spine from "spine41"; 3 | -------------------------------------------------------------------------------- /src/wrapper42.js: -------------------------------------------------------------------------------- 1 | // spine 4.2 requires a wrapper to match import signature of other versions 2 | export * as spine from "spine42"; 3 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Language and Environment */ 4 | "target": "ES2022", 5 | "lib": ["esnext", "dom"], 6 | "jsx": "preserve", 7 | 8 | /* Modules */ 9 | "module": "ES2022", 10 | "rootDir": ".", 11 | "moduleResolution": "bundler", 12 | "baseUrl": ".", 13 | "resolveJsonModule": true, 14 | "paths": { 15 | "spine-core-import": ["src/wrapper41.js"] 16 | }, 17 | 18 | /* JavaScript Support */ 19 | "allowJs": true, 20 | "checkJs": true, 21 | "maxNodeModuleJsDepth": 3, 22 | 23 | /* Emit */ 24 | "outDir": "temp", 25 | "removeComments": false, 26 | "sourceMap": false, 27 | "newLine": "lf", 28 | 29 | /* Interop Constraints */ 30 | "isolatedModules": true, 31 | "esModuleInterop": true, 32 | "forceConsistentCasingInFileNames": true, 33 | 34 | /* Type Checking */ 35 | "noImplicitThis": true, 36 | 37 | /* Completeness */ 38 | "skipLibCheck": true 39 | }, 40 | "input": ["src", "rollup.config.mjs"], 41 | "exclude": ["node_modules", "**/node_modules/*", "build"] 42 | } 43 | --------------------------------------------------------------------------------