mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-05 06:44:56 +08:00
Merge branch '4.2' into spine-android
This commit is contained in:
commit
88ea68a9bc
18
.github/workflows/spine-haxe.yml
vendored
Normal file
18
.github/workflows/spine-haxe.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
name: Build spine-haxe
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'spine-haxe/**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build spine-haxe
|
||||
working-directory: spine-haxe
|
||||
env:
|
||||
HAXE_UPDATE_URL: ${{secrets.HAXE_UPDATE_URL}}
|
||||
run: ./build.sh
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -202,3 +202,5 @@ spine-haxe/export
|
||||
spine-godot/example-v4-csharp/.godot
|
||||
spine-flutter/src/spine-cpp-lite
|
||||
spine-sdl/.vs
|
||||
spine-ts/spine-canvaskit/dist
|
||||
spine-ts/output.png
|
||||
|
||||
@ -160,6 +160,7 @@
|
||||
- All Spine Outline shaders, including the URP outline shader, now provide an additional parameter `Width in Screen Space`. Enable it to keep the outline width constant in screen space instead of texture space. Requires more expensive computations, so enable only where necessary. Defaults to `disabled` to maintain existing behaviour.
|
||||
- Added support for BlendModeMaterials at runtime instantiation from files via an additional method `SkeletonDataAsset.SetupRuntimeBlendModeMaterials`. See example scene `Spine Examples/Other Examples/Instantiate from Script` for a usage example.
|
||||
- SkeletonGraphic: You can now offset the skeleton mesh relative to the pivot via a newly added green circle handle. This allows you to e.g. frame only the face of a skeleton inside a masked frame. Previously offsetting the pivot downwards fails when `Layout Scale Mode` scales the mesh smaller and towards the pivot (e.g. the feet) and thus out of the frame. Now you can keep the pivot in the center of the `RectTransform` while offsetting only the mesh downwards, keeping the desired skeleton area (e.g. the face) centered while resizing. Moving the new larger green circle handle moves the mesh offset, while moving the blue pivot circle handle moves the pivot as usual.
|
||||
- `Universal Render Pipeline/Spine/Skeleton` shader now performs proper alpha-testing when `Depth Write` is enabled, using the existing `Shadow alpha cutoff` parameter.
|
||||
|
||||
- **Breaking changes**
|
||||
|
||||
@ -173,7 +174,6 @@
|
||||
- `SkeletonGraphicRenderTexture` example component: `protected RawImage quadRawImage` was changed to `protected SkeletonSubmeshGraphic quadMaskableGraphic` for a bugfix. This is only relevant for subclasses of `SkeletonGraphicRenderTexture` or when querying the `RawImage` component via e.g. `skeletonGraphicRenderTexture.quad.GetComponent<RawImage>()`.
|
||||
- Fixed a bug where when Linear color space is used and `PMA vertex colors` enabled, additive slots add a too dark (too transparent) color value. If you want the old incorrect behaviour (darker additive slots) or are not using Linear but Gamma color space, you can comment-out the define `LINEAR_COLOR_SPACE_FIX_ADDITIVE_ALPHA` in `MeshGenerator.cs` to deactivate the fix or just to skip unnecessary instructions.
|
||||
|
||||
|
||||
- **Changes of default values**
|
||||
|
||||
- **Deprecated**
|
||||
@ -260,6 +260,10 @@
|
||||
|
||||
### Canvas backend
|
||||
|
||||
### CanvasKit backend
|
||||
|
||||
- Added spine-canvaskit runtime. See https://esotericsoftware.com/spine-canvaskit
|
||||
|
||||
### Three.js backend
|
||||
|
||||
- Added physics support
|
||||
@ -418,6 +422,7 @@
|
||||
|
||||
- Made `SkeletonGraphic.unscaledTime` parameter protected, use the new property `UnscaledTime` instead.
|
||||
- `SkeletonGraphic` `OnRebuild` callback delegate is now issued after the skeleton has been initialized, before the `AnimationState` component is initialized. This makes behaviour consistent with `SkeletonAnimation` and `SkeletonMecanim` component behaviour. Use the new callback `OnAnimationRebuild` if you want to receive a callback after the `SkeletonGraphic` `AnimationState` has been initialized.
|
||||
- Changed name of prefab skeleton meshes stored at prefabs from `Skeleton Prefab Mesh "name"` to `Skeleton Prefab Mesh [name]` to avoid issues with quotes in mesh asset names (see [this issue](https://github.com/EsotericSoftware/spine-runtimes/issues/2572)). Likely this change poses no problems at all, however if you are parsing the prefab's mesh name for whatever reason, be sure to adjust the pattern accordingly.
|
||||
|
||||
- **Changes of default values**
|
||||
|
||||
|
||||
@ -448,10 +448,24 @@ cp -f ../sack/export/sack-pma.png "$ROOT/spine-ts/spine-webgl/example/assets/"
|
||||
cp -f ../celestial-circus/export/* "$ROOT/spine-ts/spine-webgl/example/assets/"
|
||||
|
||||
rm "$ROOT/spine-ts/spine-canvas/example/assets/"*
|
||||
cp -f ../spineboy/export/spineboy-pro.skel "$ROOT/spine-ts/spine-canvas/example/assets/"
|
||||
cp -f ../spineboy/export/spineboy-ess.json "$ROOT/spine-ts/spine-canvas/example/assets/"
|
||||
cp -f ../spineboy/export/spineboy.atlas "$ROOT/spine-ts/spine-canvas/example/assets/"
|
||||
cp -f ../spineboy/export/spineboy.png "$ROOT/spine-ts/spine-canvas/example/assets/"
|
||||
|
||||
rm "$ROOT/spine-ts/spine-canvaskit/example/assets/"*
|
||||
cp -f ../spineboy/export/spineboy-pro.skel "$ROOT/spine-ts/spine-canvaskit/example/assets/"
|
||||
cp -f ../spineboy/export/spineboy.atlas "$ROOT/spine-ts/spine-canvaskit/example/assets/"
|
||||
cp -f ../spineboy/export/spineboy.png "$ROOT/spine-ts/spine-canvaskit/example/assets/"
|
||||
|
||||
cp -f ../mix-and-match/export/mix-and-match-pro.skel "$ROOT/spine-ts/spine-canvaskit/example/assets/"
|
||||
cp -f ../mix-and-match/export/mix-and-match.atlas "$ROOT/spine-ts/spine-canvaskit/example/assets/"
|
||||
cp -f ../mix-and-match/export/mix-and-match.png "$ROOT/spine-ts/spine-canvaskit/example/assets/"
|
||||
|
||||
cp -f ../celestial-circus/export/celestial-circus-pro.json "$ROOT/spine-ts/spine-canvaskit/example/assets/"
|
||||
cp -f ../celestial-circus/export/celestial-circus.atlas "$ROOT/spine-ts/spine-canvaskit/example/assets/"
|
||||
cp -f ../celestial-circus/export/celestial-circus.png "$ROOT/spine-ts/spine-canvaskit/example/assets/"
|
||||
|
||||
rm "$ROOT/spine-ts/spine-threejs/example/assets/"*
|
||||
cp -f ../raptor/export/raptor-pro.json "$ROOT/spine-ts/spine-threejs/example/assets/"
|
||||
cp -f ../raptor/export/raptor.atlas "$ROOT/spine-ts/spine-threejs/example/assets/"
|
||||
|
||||
26
spine-haxe/LICENSE
Normal file
26
spine-haxe/LICENSE
Normal file
@ -0,0 +1,26 @@
|
||||
Spine Runtimes License Agreement
|
||||
Last updated July 28, 2023. Replaces all prior versions.
|
||||
|
||||
Copyright (c) 2013-2023, Esoteric Software LLC
|
||||
|
||||
Integration of the Spine Runtimes into software or otherwise creating
|
||||
derivative works of the Spine Runtimes is permitted under the terms and
|
||||
conditions of Section 2 of the Spine Editor License Agreement:
|
||||
http://esotericsoftware.com/spine-editor-license
|
||||
|
||||
Otherwise, it is permitted to integrate the Spine Runtimes into software or
|
||||
otherwise create derivative works of the Spine Runtimes (collectively,
|
||||
"Products"), provided that each user of the Products must obtain their own
|
||||
Spine Editor license and redistribution of the Products in any form must
|
||||
include this license and copyright notice.
|
||||
|
||||
THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
||||
BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
|
||||
SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@ -4,6 +4,10 @@
|
||||
|
||||
The spine-haxe runtime provides functionality to load, manipulate and render [Spine](http://esotericsoftware.com) skeletal animation data using [Haxe](https://haxe.org/) in combination with [OpenFL](https://www.openfl.org/) and [Lime](https://lime.openfl.org/).
|
||||
|
||||
For documentation of the core API in `spine-core`, please refer to our [Spine Runtimes Guide](http://esotericsoftware.com/spine-runtimes-guide).
|
||||
|
||||
For documentation of `spine-haxe`, please refer to our [spine-haxe Guide](https://esotericsoftware.com/spine-haxe).
|
||||
|
||||
## Licensing
|
||||
|
||||
You are welcome to evaluate the Spine Runtimes and the examples we provide in this repository free of charge.
|
||||
@ -21,12 +25,31 @@ spine-haxe works with data exported from Spine 4.2.xx.
|
||||
spine-haxe supports all Spine features except premultiplied alpha atlases and two color tinting.
|
||||
|
||||
## Setup
|
||||
The core module of spine-haxe has zero dependencies. The rendering implementation through Starling has two dependencies: openfl and starling.
|
||||
To use spine-haxe you have first to install all the necessary dependencies:
|
||||
|
||||
TBD
|
||||
```
|
||||
haxelib install openfl
|
||||
haxelib install starling
|
||||
```
|
||||
|
||||
Once you have installed the dependencies, you can [download the latest version of spine-haxe](https://esotericsoftware.com/files/spine-haxe/4.2/spine-haxe-latest.zip) and install it:
|
||||
|
||||
```
|
||||
haxelib install spine-haxe-x.y.z.zip
|
||||
```
|
||||
|
||||
Notice that the spine-haxe library is not available on [lib.haxe.org](https://lib.haxe.org/). This is why you need to download the library and install it through the zip archive.
|
||||
|
||||
## Example
|
||||
|
||||
TBD
|
||||
The `example/` folder contains the spine-haxe examples. They demonstrates the usage of the API. Enter the the `spine-haxe` folder and run the following command:
|
||||
|
||||
```
|
||||
lime test html5
|
||||
```
|
||||
|
||||
This will compile the modules and start a server that serves the example pages at http://127.0.0.1:3000.
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
32
spine-haxe/build.sh
Executable file
32
spine-haxe/build.sh
Executable file
@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
if [ -z "$GITHUB_REF" ]; then
|
||||
BRANCH=$(git symbolic-ref --short -q HEAD)
|
||||
else
|
||||
BRANCH=${GITHUB_REF#refs/heads/}
|
||||
fi
|
||||
|
||||
# Get the latest commit message
|
||||
COMMIT_MSG=$(git log -1 --pretty=%B)
|
||||
|
||||
# Public only if the commit message is in the correct format
|
||||
if echo "$COMMIT_MSG" | grep -qE '^\[haxe\] Release [0-9]+\.[0-9]+\.[0-9]+$'; then
|
||||
VERSION=$(echo "$COMMIT_MSG" | sed -E 's/^\[haxe\] Release ([0-9]+\.[0-9]+\.[0-9]+)$/\1/')
|
||||
echo "Building spine-haxe $BRANCH artifacts (version $VERSION)"
|
||||
|
||||
if [ ! -z "$HAXE_UPDATE_URL" ] && [ ! -z "$BRANCH" ]; then
|
||||
echo "Deploying spine-haxe $BRANCH artifacts (version $VERSION)"
|
||||
zip -r "spine-haxe-$VERSION.zip" \
|
||||
haxelib.json \
|
||||
LICENSE \
|
||||
README.md \
|
||||
spine-haxe
|
||||
curl -f -F "file=@spine-haxe-$VERSION.zip" "$HAXE_UPDATE_URL$BRANCH"
|
||||
else
|
||||
echo "Not deploying artifacts. HAXE_UPDATE_URL and/or BRANCH not set."
|
||||
fi
|
||||
else
|
||||
echo "The commit is not a release - do not publish."
|
||||
echo "To release the commit has to be in the for: \"[haxe] Release x.y.z\""
|
||||
fi
|
||||
@ -17,8 +17,8 @@
|
||||
"cpp"
|
||||
],
|
||||
"description": "The official Spine Runtime for Haxe",
|
||||
"version": "4.1.0",
|
||||
"releasenote": "Initial release",
|
||||
"version": "4.2.0",
|
||||
"releasenote": "Update to 4.2.0",
|
||||
"contributors": [
|
||||
"esotericsoftware"
|
||||
],
|
||||
|
||||
29
spine-haxe/publish.sh
Executable file
29
spine-haxe/publish.sh
Executable file
@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
currentVersion=$(grep -o '"version": "[^"]*' haxelib.json | grep -o '[^"]*$')
|
||||
|
||||
major=$(echo "$currentVersion" | cut -d. -f1)
|
||||
minor=$(echo "$currentVersion" | cut -d. -f2)
|
||||
patch=$(echo "$currentVersion" | cut -d. -f3)
|
||||
newPatch=$((patch + 1))
|
||||
newVersion="$major.$minor.$newPatch"
|
||||
|
||||
echo "current version: $currentVersion"
|
||||
echo "new version: $newVersion"
|
||||
|
||||
sed -i '' "s/$currentVersion/$newVersion/" haxelib.json
|
||||
|
||||
echo "Write Y if you want to commit and push the new version $newVersion."
|
||||
echo "This will trigger a pipeline that will publish the new version on esoteric software server."
|
||||
echo "Do you want to proceed [y/n]?"
|
||||
|
||||
read answer
|
||||
if [ "$answer" = "Y" ] || [ "$answer" = "y" ]; then
|
||||
git add haxelib.json
|
||||
git commit -m "[haxe] Release $newVersion"
|
||||
git push origin haxe-ci
|
||||
echo "Changes committed and pushed."
|
||||
else
|
||||
echo "Commit and push cancelled, but haxelib.json version updated."
|
||||
fi
|
||||
@ -6,10 +6,11 @@ up into multiple modules:
|
||||
1. `spine-core/`, the core classes to load and process Spine skeletons.
|
||||
1. `spine-webgl/`, a self-contained WebGL backend, built on the core classes.
|
||||
1. `spine-canvas/`, a self-contained Canvas backend, built on the core classes.
|
||||
1. `spine-threejs/`, a self-contained THREE.JS backend, built on the core classes.
|
||||
1. `spine-canvaskit/`, a self-contained [CanvasKit](https://skia.org/docs/user/modules/canvaskit/) backend, built on the core classes for CanvasKit, supporting both NodeJS for headless rendering, and browsers.
|
||||
1. `spine-threejs/`, a self-contained [THREE.JS](https://threejs.org/) backend, built on the core classes.
|
||||
1. `spine-player/`, a self-contained player to easily display Spine animations on your website, built on the core classes and WebGL backend.
|
||||
1. `spine-phaser/`, a Phaser backend, built on the core classes.
|
||||
1. `spine-pixi/`, a Pixi backend, built on the core classes.
|
||||
1. `spine-phaser/`, a [Phaser](https://phaser.io/) backend, built on the core classes.
|
||||
1. `spine-pixi/`, a [PixiJS](https://pixijs.com/) backend, built on the core classes.
|
||||
|
||||
In most cases, the `spine-player` module is best suited for your needs. Please refer to the [Spine Web Player documentation](https://esotericsoftware.com/spine-player) for more information.
|
||||
|
||||
@ -35,9 +36,11 @@ For the official legal terms governing the Spine Runtimes, please read the [Spin
|
||||
|
||||
spine-ts works with data exported from Spine 4.2.xx.
|
||||
|
||||
The spine-ts WebGL and Player backends support all Spine features.
|
||||
spine-ts Canvas does not support mesh attachments, clipping attachments, or two-color tinting. Only the alpha channel from tint colors is applied. Experimental support for mesh attachments can be enabled by setting `spine.SkeletonRenderer.useTriangleRendering` to true. Note that this experimental mesh rendering is slow and render with artifacts on some browsers.
|
||||
|
||||
spine-ts Canvas does not support mesh attachments, clipping attachments, or color tinting. Only the alpha channel from tint colors is applied. Experimental support for mesh attachments can be enabled by setting `spine.SkeletonRenderer.useTriangleRendering` to true. Note that this experimental mesh rendering is slow and render with artifacts on some browsers.
|
||||
spine-canvaskit supports all Spine features except two-color tinting.
|
||||
|
||||
The spine-webgl and spine-player support all Spine features.
|
||||
|
||||
spine-ts THREE.JS does not support two color tinting. The THREE.JS backend provides `SkeletonMesh.zOffset` to avoid z-fighting. Adjust to your near/far plane settings.
|
||||
|
||||
@ -50,20 +53,23 @@ All spine-ts modules are published to [npm](http://npmjs.com) for consumption vi
|
||||
You can include a module in your project via a `<script>` tag from the [unpkg](https://unpkg.com/) CDN, specifying the version as part of the URL. In the examples below, the version is `4.0.*`, which fetches the latest patch release, and which will work with all exports from Spine Editor version `4.0.x`.
|
||||
|
||||
```
|
||||
// spine-ts Core
|
||||
// spine-core
|
||||
<script src="https://unpkg.com/@esotericsoftware/spine-core@4.2.*/dist/iife/spine-core.js"></script>
|
||||
|
||||
// spine-ts Canvas
|
||||
// spine-canvas
|
||||
<script src="https://unpkg.com/@esotericsoftware/spine-canvas@4.2.*/dist/iife/spine-canvas.js"></script>
|
||||
|
||||
// spine-ts WebGL
|
||||
// spine-canvaskit
|
||||
<script src="https://unpkg.com/@esotericsoftware/spine-canvas@4.2.*/dist/iife/spine-canvaskit.js"></script>
|
||||
|
||||
// spine-webgl
|
||||
<script src="https://unpkg.com/@esotericsoftware/spine-webgl@4.2.*/dist/iife/spine-webgl.js"></script>
|
||||
|
||||
// spine-ts Player, which requires a spine-player.css as well
|
||||
// spine-player, which requires a spine-player.css as well
|
||||
<script src="https://unpkg.com/@esotericsoftware/spine-player@4.2.*/dist/iife/spine-player.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/@esotericsoftware/spine-player@4.0.*/dist/spine-player.css">
|
||||
|
||||
// spine-ts ThreeJS
|
||||
// spine-threejs
|
||||
<script src="https://unpkg.com/@esotericsoftware/spine-threejs@4.2.*/dist/iife/spine-threejs.js"></script>
|
||||
|
||||
// spine-phaser
|
||||
@ -84,6 +90,7 @@ If your project dependencies are managed through NPM or Yarn, you can add spine-
|
||||
```
|
||||
npm install @esotericsoftware/spine-core
|
||||
npm install @esotericsoftware/spine-canvas
|
||||
npm install @esotericsoftware/spine-canvaskit
|
||||
npm install @esotericsoftware/spine-webgl
|
||||
npm install @esotericsoftware/spine-player
|
||||
npm install @esotericsoftware/spine-threejs
|
||||
|
||||
@ -226,3 +226,7 @@ ol {
|
||||
.p-4 {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
@ -20,6 +20,15 @@
|
||||
<a href="/spine-canvas/example/mouse-click.html">Mouse click</a>
|
||||
</li>
|
||||
</ul>
|
||||
<li>CanvasKit</li>
|
||||
<ul>
|
||||
<li><a href="/spine-canvaskit/example">Example</a></li>
|
||||
<li><a href="/spine-canvaskit/example/animation-state-events.html">Animation State Events</a></li>
|
||||
<li><a href="/spine-canvaskit/example/mix-and-match.html">Skins Mix & Match</a></li>
|
||||
<li><a href="/spine-canvaskit/example/ik-following.html">IK Following</a></li>
|
||||
<li><a href="/spine-canvaskit/example/physics.html">Physics</a></li>
|
||||
<li><a href="/spine-canvaskit/example/micro-benchmark.html">Micro Benchmark</a></li>
|
||||
</ul>
|
||||
<li>Pixi</li>
|
||||
<ul>
|
||||
<li><a href="/spine-pixi/example/index.html">Basic example</a></li>
|
||||
|
||||
89
spine-ts/package-lock.json
generated
89
spine-ts/package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@esotericsoftware/spine-ts",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@esotericsoftware/spine-ts",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"license": "LicenseRef-LICENSE",
|
||||
"workspaces": [
|
||||
"spine-core",
|
||||
@ -15,6 +15,7 @@
|
||||
"spine-player",
|
||||
"spine-threejs",
|
||||
"spine-pixi",
|
||||
"spine-canvaskit",
|
||||
"spine-webgl"
|
||||
],
|
||||
"devDependencies": {
|
||||
@ -45,6 +46,10 @@
|
||||
"resolved": "spine-canvas",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@esotericsoftware/spine-canvaskit": {
|
||||
"resolved": "spine-canvaskit",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@esotericsoftware/spine-core": {
|
||||
"resolved": "spine-core",
|
||||
"link": true
|
||||
@ -69,6 +74,14 @@
|
||||
"resolved": "spine-webgl",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@pdf-lib/upng": {
|
||||
"version": "1.0.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pako": "^1.0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@pixi/assets": {
|
||||
"version": "7.4.2",
|
||||
"license": "MIT",
|
||||
@ -226,6 +239,14 @@
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.14.9",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/offscreencanvas": {
|
||||
"version": "2019.7.3",
|
||||
"dev": true,
|
||||
@ -244,6 +265,10 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webgpu/types": {
|
||||
"version": "0.1.21",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"dev": true,
|
||||
@ -516,6 +541,13 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/canvaskit-wasm": {
|
||||
"version": "0.39.1",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@webgpu/types": "0.1.21"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"dev": true,
|
||||
@ -1954,6 +1986,11 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"dev": true,
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"dev": true,
|
||||
@ -2760,6 +2797,11 @@
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/union-value": {
|
||||
"version": "1.0.1",
|
||||
"dev": true,
|
||||
@ -2989,33 +3031,46 @@
|
||||
},
|
||||
"spine-canvas": {
|
||||
"name": "@esotericsoftware/spine-canvas",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"license": "LicenseRef-LICENSE",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-core": "4.2.48"
|
||||
"@esotericsoftware/spine-core": "4.2.56"
|
||||
}
|
||||
},
|
||||
"spine-canvaskit": {
|
||||
"name": "@esotericsoftware/spine-canvaskit",
|
||||
"version": "4.2.56",
|
||||
"license": "LicenseRef-LICENSE",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-core": "4.2.56",
|
||||
"canvaskit-wasm": "0.39.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pdf-lib/upng": "1.0.1",
|
||||
"@types/node": "20.14.9"
|
||||
}
|
||||
},
|
||||
"spine-core": {
|
||||
"name": "@esotericsoftware/spine-core",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"license": "LicenseRef-LICENSE"
|
||||
},
|
||||
"spine-phaser": {
|
||||
"name": "@esotericsoftware/spine-phaser",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"license": "LicenseRef-LICENSE",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-canvas": "4.2.48",
|
||||
"@esotericsoftware/spine-core": "4.2.48",
|
||||
"@esotericsoftware/spine-webgl": "4.2.48"
|
||||
"@esotericsoftware/spine-canvas": "4.2.56",
|
||||
"@esotericsoftware/spine-core": "4.2.56",
|
||||
"@esotericsoftware/spine-webgl": "4.2.56"
|
||||
}
|
||||
},
|
||||
"spine-pixi": {
|
||||
"name": "@esotericsoftware/spine-pixi",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"license": "LicenseRef-LICENSE",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-core": "4.2.48"
|
||||
"@esotericsoftware/spine-core": "4.2.56"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@pixi/assets": "^7.2.4",
|
||||
@ -3028,26 +3083,26 @@
|
||||
},
|
||||
"spine-player": {
|
||||
"name": "@esotericsoftware/spine-player",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"license": "LicenseRef-LICENSE",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-webgl": "4.2.48"
|
||||
"@esotericsoftware/spine-webgl": "4.2.56"
|
||||
}
|
||||
},
|
||||
"spine-threejs": {
|
||||
"name": "@esotericsoftware/spine-threejs",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"license": "LicenseRef-LICENSE",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-core": "4.2.48"
|
||||
"@esotericsoftware/spine-core": "4.2.56"
|
||||
}
|
||||
},
|
||||
"spine-webgl": {
|
||||
"name": "@esotericsoftware/spine-webgl",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"license": "LicenseRef-LICENSE",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-core": "4.2.48"
|
||||
"@esotericsoftware/spine-core": "4.2.56"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@esotericsoftware/spine-ts",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"description": "The official Spine Runtimes for the web.",
|
||||
"type": "module",
|
||||
"files": [
|
||||
@ -8,21 +8,23 @@
|
||||
],
|
||||
"scripts": {
|
||||
"prepublish": "npm run clean && npm run build",
|
||||
"clean": "npx rimraf spine-core/dist spine-canvas/dist spine-webgl/dist spine-phaser/dist spine-player/dist spine-threejs/dist spine-pixi/dist",
|
||||
"build": "npm run clean && npm run build:modules && concurrently \"npm run build:core\" \"npm run build:canvas\" \"npm run build:webgl\" \"npm run build:phaser\" \"npm run build:player\" \"npm run build:threejs\" \"npm run build:pixi\"",
|
||||
"clean": "npx rimraf spine-core/dist spine-canvas/dist spine-canvaskit/dist spine-webgl/dist spine-phaser/dist spine-player/dist spine-threejs/dist spine-pixi/dist",
|
||||
"build": "npm run clean && npm run build:modules && concurrently \"npm run build:core\" \"npm run build:canvas\" \"npm run build:canvaskit\" \"npm run build:webgl\" \"npm run build:phaser\" \"npm run build:player\" \"npm run build:threejs\" \"npm run build:pixi\"",
|
||||
"postbuild": "npm run minify",
|
||||
"build:modules": "npx tsc -b -clean && npx tsc -b",
|
||||
"build:core": "npx esbuild --bundle spine-core/src/index.ts --tsconfig=spine-core/tsconfig.json --sourcemap --outfile=spine-core/dist/iife/spine-core.js --format=iife --global-name=spine",
|
||||
"build:canvas": "npx esbuild --bundle spine-canvas/src/index.ts --tsconfig=spine-canvas/tsconfig.json --sourcemap --outfile=spine-canvas/dist/iife/spine-canvas.js --format=iife --global-name=spine",
|
||||
"build:canvaskit": "npx esbuild --bundle spine-canvaskit/src/index.ts --tsconfig=spine-canvaskit/tsconfig.json --sourcemap --outfile=spine-canvaskit/dist/iife/spine-canvaskit.js --external:canvaskit-wasm --format=iife --global-name=spine",
|
||||
"build:webgl": "npx esbuild --bundle spine-webgl/src/index.ts --tsconfig=spine-webgl/tsconfig.json --sourcemap --outfile=spine-webgl/dist/iife/spine-webgl.js --format=iife --global-name=spine",
|
||||
"build:player": "npx copyfiles -f spine-player/css/spine-player.css spine-player/dist/ && npx esbuild spine-player/dist/spine-player.css --minify --outfile=spine-player/dist/spine-player.min.css && npx esbuild --bundle spine-player/src/index.ts --tsconfig=spine-player/tsconfig.json --sourcemap --outfile=spine-player/dist/iife/spine-player.js --format=iife --global-name=spine",
|
||||
"build:phaser": "npx esbuild --bundle spine-phaser/src/index.ts --tsconfig=spine-phaser/tsconfig.json --sourcemap --outfile=spine-phaser/dist/iife/spine-phaser.js --external:Phaser --alias:phaser=Phaser --format=iife --global-name=spine",
|
||||
"build:threejs": "npx esbuild --bundle spine-threejs/src/index.ts --tsconfig=spine-threejs/tsconfig.json --sourcemap --outfile=spine-threejs/dist/iife/spine-threejs.js --external:three --format=iife --global-name=spine",
|
||||
"build:pixi": "npx esbuild --bundle spine-pixi/src/index.ts --tsconfig=spine-pixi/tsconfig.json --sourcemap --outfile=spine-pixi/dist/iife/spine-pixi.js --external:@pixi/* --format=iife --global-name=spine",
|
||||
"minify": "npx esbuild --minify spine-core/dist/iife/spine-core.js --outfile=spine-core/dist/iife/spine-core.min.js && npx esbuild --minify spine-canvas/dist/iife/spine-canvas.js --outfile=spine-canvas/dist/iife/spine-canvas.min.js && npx esbuild --minify spine-player/dist/iife/spine-player.js --outfile=spine-player/dist/iife/spine-player.min.js && npx esbuild --minify spine-phaser/dist/iife/spine-phaser.js --outfile=spine-phaser/dist/iife/spine-phaser.min.js && npx esbuild --minify spine-webgl/dist/iife/spine-webgl.js --outfile=spine-webgl/dist/iife/spine-webgl.min.js && npx esbuild --minify spine-threejs/dist/iife/spine-threejs.js --outfile=spine-threejs/dist/iife/spine-threejs.min.js && npx esbuild --minify spine-pixi/dist/iife/spine-pixi.js --outfile=spine-pixi/dist/iife/spine-pixi.min.js",
|
||||
"dev": "concurrently \"npx live-server\" \"npm run dev:canvas\" \"npm run dev:webgl\" \"npm run dev:phaser\" \"npm run dev:player\" \"npm run dev:threejs\" \"npm run dev:pixi\"",
|
||||
"minify": "npx esbuild --minify spine-core/dist/iife/spine-core.js --outfile=spine-core/dist/iife/spine-core.min.js && npx esbuild --minify spine-canvas/dist/iife/spine-canvas.js --outfile=spine-canvas/dist/iife/spine-canvas.min.js && npx esbuild --minify spine-canvaskit/dist/iife/spine-canvaskit.js --outfile=spine-canvaskit/dist/iife/spine-canvaskit.min.js && npx esbuild --minify spine-player/dist/iife/spine-player.js --outfile=spine-player/dist/iife/spine-player.min.js && npx esbuild --minify spine-phaser/dist/iife/spine-phaser.js --outfile=spine-phaser/dist/iife/spine-phaser.min.js && npx esbuild --minify spine-webgl/dist/iife/spine-webgl.js --outfile=spine-webgl/dist/iife/spine-webgl.min.js && npx esbuild --minify spine-threejs/dist/iife/spine-threejs.js --outfile=spine-threejs/dist/iife/spine-threejs.min.js && npx esbuild --minify spine-pixi/dist/iife/spine-pixi.js --outfile=spine-pixi/dist/iife/spine-pixi.min.js",
|
||||
"dev": "concurrently \"npx live-server\" \"npm run dev:canvas\" \"npm run dev:canvaskit\" \"npm run dev:webgl\" \"npm run dev:phaser\" \"npm run dev:player\" \"npm run dev:threejs\" \"npm run dev:pixi\" \"npm run dev:modules\"",
|
||||
"dev:modules": "npm run build:modules -- --watch",
|
||||
"dev:canvas": "npm run build:canvas -- --watch",
|
||||
"dev:canvaskit": "npm run build:canvaskit -- --watch",
|
||||
"dev:webgl": "npm run build:webgl -- --watch",
|
||||
"dev:phaser": "npm run build:phaser -- --watch",
|
||||
"dev:player": "npm run build:player -- --watch",
|
||||
@ -55,18 +57,19 @@
|
||||
"spine-player",
|
||||
"spine-threejs",
|
||||
"spine-pixi",
|
||||
"spine-canvaskit",
|
||||
"spine-webgl"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/offscreencanvas": "^2019.6.4",
|
||||
"@types/three": "^0.146.0",
|
||||
"concurrently": "^7.6.0",
|
||||
"copyfiles": "^2.4.1",
|
||||
"esbuild": "^0.16.4",
|
||||
"live-server": "^1.2.2",
|
||||
"phaser": "^3.60.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"typescript": "^4.9.4",
|
||||
"@types/three": "^0.146.0",
|
||||
"three": "^0.146.0",
|
||||
"phaser": "^3.60.0"
|
||||
"typescript": "^4.9.4"
|
||||
}
|
||||
}
|
||||
@ -1,24 +1,25 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
if [ ! "$#" -eq 2 ]; then
|
||||
echo "Usage: ./publish.sh <last-version> <new-version>"
|
||||
exit
|
||||
else
|
||||
lastVersion=${1%/}
|
||||
newVersion=${2%/}
|
||||
echo "last version: $lastVersion"
|
||||
echo "new version: $newVersion"
|
||||
fi
|
||||
currentVersion=$(grep -o '"version": "[^"]*' package.json | grep -o '[^"]*$')
|
||||
major=$(echo "$currentVersion" | cut -d. -f1)
|
||||
minor=$(echo "$currentVersion" | cut -d. -f2)
|
||||
patch=$(echo "$currentVersion" | cut -d. -f3)
|
||||
newPatch=$((patch + 1))
|
||||
newVersion="$major.$minor.$newPatch"
|
||||
|
||||
sed -i '' "s/$lastVersion/$newVersion/" package.json
|
||||
sed -i '' "s/$lastVersion/$newVersion/" spine-canvas/package.json
|
||||
sed -i '' "s/$lastVersion/$newVersion/" spine-core/package.json
|
||||
sed -i '' "s/$lastVersion/$newVersion/" spine-phaser/package.json
|
||||
sed -i '' "s/$lastVersion/$newVersion/" spine-pixi/package.json
|
||||
sed -i '' "s/$lastVersion/$newVersion/" spine-player/package.json
|
||||
sed -i '' "s/$lastVersion/$newVersion/" spine-threejs/package.json
|
||||
sed -i '' "s/$lastVersion/$newVersion/" spine-webgl/package.json
|
||||
echo "current version: $currentVersion"
|
||||
echo "new version: $newVersion"
|
||||
|
||||
sed -i '' "s/$currentVersion/$newVersion/" package.json
|
||||
sed -i '' "s/$currentVersion/$newVersion/" spine-canvas/package.json
|
||||
sed -i '' "s/$currentVersion/$newVersion/" spine-canvaskit/package.json
|
||||
sed -i '' "s/$currentVersion/$newVersion/" spine-core/package.json
|
||||
sed -i '' "s/$currentVersion/$newVersion/" spine-phaser/package.json
|
||||
sed -i '' "s/$currentVersion/$newVersion/" spine-pixi/package.json
|
||||
sed -i '' "s/$currentVersion/$newVersion/" spine-player/package.json
|
||||
sed -i '' "s/$currentVersion/$newVersion/" spine-threejs/package.json
|
||||
sed -i '' "s/$currentVersion/$newVersion/" spine-webgl/package.json
|
||||
|
||||
rm package-lock.json
|
||||
rm -rf node_modules/@esotericsoftware
|
||||
|
||||
BIN
spine-ts/spine-canvas/example/assets/spineboy-pro.skel
Normal file
BIN
spine-ts/spine-canvas/example/assets/spineboy-pro.skel
Normal file
Binary file not shown.
@ -21,19 +21,19 @@
|
||||
canvas = document.getElementById("canvas");
|
||||
context = canvas.getContext("2d");
|
||||
skeletonRenderer = new spine.SkeletonRenderer(context);
|
||||
// skeletonRenderer.triangleRendering = true;
|
||||
skeletonRenderer.triangleRendering = true;
|
||||
|
||||
// Load the assets.
|
||||
assetManager = new spine.AssetManager("https://esotericsoftware.com/files/examples/4.0/spineboy/export/");
|
||||
assetManager.loadText("spineboy-ess.json");
|
||||
assetManager = new spine.AssetManager("assets/");
|
||||
assetManager.loadBinary("spineboy-pro.skel");
|
||||
assetManager.loadTextureAtlas("spineboy.atlas");
|
||||
await assetManager.loadAll();
|
||||
|
||||
// Create the texture atlas and skeleton data.
|
||||
let atlas = assetManager.require("spineboy.atlas");
|
||||
let atlasLoader = new spine.AtlasAttachmentLoader(atlas);
|
||||
let skeletonJson = new spine.SkeletonJson(atlasLoader);
|
||||
let skeletonData = skeletonJson.readSkeletonData(assetManager.require("spineboy-ess.json"));
|
||||
let skeletonBinary = new spine.SkeletonBinary(atlasLoader);
|
||||
let skeletonData = skeletonBinary.readSkeletonData(assetManager.require("spineboy-pro.skel"));
|
||||
|
||||
// Instantiate a new skeleton based on the atlas and skeleton data.
|
||||
skeleton = new spine.Skeleton(skeletonData);
|
||||
|
||||
@ -24,8 +24,8 @@
|
||||
skeletonRenderer.triangleRendering = true;
|
||||
|
||||
// Load the assets.
|
||||
assetManager = new spine.AssetManager("https://esotericsoftware.com/files/examples/4.0/spineboy/export/");
|
||||
assetManager.loadText("spineboy-pro.json");
|
||||
assetManager = new spine.AssetManager("assets/");
|
||||
assetManager.loadText("spineboy-ess.json");
|
||||
assetManager.loadTextureAtlas("spineboy.atlas");
|
||||
await assetManager.loadAll();
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
let atlas = assetManager.require("spineboy.atlas");
|
||||
let atlasLoader = new spine.AtlasAttachmentLoader(atlas);
|
||||
let skeletonJson = new spine.SkeletonJson(atlasLoader);
|
||||
let skeletonData = skeletonJson.readSkeletonData(assetManager.require("spineboy-pro.json"));
|
||||
let skeletonData = skeletonJson.readSkeletonData(assetManager.require("spineboy-ess.json"));
|
||||
|
||||
// Instantiate a new skeleton based on the atlas and skeleton data.
|
||||
skeleton = new spine.Skeleton(skeletonData);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@esotericsoftware/spine-canvas",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"description": "The official Spine Runtimes for the web.",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
@ -31,6 +31,6 @@
|
||||
},
|
||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-core": "4.2.48"
|
||||
"@esotericsoftware/spine-core": "4.2.56"
|
||||
}
|
||||
}
|
||||
@ -178,12 +178,14 @@ export class SkeletonRenderer {
|
||||
x2: number, y2: number, u2: number, v2: number) {
|
||||
let ctx = this.ctx;
|
||||
|
||||
u0 *= img.width;
|
||||
v0 *= img.height;
|
||||
u1 *= img.width;
|
||||
v1 *= img.height;
|
||||
u2 *= img.width;
|
||||
v2 *= img.height;
|
||||
const width = img.width - 1;
|
||||
const height = img.height - 1;
|
||||
u0 *= width;
|
||||
v0 *= height;
|
||||
u1 *= width;
|
||||
v1 *= height;
|
||||
u2 *= width;
|
||||
v2 *= height;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x0, y0);
|
||||
@ -201,17 +203,19 @@ export class SkeletonRenderer {
|
||||
u2 -= u0;
|
||||
v2 -= v0;
|
||||
|
||||
var det = 1 / (u1 * v2 - u2 * v1),
|
||||
let det = u1 * v2 - u2 * v1;
|
||||
if (det == 0) return;
|
||||
det = 1 / det;
|
||||
|
||||
// linear transformation
|
||||
a = (v2 * x1 - v1 * x2) * det,
|
||||
b = (v2 * y1 - v1 * y2) * det,
|
||||
c = (u1 * x2 - u2 * x1) * det,
|
||||
d = (u1 * y2 - u2 * y1) * det,
|
||||
// linear transformation
|
||||
const a = (v2 * x1 - v1 * x2) * det;
|
||||
const b = (v2 * y1 - v1 * y2) * det;
|
||||
const c = (u1 * x2 - u2 * x1) * det;
|
||||
const d = (u1 * y2 - u2 * y1) * det;
|
||||
|
||||
// translation
|
||||
e = x0 - a * u0 - c * v0,
|
||||
f = y0 - b * u0 - d * v0;
|
||||
// translation
|
||||
const e = x0 - a * u0 - c * v0;
|
||||
const f = y0 - b * u0 - d * v0;
|
||||
|
||||
ctx.save();
|
||||
ctx.transform(a, b, c, d, e, f);
|
||||
|
||||
26
spine-ts/spine-canvaskit/LICENSE
Normal file
26
spine-ts/spine-canvaskit/LICENSE
Normal file
@ -0,0 +1,26 @@
|
||||
Spine Runtimes License Agreement
|
||||
Last updated May 1, 2019. Replaces all prior versions.
|
||||
|
||||
Copyright (c) 2013-2019, Esoteric Software LLC
|
||||
|
||||
Integration of the Spine Runtimes into software or otherwise creating
|
||||
derivative works of the Spine Runtimes is permitted under the terms and
|
||||
conditions of Section 2 of the Spine Editor License Agreement:
|
||||
http://esotericsoftware.com/spine-editor-license
|
||||
|
||||
Otherwise, it is permitted to integrate the Spine Runtimes into software
|
||||
or otherwise create derivative works of the Spine Runtimes (collectively,
|
||||
"Products"), provided that each user of the Products must obtain their own
|
||||
Spine Editor license and redistribution of the Products in any form must
|
||||
include this license and copyright notice.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS
|
||||
INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
3
spine-ts/spine-canvaskit/README.md
Normal file
3
spine-ts/spine-canvaskit/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# spine-ts CanvasKit
|
||||
|
||||
Please see the top-level [README.md](../README.md) for more information.
|
||||
79
spine-ts/spine-canvaskit/example/animation-state-events.html
Normal file
79
spine-ts/spine-canvaskit/example/animation-state-events.html
Normal file
@ -0,0 +1,79 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
<script src="https://unpkg.com/canvaskit-wasm@latest/bin/canvaskit.js"></script>
|
||||
<script src="../dist/iife/spine-canvaskit.js"></script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="p-4 flex flex-col items-center">
|
||||
<h1>Animation State Events</h1>
|
||||
<p class="mb-4">Open the console in the developer tools to view events logs.</p>
|
||||
<canvas id=foo style="margin: 0 auto; width: 600px; height: 400px;"></canvas>
|
||||
</body>
|
||||
|
||||
<script type="module">
|
||||
async function readFile(path) {
|
||||
const response = await fetch(path);
|
||||
if (!response.ok) throw new Error("Could not load file " + path);
|
||||
return await response.arrayBuffer();
|
||||
}
|
||||
|
||||
const canvasElement = document.querySelector("#foo");
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
canvasElement.width = canvasElement.clientWidth * dpr;
|
||||
canvasElement.height = canvasElement.clientHeight * dpr;
|
||||
|
||||
const ck = await CanvasKitInit();
|
||||
const surface = ck.MakeCanvasSurface('foo');
|
||||
surface.getCanvas().scale(dpr, dpr);
|
||||
|
||||
const atlas = await spine.loadTextureAtlas(ck, "assets/spineboy.atlas", readFile);
|
||||
const skeletonData = await spine.loadSkeletonData("assets/spineboy-pro.skel", atlas, readFile);
|
||||
const drawable = new spine.SkeletonDrawable(skeletonData);
|
||||
drawable.skeleton.scaleX = drawable.skeleton.scaleY = 0.4;
|
||||
drawable.skeleton.x = 300;
|
||||
drawable.skeleton.y = 380;
|
||||
|
||||
// Set the default mix to 0.2 seconds, queue animations, and set listeners
|
||||
const animationState = drawable.animationState;
|
||||
animationState.data.defaultMix = 0.2;
|
||||
animationState.setAnimation(0, "walk", true).listener = {
|
||||
start: (entry) => console.log("Walk animation started"),
|
||||
end: (entry) => console.log("Walk animation ended"),
|
||||
};
|
||||
animationState.addAnimation(0, "jump", false, 2);
|
||||
animationState.addAnimation(0, "run", true, 0).listener = {
|
||||
event: (entry, event) => console.log(`Custom event "${event.data.name}"`)
|
||||
};
|
||||
animationState.addListener({
|
||||
completed: (entry) => console.log(`Animation ${entry.animation.name} completed`)
|
||||
});
|
||||
|
||||
const renderer = new spine.SkeletonRenderer(ck);
|
||||
let lastTime = performance.now();
|
||||
function drawFrame(canvas) {
|
||||
canvas.clear(ck.Color(52, 52, 54, 1));
|
||||
|
||||
const now = performance.now();
|
||||
const deltaTime = (now - lastTime) / 1000;
|
||||
lastTime = now;
|
||||
|
||||
drawable.update(deltaTime);
|
||||
renderer.render(canvas, drawable);
|
||||
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
</script>
|
||||
|
||||
</html>
|
||||
3758
spine-ts/spine-canvaskit/example/assets/celestial-circus-pro.json
Normal file
3758
spine-ts/spine-canvaskit/example/assets/celestial-circus-pro.json
Normal file
File diff suppressed because it is too large
Load Diff
173
spine-ts/spine-canvaskit/example/assets/celestial-circus.atlas
Normal file
173
spine-ts/spine-canvaskit/example/assets/celestial-circus.atlas
Normal file
@ -0,0 +1,173 @@
|
||||
celestial-circus.png
|
||||
size: 1024, 1024
|
||||
filter: Linear, Linear
|
||||
scale: 0.4
|
||||
arm-back-down
|
||||
bounds: 324, 401, 38, 82
|
||||
rotate: 90
|
||||
arm-back-up
|
||||
bounds: 290, 44, 83, 116
|
||||
rotate: 90
|
||||
arm-front-down
|
||||
bounds: 706, 2, 36, 78
|
||||
rotate: 90
|
||||
arm-front-up
|
||||
bounds: 860, 138, 77, 116
|
||||
bench
|
||||
bounds: 725, 256, 189, 48
|
||||
body-bottom
|
||||
bounds: 879, 868, 154, 124
|
||||
rotate: 90
|
||||
body-top
|
||||
bounds: 725, 128, 126, 133
|
||||
rotate: 90
|
||||
chest
|
||||
bounds: 408, 26, 104, 93
|
||||
cloud-back
|
||||
bounds: 752, 378, 202, 165
|
||||
cloud-front
|
||||
bounds: 2, 2, 325, 196
|
||||
rotate: 90
|
||||
collar
|
||||
bounds: 786, 13, 47, 26
|
||||
ear
|
||||
bounds: 1002, 643, 20, 28
|
||||
eye-back-shadow
|
||||
bounds: 428, 395, 14, 10
|
||||
eye-front-shadow
|
||||
bounds: 704, 529, 24, 14
|
||||
eye-reflex-back
|
||||
bounds: 860, 128, 8, 7
|
||||
rotate: 90
|
||||
eye-reflex-front
|
||||
bounds: 726, 386, 10, 7
|
||||
eye-white-back
|
||||
bounds: 835, 23, 13, 16
|
||||
eye-white-front
|
||||
bounds: 1005, 1000, 22, 17
|
||||
rotate: 90
|
||||
eyelashes-down-back
|
||||
bounds: 232, 329, 11, 6
|
||||
rotate: 90
|
||||
eyelashes-down-front
|
||||
bounds: 913, 851, 15, 6
|
||||
rotate: 90
|
||||
eyelashes-top-back
|
||||
bounds: 408, 395, 18, 10
|
||||
eyelashes-top-front
|
||||
bounds: 702, 179, 30, 16
|
||||
rotate: 90
|
||||
face
|
||||
bounds: 514, 26, 93, 102
|
||||
rotate: 90
|
||||
feathers-back
|
||||
bounds: 954, 625, 46, 46
|
||||
feathers-front
|
||||
bounds: 706, 40, 72, 86
|
||||
fringe-middle-back
|
||||
bounds: 200, 6, 33, 52
|
||||
rotate: 90
|
||||
fringe-middle-front
|
||||
bounds: 878, 76, 60, 50
|
||||
rotate: 90
|
||||
fringe-side-back
|
||||
bounds: 780, 41, 27, 94
|
||||
rotate: 90
|
||||
fringe-side-front
|
||||
bounds: 939, 161, 26, 93
|
||||
glove-bottom-back
|
||||
bounds: 954, 572, 51, 41
|
||||
rotate: 90
|
||||
glove-bottom-front
|
||||
bounds: 916, 256, 47, 48
|
||||
hair-back-1
|
||||
bounds: 444, 395, 132, 306
|
||||
rotate: 90
|
||||
hair-back-2
|
||||
bounds: 438, 211, 80, 285
|
||||
rotate: 90
|
||||
hair-back-3
|
||||
bounds: 719, 306, 70, 268
|
||||
rotate: 90
|
||||
hair-back-4
|
||||
bounds: 438, 121, 88, 262
|
||||
rotate: 90
|
||||
hair-back-5
|
||||
bounds: 438, 293, 88, 279
|
||||
rotate: 90
|
||||
hair-back-6
|
||||
bounds: 200, 41, 88, 286
|
||||
hair-hat-shadow
|
||||
bounds: 232, 398, 90, 41
|
||||
hand-back
|
||||
bounds: 954, 673, 60, 47
|
||||
rotate: 90
|
||||
hand-front
|
||||
bounds: 967, 172, 53, 60
|
||||
hat-back
|
||||
bounds: 954, 802, 64, 45
|
||||
rotate: 90
|
||||
hat-front
|
||||
bounds: 780, 70, 96, 56
|
||||
head-back
|
||||
bounds: 618, 17, 102, 86
|
||||
rotate: 90
|
||||
jabot
|
||||
bounds: 967, 234, 70, 55
|
||||
rotate: 90
|
||||
leg-back
|
||||
bounds: 232, 441, 210, 333
|
||||
leg-front
|
||||
bounds: 444, 529, 258, 320
|
||||
logo-brooch
|
||||
bounds: 954, 545, 16, 25
|
||||
mouth
|
||||
bounds: 408, 121, 22, 6
|
||||
neck
|
||||
bounds: 232, 342, 39, 56
|
||||
rotate: 90
|
||||
nose
|
||||
bounds: 742, 529, 6, 7
|
||||
rotate: 90
|
||||
nose-highlight
|
||||
bounds: 719, 300, 4, 4
|
||||
nose-shadow
|
||||
bounds: 869, 128, 7, 8
|
||||
pupil-back
|
||||
bounds: 730, 529, 10, 14
|
||||
pupil-front
|
||||
bounds: 254, 21, 12, 18
|
||||
rope-back
|
||||
bounds: 232, 383, 10, 492
|
||||
rotate: 90
|
||||
rope-front
|
||||
bounds: 232, 383, 10, 492
|
||||
rotate: 90
|
||||
rope-front-bottom
|
||||
bounds: 954, 735, 42, 65
|
||||
skirt
|
||||
bounds: 2, 776, 440, 246
|
||||
sock-bow
|
||||
bounds: 408, 407, 33, 32
|
||||
spine-logo-body
|
||||
bounds: 879, 853, 13, 32
|
||||
rotate: 90
|
||||
star-big
|
||||
bounds: 939, 141, 18, 24
|
||||
rotate: 90
|
||||
star-medium
|
||||
bounds: 742, 537, 6, 8
|
||||
rotate: 90
|
||||
star-small
|
||||
bounds: 719, 378, 3, 4
|
||||
rotate: 90
|
||||
underskirt
|
||||
bounds: 2, 329, 445, 228
|
||||
rotate: 90
|
||||
underskirt-back
|
||||
bounds: 444, 851, 433, 171
|
||||
wing-back
|
||||
bounds: 290, 129, 146, 252
|
||||
wing-front
|
||||
bounds: 704, 545, 304, 248
|
||||
rotate: 90
|
||||
BIN
spine-ts/spine-canvaskit/example/assets/celestial-circus.png
Normal file
BIN
spine-ts/spine-canvaskit/example/assets/celestial-circus.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 790 KiB |
BIN
spine-ts/spine-canvaskit/example/assets/mix-and-match-pro.skel
Normal file
BIN
spine-ts/spine-canvaskit/example/assets/mix-and-match-pro.skel
Normal file
Binary file not shown.
358
spine-ts/spine-canvaskit/example/assets/mix-and-match.atlas
Normal file
358
spine-ts/spine-canvaskit/example/assets/mix-and-match.atlas
Normal file
@ -0,0 +1,358 @@
|
||||
mix-and-match.png
|
||||
size: 1024, 512
|
||||
filter: Linear, Linear
|
||||
scale: 0.5
|
||||
base-head
|
||||
bounds: 118, 70, 95, 73
|
||||
boy/arm-front
|
||||
bounds: 831, 311, 36, 115
|
||||
rotate: 90
|
||||
boy/backpack
|
||||
bounds: 249, 357, 119, 153
|
||||
boy/backpack-pocket
|
||||
bounds: 628, 193, 34, 62
|
||||
rotate: 90
|
||||
boy/backpack-strap-front
|
||||
bounds: 330, 263, 38, 88
|
||||
rotate: 90
|
||||
boy/backpack-up
|
||||
bounds: 482, 171, 21, 70
|
||||
boy/body
|
||||
bounds: 845, 413, 97, 132
|
||||
rotate: 90
|
||||
boy/boot-ribbon-front
|
||||
bounds: 234, 304, 9, 11
|
||||
boy/collar
|
||||
bounds: 471, 243, 73, 29
|
||||
rotate: 90
|
||||
boy/ear
|
||||
bounds: 991, 352, 19, 23
|
||||
rotate: 90
|
||||
boy/eye-back-low-eyelid
|
||||
bounds: 66, 72, 17, 6
|
||||
boy/eye-back-pupil
|
||||
bounds: 694, 279, 8, 9
|
||||
rotate: 90
|
||||
boy/eye-back-up-eyelid
|
||||
bounds: 460, 101, 23, 5
|
||||
rotate: 90
|
||||
boy/eye-back-up-eyelid-back
|
||||
bounds: 979, 414, 19, 10
|
||||
rotate: 90
|
||||
boy/eye-front-low-eyelid
|
||||
bounds: 1015, 203, 22, 7
|
||||
rotate: 90
|
||||
boy/eye-front-pupil
|
||||
bounds: 309, 50, 9, 9
|
||||
boy/eye-front-up-eyelid
|
||||
bounds: 991, 373, 31, 6
|
||||
boy/eye-front-up-eyelid-back
|
||||
bounds: 107, 76, 26, 9
|
||||
rotate: 90
|
||||
boy/eye-iris-back
|
||||
bounds: 810, 260, 17, 17
|
||||
boy/eye-iris-front
|
||||
bounds: 902, 230, 18, 18
|
||||
boy/eye-white-back
|
||||
bounds: 599, 179, 20, 12
|
||||
boy/eye-white-front
|
||||
bounds: 544, 183, 27, 13
|
||||
boy/eyebrow-back
|
||||
bounds: 1002, 225, 20, 11
|
||||
rotate: 90
|
||||
boy/eyebrow-front
|
||||
bounds: 975, 234, 25, 11
|
||||
boy/hair-back
|
||||
bounds: 629, 289, 122, 81
|
||||
rotate: 90
|
||||
boy/hair-bangs
|
||||
bounds: 505, 180, 70, 37
|
||||
rotate: 90
|
||||
boy/hair-side
|
||||
bounds: 979, 435, 25, 43
|
||||
rotate: 90
|
||||
boy/hand-backfingers
|
||||
bounds: 858, 183, 19, 21
|
||||
boy/hand-front-fingers
|
||||
bounds: 879, 183, 19, 21
|
||||
boy/hat
|
||||
bounds: 218, 121, 93, 56
|
||||
boy/leg-front
|
||||
bounds: 85, 104, 31, 158
|
||||
boy/mouth-close
|
||||
bounds: 467, 100, 21, 5
|
||||
girl-blue-cape/mouth-close
|
||||
bounds: 467, 100, 21, 5
|
||||
girl-spring-dress/mouth-close
|
||||
bounds: 467, 100, 21, 5
|
||||
girl/mouth-close
|
||||
bounds: 467, 100, 21, 5
|
||||
boy/mouth-smile
|
||||
bounds: 1015, 258, 29, 7
|
||||
rotate: 90
|
||||
boy/nose
|
||||
bounds: 323, 79, 17, 10
|
||||
boy/pompom
|
||||
bounds: 979, 462, 48, 43
|
||||
rotate: 90
|
||||
boy/zip
|
||||
bounds: 922, 231, 14, 23
|
||||
rotate: 90
|
||||
girl-blue-cape/back-eyebrow
|
||||
bounds: 527, 106, 18, 12
|
||||
rotate: 90
|
||||
girl-blue-cape/body-dress
|
||||
bounds: 2, 264, 109, 246
|
||||
girl-blue-cape/body-ribbon
|
||||
bounds: 576, 193, 50, 38
|
||||
girl-blue-cape/cape-back
|
||||
bounds: 113, 317, 134, 193
|
||||
girl-blue-cape/cape-back-up
|
||||
bounds: 504, 305, 123, 106
|
||||
girl-blue-cape/cape-ribbon
|
||||
bounds: 396, 118, 50, 18
|
||||
rotate: 90
|
||||
girl-blue-cape/cape-shoulder-back
|
||||
bounds: 420, 243, 49, 59
|
||||
girl-blue-cape/cape-shoulder-front
|
||||
bounds: 2, 2, 62, 76
|
||||
girl-blue-cape/cape-up-front
|
||||
bounds: 118, 145, 98, 117
|
||||
girl-blue-cape/ear
|
||||
bounds: 837, 181, 19, 23
|
||||
girl-spring-dress/ear
|
||||
bounds: 837, 181, 19, 23
|
||||
girl/ear
|
||||
bounds: 837, 181, 19, 23
|
||||
girl-blue-cape/eye-back-low-eyelid
|
||||
bounds: 810, 252, 17, 6
|
||||
girl-spring-dress/eye-back-low-eyelid
|
||||
bounds: 810, 252, 17, 6
|
||||
girl/eye-back-low-eyelid
|
||||
bounds: 810, 252, 17, 6
|
||||
girl-blue-cape/eye-back-pupil
|
||||
bounds: 309, 40, 8, 9
|
||||
rotate: 90
|
||||
girl-spring-dress/eye-back-pupil
|
||||
bounds: 309, 40, 8, 9
|
||||
rotate: 90
|
||||
girl/eye-back-pupil
|
||||
bounds: 309, 40, 8, 9
|
||||
rotate: 90
|
||||
girl-blue-cape/eye-back-up-eyelid
|
||||
bounds: 573, 179, 24, 12
|
||||
girl-spring-dress/eye-back-up-eyelid
|
||||
bounds: 573, 179, 24, 12
|
||||
girl/eye-back-up-eyelid
|
||||
bounds: 573, 179, 24, 12
|
||||
girl-blue-cape/eye-back-up-eyelid-back
|
||||
bounds: 380, 105, 17, 11
|
||||
rotate: 90
|
||||
girl-spring-dress/eye-back-up-eyelid-back
|
||||
bounds: 380, 105, 17, 11
|
||||
rotate: 90
|
||||
girl/eye-back-up-eyelid-back
|
||||
bounds: 380, 105, 17, 11
|
||||
rotate: 90
|
||||
girl-blue-cape/eye-front-low-eyelid
|
||||
bounds: 1016, 353, 18, 6
|
||||
rotate: 90
|
||||
girl-spring-dress/eye-front-low-eyelid
|
||||
bounds: 1016, 353, 18, 6
|
||||
rotate: 90
|
||||
girl/eye-front-low-eyelid
|
||||
bounds: 1016, 353, 18, 6
|
||||
rotate: 90
|
||||
girl-blue-cape/eye-front-pupil
|
||||
bounds: 363, 94, 9, 9
|
||||
girl-spring-dress/eye-front-pupil
|
||||
bounds: 363, 94, 9, 9
|
||||
girl/eye-front-pupil
|
||||
bounds: 363, 94, 9, 9
|
||||
girl-blue-cape/eye-front-up-eyelid
|
||||
bounds: 679, 413, 30, 14
|
||||
rotate: 90
|
||||
girl-spring-dress/eye-front-up-eyelid
|
||||
bounds: 679, 413, 30, 14
|
||||
rotate: 90
|
||||
girl/eye-front-up-eyelid
|
||||
bounds: 679, 413, 30, 14
|
||||
rotate: 90
|
||||
girl-blue-cape/eye-front-up-eyelid-back
|
||||
bounds: 947, 234, 26, 11
|
||||
girl-spring-dress/eye-front-up-eyelid-back
|
||||
bounds: 947, 234, 26, 11
|
||||
girl/eye-front-up-eyelid-back
|
||||
bounds: 947, 234, 26, 11
|
||||
girl-blue-cape/eye-iris-back
|
||||
bounds: 323, 105, 17, 17
|
||||
girl-blue-cape/eye-iris-front
|
||||
bounds: 467, 107, 18, 18
|
||||
girl-blue-cape/eye-white-back
|
||||
bounds: 621, 175, 20, 16
|
||||
girl-spring-dress/eye-white-back
|
||||
bounds: 621, 175, 20, 16
|
||||
girl-blue-cape/eye-white-front
|
||||
bounds: 643, 175, 20, 16
|
||||
girl-spring-dress/eye-white-front
|
||||
bounds: 643, 175, 20, 16
|
||||
girl/eye-white-front
|
||||
bounds: 643, 175, 20, 16
|
||||
girl-blue-cape/front-eyebrow
|
||||
bounds: 309, 101, 18, 12
|
||||
rotate: 90
|
||||
girl-blue-cape/hair-back
|
||||
bounds: 712, 317, 117, 98
|
||||
girl-blue-cape/hair-bangs
|
||||
bounds: 313, 170, 91, 40
|
||||
rotate: 90
|
||||
girl-blue-cape/hair-head-side-back
|
||||
bounds: 544, 198, 30, 52
|
||||
girl-blue-cape/hair-head-side-front
|
||||
bounds: 466, 127, 41, 42
|
||||
girl-blue-cape/hair-side
|
||||
bounds: 175, 2, 36, 71
|
||||
rotate: 90
|
||||
girl-blue-cape/hand-front-fingers
|
||||
bounds: 902, 207, 19, 21
|
||||
girl-spring-dress/hand-front-fingers
|
||||
bounds: 902, 207, 19, 21
|
||||
girl-blue-cape/leg-front
|
||||
bounds: 519, 413, 30, 158
|
||||
rotate: 90
|
||||
girl-blue-cape/mouth-smile
|
||||
bounds: 1015, 227, 29, 7
|
||||
rotate: 90
|
||||
girl-spring-dress/mouth-smile
|
||||
bounds: 1015, 227, 29, 7
|
||||
rotate: 90
|
||||
girl/mouth-smile
|
||||
bounds: 1015, 227, 29, 7
|
||||
rotate: 90
|
||||
girl-blue-cape/nose
|
||||
bounds: 342, 82, 11, 7
|
||||
girl-spring-dress/nose
|
||||
bounds: 342, 82, 11, 7
|
||||
girl/nose
|
||||
bounds: 342, 82, 11, 7
|
||||
girl-blue-cape/sleeve-back
|
||||
bounds: 416, 95, 42, 29
|
||||
girl-blue-cape/sleeve-front
|
||||
bounds: 249, 303, 52, 119
|
||||
rotate: 90
|
||||
girl-spring-dress/arm-front
|
||||
bounds: 829, 292, 17, 111
|
||||
rotate: 90
|
||||
girl-spring-dress/back-eyebrow
|
||||
bounds: 309, 81, 18, 12
|
||||
rotate: 90
|
||||
girl-spring-dress/body-up
|
||||
bounds: 66, 2, 64, 66
|
||||
girl-spring-dress/cloak-down
|
||||
bounds: 758, 227, 50, 50
|
||||
girl-spring-dress/cloak-up
|
||||
bounds: 628, 229, 64, 58
|
||||
girl-spring-dress/eye-iris-back
|
||||
bounds: 342, 105, 17, 17
|
||||
girl-spring-dress/eye-iris-front
|
||||
bounds: 487, 107, 18, 18
|
||||
girl-spring-dress/front-eyebrow
|
||||
bounds: 323, 91, 18, 12
|
||||
girl-spring-dress/hair-back
|
||||
bounds: 370, 417, 147, 93
|
||||
girl-spring-dress/hair-bangs
|
||||
bounds: 829, 250, 91, 40
|
||||
girl-spring-dress/hair-head-side-back
|
||||
bounds: 509, 126, 30, 52
|
||||
girl-spring-dress/hair-head-side-front
|
||||
bounds: 816, 206, 41, 42
|
||||
girl-spring-dress/hair-side
|
||||
bounds: 248, 2, 36, 71
|
||||
rotate: 90
|
||||
girl-spring-dress/leg-front
|
||||
bounds: 831, 381, 30, 158
|
||||
rotate: 90
|
||||
girl-spring-dress/neck
|
||||
bounds: 85, 70, 20, 32
|
||||
girl-spring-dress/shoulder-ribbon
|
||||
bounds: 175, 44, 36, 24
|
||||
girl-spring-dress/skirt
|
||||
bounds: 2, 80, 182, 81
|
||||
rotate: 90
|
||||
girl-spring-dress/underskirt
|
||||
bounds: 519, 445, 175, 65
|
||||
girl/arm-front
|
||||
bounds: 712, 279, 36, 115
|
||||
rotate: 90
|
||||
girl/back-eyebrow
|
||||
bounds: 309, 61, 18, 12
|
||||
rotate: 90
|
||||
girl/bag-base
|
||||
bounds: 694, 219, 62, 58
|
||||
girl/bag-strap-front
|
||||
bounds: 370, 304, 12, 96
|
||||
rotate: 90
|
||||
girl/bag-top
|
||||
bounds: 765, 175, 49, 50
|
||||
girl/body
|
||||
bounds: 370, 318, 97, 132
|
||||
rotate: 90
|
||||
girl/boot-ribbon-front
|
||||
bounds: 323, 64, 13, 13
|
||||
girl/eye-iris-back
|
||||
bounds: 361, 105, 17, 17
|
||||
girl/eye-iris-front
|
||||
bounds: 507, 106, 18, 18
|
||||
girl/eye-white-back
|
||||
bounds: 665, 175, 20, 16
|
||||
girl/front-eyebrow
|
||||
bounds: 343, 91, 18, 12
|
||||
girl/hair-back
|
||||
bounds: 696, 417, 147, 93
|
||||
girl/hair-bangs
|
||||
bounds: 922, 247, 91, 40
|
||||
girl/hair-flap-down-front
|
||||
bounds: 415, 171, 70, 65
|
||||
rotate: 90
|
||||
girl/hair-head-side-back
|
||||
bounds: 991, 381, 30, 52
|
||||
girl/hair-head-side-front
|
||||
bounds: 859, 206, 41, 42
|
||||
girl/hair-patch
|
||||
bounds: 132, 2, 66, 41
|
||||
rotate: 90
|
||||
girl/hair-side
|
||||
bounds: 692, 181, 36, 71
|
||||
rotate: 90
|
||||
girl/hair-strand-back-1
|
||||
bounds: 948, 289, 58, 74
|
||||
rotate: 90
|
||||
girl/hair-strand-back-2
|
||||
bounds: 355, 170, 91, 58
|
||||
rotate: 90
|
||||
girl/hair-strand-back-3
|
||||
bounds: 215, 40, 92, 79
|
||||
girl/hair-strand-front-1
|
||||
bounds: 234, 263, 38, 94
|
||||
rotate: 90
|
||||
girl/hair-strand-front-2
|
||||
bounds: 576, 233, 70, 50
|
||||
rotate: 90
|
||||
girl/hair-strand-front-3
|
||||
bounds: 313, 124, 44, 81
|
||||
rotate: 90
|
||||
girl/hand-front-fingers
|
||||
bounds: 923, 208, 19, 21
|
||||
girl/hat
|
||||
bounds: 218, 179, 93, 82
|
||||
girl/leg-front
|
||||
bounds: 831, 349, 30, 158
|
||||
rotate: 90
|
||||
girl/pompom
|
||||
bounds: 416, 126, 48, 43
|
||||
girl/scarf
|
||||
bounds: 113, 264, 119, 51
|
||||
girl/scarf-back
|
||||
bounds: 502, 252, 72, 51
|
||||
girl/zip
|
||||
bounds: 816, 179, 19, 25
|
||||
BIN
spine-ts/spine-canvaskit/example/assets/mix-and-match.png
Normal file
BIN
spine-ts/spine-canvaskit/example/assets/mix-and-match.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 341 KiB |
BIN
spine-ts/spine-canvaskit/example/assets/spineboy-pro.skel
Normal file
BIN
spine-ts/spine-canvaskit/example/assets/spineboy-pro.skel
Normal file
Binary file not shown.
94
spine-ts/spine-canvaskit/example/assets/spineboy.atlas
Normal file
94
spine-ts/spine-canvaskit/example/assets/spineboy.atlas
Normal file
@ -0,0 +1,94 @@
|
||||
spineboy.png
|
||||
size: 1024, 256
|
||||
filter: Linear, Linear
|
||||
scale: 0.5
|
||||
crosshair
|
||||
bounds: 352, 7, 45, 45
|
||||
eye-indifferent
|
||||
bounds: 862, 105, 47, 45
|
||||
eye-surprised
|
||||
bounds: 505, 79, 47, 45
|
||||
front-bracer
|
||||
bounds: 826, 66, 29, 40
|
||||
front-fist-closed
|
||||
bounds: 786, 65, 38, 41
|
||||
front-fist-open
|
||||
bounds: 710, 51, 43, 44
|
||||
rotate: 90
|
||||
front-foot
|
||||
bounds: 210, 6, 63, 35
|
||||
front-shin
|
||||
bounds: 665, 128, 41, 92
|
||||
rotate: 90
|
||||
front-thigh
|
||||
bounds: 2, 2, 23, 56
|
||||
rotate: 90
|
||||
front-upper-arm
|
||||
bounds: 250, 205, 23, 49
|
||||
goggles
|
||||
bounds: 665, 171, 131, 83
|
||||
gun
|
||||
bounds: 798, 152, 105, 102
|
||||
head
|
||||
bounds: 2, 27, 136, 149
|
||||
hoverboard-board
|
||||
bounds: 2, 178, 246, 76
|
||||
hoverboard-thruster
|
||||
bounds: 722, 96, 30, 32
|
||||
rotate: 90
|
||||
hoverglow-small
|
||||
bounds: 275, 81, 137, 38
|
||||
mouth-grind
|
||||
bounds: 614, 97, 47, 30
|
||||
mouth-oooo
|
||||
bounds: 612, 65, 47, 30
|
||||
mouth-smile
|
||||
bounds: 661, 64, 47, 30
|
||||
muzzle-glow
|
||||
bounds: 382, 54, 25, 25
|
||||
muzzle-ring
|
||||
bounds: 275, 54, 25, 105
|
||||
rotate: 90
|
||||
muzzle01
|
||||
bounds: 911, 95, 67, 40
|
||||
rotate: 90
|
||||
muzzle02
|
||||
bounds: 792, 108, 68, 42
|
||||
muzzle03
|
||||
bounds: 956, 171, 83, 53
|
||||
rotate: 90
|
||||
muzzle04
|
||||
bounds: 275, 7, 75, 45
|
||||
muzzle05
|
||||
bounds: 140, 3, 68, 38
|
||||
neck
|
||||
bounds: 250, 182, 18, 21
|
||||
portal-bg
|
||||
bounds: 140, 43, 133, 133
|
||||
portal-flare1
|
||||
bounds: 554, 65, 56, 30
|
||||
portal-flare2
|
||||
bounds: 759, 112, 57, 31
|
||||
rotate: 90
|
||||
portal-flare3
|
||||
bounds: 554, 97, 58, 30
|
||||
portal-shade
|
||||
bounds: 275, 121, 133, 133
|
||||
portal-streaks1
|
||||
bounds: 410, 126, 126, 128
|
||||
portal-streaks2
|
||||
bounds: 538, 129, 125, 125
|
||||
rear-bracer
|
||||
bounds: 857, 67, 28, 36
|
||||
rear-foot
|
||||
bounds: 663, 96, 57, 30
|
||||
rear-shin
|
||||
bounds: 414, 86, 38, 89
|
||||
rotate: 90
|
||||
rear-thigh
|
||||
bounds: 756, 63, 28, 47
|
||||
rear-upper-arm
|
||||
bounds: 60, 5, 20, 44
|
||||
rotate: 90
|
||||
torso
|
||||
bounds: 905, 164, 49, 90
|
||||
BIN
spine-ts/spine-canvaskit/example/assets/spineboy.png
Normal file
BIN
spine-ts/spine-canvaskit/example/assets/spineboy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 240 KiB |
74
spine-ts/spine-canvaskit/example/headless.js
Normal file
74
spine-ts/spine-canvaskit/example/headless.js
Normal file
@ -0,0 +1,74 @@
|
||||
import * as fs from "fs"
|
||||
import { fileURLToPath } from 'url';
|
||||
import path from 'path';
|
||||
import CanvasKitInit from "canvaskit-wasm";
|
||||
import UPNG from "@pdf-lib/upng"
|
||||
import {loadTextureAtlas, SkeletonRenderer, Skeleton, SkeletonBinary, AnimationState, AnimationStateData, AtlasAttachmentLoader, Physics, loadSkeletonData, SkeletonDrawable} from "../dist/index.js"
|
||||
|
||||
// Get the current directory
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
// This app loads the Spineboy skeleton and its atlas, then renders Spineboy's "portal" animation
|
||||
// at 30 fps to individual frames, which are then encoded as an animated PNG (APNG), which is
|
||||
// written to "output.png"
|
||||
async function main() {
|
||||
// Initialize CanvasKit and create a surface and canvas.
|
||||
const ck = await CanvasKitInit();
|
||||
const surface = ck.MakeSurface(600, 400);
|
||||
if (!surface) throw new Error();
|
||||
|
||||
// Load atlas
|
||||
const atlas = await loadTextureAtlas(ck, __dirname + "/assets/spineboy.atlas", async (path) => fs.readFileSync(path));
|
||||
|
||||
// Load the skeleton data
|
||||
const skeletonData = await loadSkeletonData(__dirname + "/assets/spineboy-pro.skel", atlas, async (path) => fs.readFileSync(path));
|
||||
|
||||
// Create a SkeletonDrawable
|
||||
const drawable = new SkeletonDrawable(skeletonData);
|
||||
|
||||
// Scale and position the skeleton
|
||||
drawable.skeleton.x = 300;
|
||||
drawable.skeleton.y = 380;
|
||||
drawable.skeleton.scaleX = drawable.skeleton.scaleY = 0.5;
|
||||
|
||||
// Set the "hoverboard" animation on track one
|
||||
drawable.animationState.setAnimation(0, "hoverboard", true);
|
||||
|
||||
// Create a skeleton renderer to render the skeleton to the canvas with
|
||||
const renderer = new SkeletonRenderer(ck);
|
||||
|
||||
// Render the full animation in 1/30 second steps (30fps) and save it to an APNG
|
||||
const animationDuration = skeletonData.findAnimation("hoverboard")?.duration ?? 0;
|
||||
const FRAME_TIME = 1 / 30; // 30 FPS
|
||||
let deltaTime = 0;
|
||||
const frames = [];
|
||||
const imageInfo = { width: 600, height: 400, colorType: ck.ColorType.RGBA_8888, alphaType: ck.AlphaType.Unpremul, colorSpace: ck.ColorSpace.SRGB };
|
||||
const pixelArray = ck.Malloc(Uint8Array, imageInfo.width * imageInfo.height * 4);
|
||||
for (let time = 0; time <= animationDuration; time += deltaTime) {
|
||||
// Get the canvas object to render to the surface to
|
||||
const canvas = surface.getCanvas();
|
||||
|
||||
// Clear the canvas
|
||||
canvas.clear(ck.WHITE);
|
||||
|
||||
// Update the drawable, which will advance the animation(s)
|
||||
// apply them to the skeleton, and update the skeleton's pose.
|
||||
drawable.update(deltaTime);
|
||||
|
||||
// Render the skeleton to the canvas
|
||||
renderer.render(canvas, drawable)
|
||||
|
||||
// Read the pixels of the current frame and store it.
|
||||
canvas.readPixels(0, 0, imageInfo, pixelArray);
|
||||
frames.push(new Uint8Array(pixelArray.toTypedArray()).buffer.slice(0));
|
||||
|
||||
// First frame has deltaTime 0, subsequent use FRAME_TIME
|
||||
deltaTime = FRAME_TIME;
|
||||
}
|
||||
|
||||
const apng = UPNG.default.encode(frames, 600, 400, 0, frames.map(() => FRAME_TIME * 1000));
|
||||
fs.writeFileSync('output.png', Buffer.from(apng));
|
||||
}
|
||||
|
||||
main();
|
||||
105
spine-ts/spine-canvaskit/example/ik-following.html
Normal file
105
spine-ts/spine-canvaskit/example/ik-following.html
Normal file
@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
<script src="https://unpkg.com/canvaskit-wasm@latest/bin/canvaskit.js"></script>
|
||||
<script src="../dist/iife/spine-canvaskit.js"></script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="p-4 flex flex-col items-center">
|
||||
<h1>IK Following</h1>
|
||||
<p class="mb-4">Click/touch to set the aim</p>
|
||||
<canvas id=foo style="margin: 0 auto; width: 600px; height: 400px;"></canvas>
|
||||
</body>
|
||||
|
||||
<script type="module">
|
||||
async function readFile(path) {
|
||||
const response = await fetch(path);
|
||||
if (!response.ok) throw new Error("Could not load file " + path);
|
||||
return await response.arrayBuffer();
|
||||
}
|
||||
|
||||
const canvasElement = document.querySelector("#foo");
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
canvasElement.width = canvasElement.clientWidth * dpr;
|
||||
canvasElement.height = canvasElement.clientHeight * dpr;
|
||||
|
||||
const ck = await CanvasKitInit();
|
||||
const surface = ck.MakeCanvasSurface('foo');
|
||||
surface.getCanvas().scale(dpr, dpr);
|
||||
|
||||
const atlas = await spine.loadTextureAtlas(ck, "assets/spineboy.atlas", readFile);
|
||||
const skeletonData = await spine.loadSkeletonData("assets/spineboy-pro.skel", atlas, readFile);
|
||||
const drawable = new spine.SkeletonDrawable(skeletonData);
|
||||
drawable.skeleton.scaleX = drawable.skeleton.scaleY = 0.5;
|
||||
drawable.skeleton.x = 100;
|
||||
drawable.skeleton.y = 380;
|
||||
|
||||
// Set the walk animation on track 0 and the aim animation on track 1
|
||||
drawable.animationState.setAnimation(0, "walk", true);
|
||||
drawable.animationState.setAnimation(1, "aim", true);
|
||||
|
||||
// Set up touch and mouse listeners on the canvas element
|
||||
// and set the touch/mouse coordinate on the aim bone
|
||||
const canvasElement = document.querySelector("#foo");
|
||||
let mouseDown = false;
|
||||
canvasElement.addEventListener("touchmove", (ev) => setCrosshairPosition(ev.changedTouches[0].clientX, ev.changedTouches[0].clientY));
|
||||
canvasElement.addEventListener("mousedown", (ev) => {
|
||||
mouseDown = true;
|
||||
setCrosshairPosition(ev.clientX, ev.clientY);
|
||||
});
|
||||
canvasElement.addEventListener("mouseup", () => mouseDown = false)
|
||||
canvasElement.addEventListener("mousemove", (ev) => {
|
||||
if (mouseDown) setCrosshairPosition(ev.clientX, ev.clientY);
|
||||
})
|
||||
|
||||
// Get the crosshair bone. We will adjust its position based on the
|
||||
// touch/mouse coordinates
|
||||
const crosshair = drawable.skeleton.findBone("crosshair");
|
||||
|
||||
// Sets the crosshair bone position based on the touch/mouse position
|
||||
const setCrosshairPosition = (touchX, touchY) => {
|
||||
const clientRect = canvasElement.getBoundingClientRect();
|
||||
let x = touchX - clientRect.left;
|
||||
let y = touchY - clientRect.top;
|
||||
|
||||
// Transform the touch/mouse position to the crosshair
|
||||
// bone's parent bone coordinate system
|
||||
const parent = crosshair.parent;
|
||||
const parentPosition = new spine.Vector2(x, y);
|
||||
parent.worldToLocal(parentPosition)
|
||||
|
||||
// Set the position on the bone (relative to its parent bone)
|
||||
crosshair.x = parentPosition.x;
|
||||
crosshair.y = parentPosition.y;
|
||||
}
|
||||
|
||||
|
||||
const renderer = new spine.SkeletonRenderer(ck);
|
||||
let lastTime = performance.now();
|
||||
|
||||
function drawFrame(canvas) {
|
||||
canvas.clear(ck.Color(52, 52, 54, 1));
|
||||
|
||||
const now = performance.now();
|
||||
const deltaTime = (now - lastTime) / 1000;
|
||||
lastTime = now;
|
||||
|
||||
drawable.update(deltaTime);
|
||||
renderer.render(canvas, drawable);
|
||||
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
</script>
|
||||
|
||||
</html>
|
||||
86
spine-ts/spine-canvaskit/example/index.html
Normal file
86
spine-ts/spine-canvaskit/example/index.html
Normal file
@ -0,0 +1,86 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
<script src="https://unpkg.com/canvaskit-wasm@latest/bin/canvaskit.js"></script>
|
||||
<script src="../dist/iife/spine-canvaskit.js"></script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="p-4 flex flex-col items-center">
|
||||
<h1>CanvasKit Example</h1>
|
||||
<canvas id=foo style="margin: 0 auto; width: 600px; height: 400px;"></canvas>
|
||||
</body>
|
||||
|
||||
<script type="module">
|
||||
// Function to read file contents from a path, used to load texture atlas and skeleton file.
|
||||
async function readFile(path) {
|
||||
const response = await fetch(path);
|
||||
if (!response.ok) throw new Error("Could not load file " + path);
|
||||
return await response.arrayBuffer();
|
||||
}
|
||||
|
||||
// Ensure we render at full DPI.
|
||||
const canvasElement = document.querySelector("#foo");
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
canvasElement.width = canvasElement.clientWidth * dpr;
|
||||
canvasElement.height = canvasElement.clientHeight * dpr;
|
||||
|
||||
// Initialize CanvasKit and create a surface from the Canvas element to draw to
|
||||
const ck = await CanvasKitInit();
|
||||
const surface = ck.MakeCanvasSurface('foo');
|
||||
|
||||
// Scale the CanvasKit coordinate system
|
||||
surface.getCanvas().scale(dpr, dpr);
|
||||
|
||||
// Load the texture atlas
|
||||
const atlas = await spine.loadTextureAtlas(ck, "assets/spineboy.atlas", readFile);
|
||||
|
||||
// Load skeleton data
|
||||
const skeletonData = await spine.loadSkeletonData("assets/spineboy-pro.skel", atlas, readFile);
|
||||
|
||||
// Create a drawable and scale and position the skeleton
|
||||
const drawable = new spine.SkeletonDrawable(skeletonData);
|
||||
drawable.skeleton.scaleX = drawable.skeleton.scaleY = 0.4;
|
||||
drawable.skeleton.x = 300;
|
||||
drawable.skeleton.y = 380;
|
||||
|
||||
// Set the "hoverboard" animation on the first track of the animation state.
|
||||
drawable.animationState.setAnimation(0, "hoverboard", true);
|
||||
|
||||
// Create a skeleton renderer to render the skeleton with to the canvas
|
||||
const renderer = new spine.SkeletonRenderer(ck);
|
||||
|
||||
let lastTime = performance.now();
|
||||
// Rendering loop
|
||||
function drawFrame(canvas) {
|
||||
// Clear the canvas
|
||||
canvas.clear(ck.Color(52, 52, 54, 1));
|
||||
|
||||
// Calculate the time that's passed between now and the last frame
|
||||
const now = performance.now();
|
||||
const deltaTime = (now - lastTime) / 1000;
|
||||
lastTime = now;
|
||||
|
||||
// Update the drawable, which will advance the animation(s)
|
||||
// apply them to the skeleton, and update the skeleton's pose.
|
||||
drawable.update(deltaTime);
|
||||
|
||||
// Render the skeleton to the canvas
|
||||
renderer.render(canvas, drawable);
|
||||
|
||||
// Request the next frame
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
</script>
|
||||
|
||||
</html>
|
||||
78
spine-ts/spine-canvaskit/example/micro-benchmark.html
Normal file
78
spine-ts/spine-canvaskit/example/micro-benchmark.html
Normal file
@ -0,0 +1,78 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
<script src="https://unpkg.com/canvaskit-wasm@latest/bin/canvaskit.js"></script>
|
||||
<script src="../dist/iife/spine-canvaskit.js"></script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="p-4 flex flex-col items-center">
|
||||
<h1>Micro Benchmark</h1>
|
||||
<div id="timing"></div>
|
||||
<canvas id=foo style="margin: 0 auto; width: 600px; height: 400px;"></canvas>
|
||||
</body>
|
||||
|
||||
<script type="module">
|
||||
async function readFile(path) {
|
||||
const response = await fetch(path);
|
||||
if (!response.ok) throw new Error("Could not load file " + path);
|
||||
return await response.arrayBuffer();
|
||||
}
|
||||
|
||||
const canvasElement = document.querySelector("#foo");
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
canvasElement.width = canvasElement.clientWidth * dpr;
|
||||
canvasElement.height = canvasElement.clientHeight * dpr;
|
||||
|
||||
const ck = await CanvasKitInit();
|
||||
const surface = ck.MakeCanvasSurface('foo');
|
||||
surface.getCanvas().scale(dpr, dpr);
|
||||
|
||||
const atlas = await spine.loadTextureAtlas(ck, "assets/spineboy.atlas", readFile);
|
||||
const skeletonData = await spine.loadSkeletonData("assets/spineboy-pro.skel", atlas, readFile);
|
||||
|
||||
// Instantiate 100 drawables, randomly placed and scaled.
|
||||
const drawables = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const drawable = new spine.SkeletonDrawable(skeletonData);
|
||||
drawable.skeleton.scaleX = drawable.skeleton.scaleY = 0.1 + Math.random() * 0.4;
|
||||
drawable.skeleton.x = Math.random() * 600;
|
||||
drawable.skeleton.y = Math.random() * 400;
|
||||
drawable.animationState.setAnimation(0, "walk", true);
|
||||
|
||||
drawables.push(drawable);
|
||||
}
|
||||
|
||||
const timingElement = document.querySelector("#timing");
|
||||
const renderer = new spine.SkeletonRenderer(ck);
|
||||
let lastTime = performance.now();
|
||||
function drawFrame(canvas) {
|
||||
canvas.clear(ck.Color(52, 52, 54, 1));
|
||||
|
||||
const now = performance.now();
|
||||
const deltaTime = (now - lastTime) / 1000;
|
||||
lastTime = now;
|
||||
|
||||
// Render all drawables
|
||||
for (let i = 0; i < drawables.length; i++) {
|
||||
const drawable = drawables[i];
|
||||
drawable.update(deltaTime);
|
||||
renderer.render(canvas, drawable);
|
||||
}
|
||||
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
timingElement.textContent = (deltaTime * 1000).toFixed(0) + "ms/frame";
|
||||
}
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
</script>
|
||||
|
||||
</html>
|
||||
84
spine-ts/spine-canvaskit/example/mix-and-match.html
Normal file
84
spine-ts/spine-canvaskit/example/mix-and-match.html
Normal file
@ -0,0 +1,84 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
<script src="https://unpkg.com/canvaskit-wasm@latest/bin/canvaskit.js"></script>
|
||||
<script src="../dist/iife/spine-canvaskit.js"></script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="p-4 flex flex-col items-center">
|
||||
<h1>Skins Mix & Match Example</h1>
|
||||
<canvas id=foo style="margin: 0 auto; width: 600px; height: 400px;"></canvas>
|
||||
</body>
|
||||
|
||||
<script type="module">
|
||||
async function readFile(path) {
|
||||
const response = await fetch(path);
|
||||
if (!response.ok) throw new Error("Could not load file " + path);
|
||||
return await response.arrayBuffer();
|
||||
}
|
||||
|
||||
const canvasElement = document.querySelector("#foo");
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
canvasElement.width = canvasElement.clientWidth * dpr;
|
||||
canvasElement.height = canvasElement.clientHeight * dpr;
|
||||
|
||||
const ck = await CanvasKitInit();
|
||||
const surface = ck.MakeCanvasSurface('foo');
|
||||
surface.getCanvas().scale(dpr, dpr);
|
||||
|
||||
const atlas = await spine.loadTextureAtlas(ck, "assets/mix-and-match.atlas", readFile);
|
||||
const skeletonData = await spine.loadSkeletonData("assets/mix-and-match-pro.skel", atlas, readFile);
|
||||
|
||||
const drawable = new spine.SkeletonDrawable(skeletonData);
|
||||
drawable.skeleton.scaleX = drawable.skeleton.scaleY = 0.4;
|
||||
drawable.skeleton.x = 300;
|
||||
drawable.skeleton.y = 380;
|
||||
|
||||
drawable.animationState.setAnimation(0, "dance", true);
|
||||
|
||||
// Create a custom, empty skin
|
||||
const skin = new spine.Skin("custom");
|
||||
|
||||
// Add other skins to the custom skin
|
||||
skin.addSkin(skeletonData.findSkin("skin-base"));
|
||||
skin.addSkin(skeletonData.findSkin("nose/short"));
|
||||
skin.addSkin(skeletonData.findSkin("eyelids/girly"));
|
||||
skin.addSkin(skeletonData.findSkin("eyes/violet"));
|
||||
skin.addSkin(skeletonData.findSkin("hair/brown"));
|
||||
skin.addSkin(skeletonData.findSkin("clothes/hoodie-orange"));
|
||||
skin.addSkin(skeletonData.findSkin("legs/pants-jeans"));
|
||||
skin.addSkin(skeletonData.findSkin("accessories/bag"));
|
||||
skin.addSkin(skeletonData.findSkin("accessories/hat-red-yellow"));
|
||||
|
||||
// Set the skin and the skeleton to the setup pose
|
||||
drawable.skeleton.setSkin(skin);
|
||||
drawable.skeleton.setSlotsToSetupPose();
|
||||
|
||||
const renderer = new spine.SkeletonRenderer(ck);
|
||||
let lastTime = performance.now();
|
||||
function drawFrame(canvas) {
|
||||
canvas.clear(ck.Color(52, 52, 54, 1));
|
||||
|
||||
const now = performance.now();
|
||||
const deltaTime = (now - lastTime) / 1000;
|
||||
lastTime = now;
|
||||
|
||||
drawable.update(deltaTime);
|
||||
renderer.render(canvas, drawable);
|
||||
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
</script>
|
||||
|
||||
</html>
|
||||
103
spine-ts/spine-canvaskit/example/physics.html
Normal file
103
spine-ts/spine-canvaskit/example/physics.html
Normal file
@ -0,0 +1,103 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="../../index.css">
|
||||
<script src="https://unpkg.com/canvaskit-wasm@latest/bin/canvaskit.js"></script>
|
||||
<script src="../dist/iife/spine-canvaskit.js"></script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="p-4 flex flex-col items-center">
|
||||
<h1>IK Following</h1>
|
||||
<p class="mb-4">Drag anywhere</p>
|
||||
<canvas id=foo style="margin: 0 auto; width: 600px; height: 400px;"></canvas>
|
||||
</body>
|
||||
|
||||
<script type="module">
|
||||
async function readFile(path) {
|
||||
const response = await fetch(path);
|
||||
if (!response.ok) throw new Error("Could not load file " + path);
|
||||
return await response.arrayBuffer();
|
||||
}
|
||||
|
||||
const canvasElement = document.querySelector("#foo");
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
canvasElement.width = canvasElement.clientWidth * dpr;
|
||||
canvasElement.height = canvasElement.clientHeight * dpr;
|
||||
|
||||
const ck = await CanvasKitInit();
|
||||
const surface = ck.MakeCanvasSurface('foo');
|
||||
surface.getCanvas().scale(dpr, dpr);
|
||||
|
||||
const atlas = await spine.loadTextureAtlas(ck, "assets/celestial-circus.atlas", readFile);
|
||||
const skeletonData = await spine.loadSkeletonData("assets/celestial-circus-pro.json", atlas, readFile);
|
||||
const drawable = new spine.SkeletonDrawable(skeletonData);
|
||||
drawable.skeleton.scaleX = drawable.skeleton.scaleY = 0.15;
|
||||
drawable.skeleton.x = 300;
|
||||
drawable.skeleton.y = 300;
|
||||
|
||||
// Set the blink animation on track 0
|
||||
drawable.animationState.setAnimation(0, "eyeblink-long", true);
|
||||
|
||||
// Set up touch and mouse listeners on the canvas element
|
||||
// and set the touch/mouse coordinate on the aim bone
|
||||
let mouseDown = false;
|
||||
let lastX = -1, lastY = -1;
|
||||
canvasElement.addEventListener("touchmove", (ev) => drag(ev.changedTouches[0].clientX, ev.changedTouches[0].clientY));
|
||||
canvasElement.addEventListener("mousedown", (ev) => {
|
||||
mouseDown = true;
|
||||
drag(ev.clientX, ev.clientY);
|
||||
});
|
||||
canvasElement.addEventListener("mouseup", () => {
|
||||
mouseDown = false;
|
||||
lastX = -1; lastY = -1;
|
||||
})
|
||||
canvasElement.addEventListener("mousemove", (ev) => {
|
||||
if (mouseDown) drag(ev.clientX, ev.clientY);
|
||||
})
|
||||
|
||||
// Move the skeleton around based on the distance between
|
||||
// the last touch/mouse location and the current touch/mouse location.
|
||||
const drag = (touchX, touchY) => {
|
||||
const clientRect = canvasElement.getBoundingClientRect();
|
||||
let x = touchX - clientRect.left;
|
||||
let y = touchY - clientRect.top;
|
||||
if (lastX == -1 && lastY == -1) {
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
return;
|
||||
}
|
||||
|
||||
drawable.skeleton.x += (x - lastX);
|
||||
drawable.skeleton.y += (y - lastY);
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
}
|
||||
|
||||
const renderer = new spine.SkeletonRenderer(ck);
|
||||
let lastTime = performance.now();
|
||||
|
||||
function drawFrame(canvas) {
|
||||
canvas.clear(ck.Color(52, 52, 54, 1));
|
||||
|
||||
const now = performance.now();
|
||||
const deltaTime = (now - lastTime) / 1000;
|
||||
lastTime = now;
|
||||
|
||||
drawable.update(deltaTime);
|
||||
renderer.render(canvas, drawable);
|
||||
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
</script>
|
||||
|
||||
</html>
|
||||
41
spine-ts/spine-canvaskit/package.json
Normal file
41
spine-ts/spine-canvaskit/package.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "@esotericsoftware/spine-canvaskit",
|
||||
"version": "4.2.56",
|
||||
"description": "The official Spine Runtimes for CanvasKit for NodeJS",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"scripts": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/esotericsoftware/spine-runtimes.git"
|
||||
},
|
||||
"keywords": [
|
||||
"gamedev",
|
||||
"animations",
|
||||
"2d",
|
||||
"spine",
|
||||
"game-dev",
|
||||
"runtimes",
|
||||
"skeletal"
|
||||
],
|
||||
"author": "Esoteric Software LLC",
|
||||
"license": "LicenseRef-LICENSE",
|
||||
"bugs": {
|
||||
"url": "https://github.com/esotericsoftware/spine-runtimes/issues"
|
||||
},
|
||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-core": "4.2.56",
|
||||
"canvaskit-wasm": "0.39.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pdf-lib/upng": "1.0.1",
|
||||
"@types/node": "20.14.9"
|
||||
}
|
||||
}
|
||||
345
spine-ts/spine-canvaskit/src/index.ts
Normal file
345
spine-ts/spine-canvaskit/src/index.ts
Normal file
@ -0,0 +1,345 @@
|
||||
export * from "@esotericsoftware/spine-core";
|
||||
|
||||
import {
|
||||
AnimationState,
|
||||
AnimationStateData,
|
||||
AtlasAttachmentLoader,
|
||||
BlendMode,
|
||||
ClippingAttachment,
|
||||
Color,
|
||||
MeshAttachment,
|
||||
NumberArrayLike,
|
||||
Physics,
|
||||
RegionAttachment,
|
||||
Skeleton,
|
||||
SkeletonBinary,
|
||||
SkeletonClipping,
|
||||
SkeletonData,
|
||||
SkeletonJson,
|
||||
Texture,
|
||||
TextureAtlas,
|
||||
TextureFilter,
|
||||
TextureWrap,
|
||||
Utils,
|
||||
} from "@esotericsoftware/spine-core";
|
||||
import {
|
||||
Canvas,
|
||||
Surface,
|
||||
CanvasKit,
|
||||
Image,
|
||||
Paint,
|
||||
Shader,
|
||||
BlendMode as CanvasKitBlendMode,
|
||||
} from "canvaskit-wasm";
|
||||
|
||||
Skeleton.yDown = true;
|
||||
|
||||
type CanvasKitImage = {
|
||||
shaders: Shader[];
|
||||
paintPerBlendMode: Map<BlendMode, Paint>;
|
||||
image: Image;
|
||||
};
|
||||
|
||||
// CanvasKit blend modes for premultiplied alpha
|
||||
function toCkBlendMode (ck: CanvasKit, blendMode: BlendMode) {
|
||||
switch (blendMode) {
|
||||
case BlendMode.Normal:
|
||||
return ck.BlendMode.SrcOver;
|
||||
case BlendMode.Additive:
|
||||
return ck.BlendMode.Plus;
|
||||
case BlendMode.Multiply:
|
||||
return ck.BlendMode.SrcOver;
|
||||
case BlendMode.Screen:
|
||||
return ck.BlendMode.Screen;
|
||||
default:
|
||||
return ck.BlendMode.SrcOver;
|
||||
}
|
||||
}
|
||||
|
||||
function bufferToUtf8String (buffer: any) {
|
||||
if (typeof Buffer !== "undefined") {
|
||||
return buffer.toString("utf-8");
|
||||
} else if (typeof TextDecoder !== "undefined") {
|
||||
return new TextDecoder("utf-8").decode(buffer);
|
||||
} else {
|
||||
throw new Error("Unsupported environment");
|
||||
}
|
||||
}
|
||||
|
||||
class CanvasKitTexture extends Texture {
|
||||
getImage (): CanvasKitImage {
|
||||
return this._image;
|
||||
}
|
||||
|
||||
setFilters (minFilter: TextureFilter, magFilter: TextureFilter): void { }
|
||||
|
||||
setWraps (uWrap: TextureWrap, vWrap: TextureWrap): void { }
|
||||
|
||||
dispose (): void {
|
||||
const data: CanvasKitImage = this._image;
|
||||
for (const paint of data.paintPerBlendMode.values()) {
|
||||
paint.delete();
|
||||
}
|
||||
for (const shader of data.shaders) {
|
||||
shader.delete();
|
||||
}
|
||||
data.image.delete();
|
||||
this._image = null;
|
||||
}
|
||||
|
||||
static async fromFile (
|
||||
ck: CanvasKit,
|
||||
path: string,
|
||||
readFile: (path: string) => Promise<any>
|
||||
): Promise<CanvasKitTexture> {
|
||||
const imgData = await readFile(path);
|
||||
if (!imgData) throw new Error(`Could not load image ${path}`);
|
||||
const image = ck.MakeImageFromEncoded(imgData);
|
||||
if (!image) throw new Error(`Could not load image ${path}`);
|
||||
const paintPerBlendMode = new Map<BlendMode, Paint>();
|
||||
const shaders: Shader[] = [];
|
||||
for (const blendMode of [
|
||||
BlendMode.Normal,
|
||||
BlendMode.Additive,
|
||||
BlendMode.Multiply,
|
||||
BlendMode.Screen,
|
||||
]) {
|
||||
const paint = new ck.Paint();
|
||||
const shader = image.makeShaderOptions(
|
||||
ck.TileMode.Clamp,
|
||||
ck.TileMode.Clamp,
|
||||
ck.FilterMode.Linear,
|
||||
ck.MipmapMode.Linear
|
||||
);
|
||||
paint.setShader(shader);
|
||||
paint.setBlendMode(toCkBlendMode(ck, blendMode));
|
||||
paintPerBlendMode.set(blendMode, paint);
|
||||
shaders.push(shader);
|
||||
}
|
||||
return new CanvasKitTexture({ shaders, paintPerBlendMode, image });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a {@link TextureAtlas} and its atlas page images from the given file path using the `readFile(path: string): Promise<Buffer>` function.
|
||||
* Throws an `Error` if the file or one of the atlas page images could not be loaded.
|
||||
*/
|
||||
export async function loadTextureAtlas (
|
||||
ck: CanvasKit,
|
||||
atlasFile: string,
|
||||
readFile: (path: string) => Promise<Buffer>
|
||||
): Promise<TextureAtlas> {
|
||||
const atlas = new TextureAtlas(bufferToUtf8String(await readFile(atlasFile)));
|
||||
const slashIndex = atlasFile.lastIndexOf("/");
|
||||
const parentDir =
|
||||
slashIndex >= 0 ? atlasFile.substring(0, slashIndex + 1) + "/" : "";
|
||||
for (const page of atlas.pages) {
|
||||
const texture = await CanvasKitTexture.fromFile(
|
||||
ck,
|
||||
parentDir + page.name,
|
||||
readFile
|
||||
);
|
||||
page.setTexture(texture);
|
||||
}
|
||||
return atlas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a {@link SkeletonData} from the given file path (`.json` or `.skel`) using the `readFile(path: string): Promise<Buffer>` function.
|
||||
* Attachments will be looked up in the provided atlas.
|
||||
*/
|
||||
export async function loadSkeletonData (
|
||||
skeletonFile: string,
|
||||
atlas: TextureAtlas,
|
||||
readFile: (path: string) => Promise<Buffer>,
|
||||
scale = 1
|
||||
): Promise<SkeletonData> {
|
||||
const attachmentLoader = new AtlasAttachmentLoader(atlas);
|
||||
const loader = skeletonFile.endsWith(".json")
|
||||
? new SkeletonJson(attachmentLoader)
|
||||
: new SkeletonBinary(attachmentLoader);
|
||||
loader.scale = scale;
|
||||
let data = await readFile(skeletonFile);
|
||||
if (skeletonFile.endsWith(".json")) {
|
||||
data = bufferToUtf8String(data);
|
||||
}
|
||||
const skeletonData = loader.readSkeletonData(data);
|
||||
return skeletonData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages a {@link Skeleton} and its associated {@link AnimationState}. A drawable is constructed from a {@link SkeletonData}, which can
|
||||
* be shared by any number of drawables.
|
||||
*/
|
||||
export class SkeletonDrawable {
|
||||
public readonly skeleton: Skeleton;
|
||||
public readonly animationState: AnimationState;
|
||||
|
||||
/**
|
||||
* Constructs a new drawble from the skeleton data.
|
||||
*/
|
||||
constructor (skeletonData: SkeletonData) {
|
||||
this.skeleton = new Skeleton(skeletonData);
|
||||
this.animationState = new AnimationState(
|
||||
new AnimationStateData(skeletonData)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the animation state and skeleton time by the delta time. Applies the
|
||||
* animations to the skeleton and calculates the final pose of the skeleton.
|
||||
*
|
||||
* @param deltaTime the time since the last update in seconds
|
||||
* @param physicsUpdate optional {@link Physics} update mode.
|
||||
*/
|
||||
update (deltaTime: number, physicsUpdate: Physics = Physics.update) {
|
||||
this.animationState.update(deltaTime);
|
||||
this.skeleton.update(deltaTime);
|
||||
this.animationState.apply(this.skeleton);
|
||||
this.skeleton.updateWorldTransform(physicsUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a {@link Skeleton} or {@link SkeletonDrawable} to a CanvasKit {@link Canvas}.
|
||||
*/
|
||||
export class SkeletonRenderer {
|
||||
private clipper = new SkeletonClipping();
|
||||
private tempColor = new Color();
|
||||
private tempColor2 = new Color();
|
||||
private static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
|
||||
private scratchPositions = Utils.newFloatArray(100);
|
||||
private scratchColors = Utils.newFloatArray(100);
|
||||
private scratchUVs = Utils.newFloatArray(100);
|
||||
|
||||
/**
|
||||
* Creates a new skeleton renderer.
|
||||
* @param ck the {@link CanvasKit} instance returned by `CanvasKitInit()`.
|
||||
*/
|
||||
constructor (private ck: CanvasKit) { }
|
||||
|
||||
/**
|
||||
* Renders a skeleton or skeleton drawable in its current pose to the canvas.
|
||||
* @param canvas the canvas to render to.
|
||||
* @param skeleton the skeleton or drawable to render.
|
||||
*/
|
||||
render (canvas: Canvas, skeleton: Skeleton | SkeletonDrawable) {
|
||||
if (skeleton instanceof SkeletonDrawable) skeleton = skeleton.skeleton;
|
||||
let clipper = this.clipper;
|
||||
let drawOrder = skeleton.drawOrder;
|
||||
let skeletonColor = skeleton.color;
|
||||
|
||||
for (let i = 0, n = drawOrder.length; i < n; i++) {
|
||||
let slot = drawOrder[i];
|
||||
if (!slot.bone.active) {
|
||||
clipper.clipEndWithSlot(slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
let attachment = slot.getAttachment();
|
||||
let positions = this.scratchPositions;
|
||||
let colors = this.scratchColors;
|
||||
let uvs: NumberArrayLike;
|
||||
let texture: CanvasKitTexture;
|
||||
let triangles: Array<number>;
|
||||
let attachmentColor: Color;
|
||||
let numVertices = 0;
|
||||
if (attachment instanceof RegionAttachment) {
|
||||
let region = attachment as RegionAttachment;
|
||||
positions = positions.length < 8 ? Utils.newFloatArray(8) : positions;
|
||||
numVertices = 4;
|
||||
region.computeWorldVertices(slot, positions, 0, 2);
|
||||
triangles = SkeletonRenderer.QUAD_TRIANGLES;
|
||||
uvs = region.uvs as Float32Array;
|
||||
texture = region.region?.texture as CanvasKitTexture;
|
||||
attachmentColor = region.color;
|
||||
} else if (attachment instanceof MeshAttachment) {
|
||||
let mesh = attachment as MeshAttachment;
|
||||
positions =
|
||||
positions.length < mesh.worldVerticesLength
|
||||
? Utils.newFloatArray(mesh.worldVerticesLength)
|
||||
: positions;
|
||||
numVertices = mesh.worldVerticesLength >> 1;
|
||||
mesh.computeWorldVertices(
|
||||
slot,
|
||||
0,
|
||||
mesh.worldVerticesLength,
|
||||
positions,
|
||||
0,
|
||||
2
|
||||
);
|
||||
triangles = mesh.triangles;
|
||||
texture = mesh.region?.texture as CanvasKitTexture;
|
||||
uvs = mesh.uvs as Float32Array;
|
||||
attachmentColor = mesh.color;
|
||||
} else if (attachment instanceof ClippingAttachment) {
|
||||
let clip = attachment as ClippingAttachment;
|
||||
clipper.clipStart(slot, clip);
|
||||
continue;
|
||||
} else {
|
||||
clipper.clipEndWithSlot(slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (texture) {
|
||||
if (clipper.isClipping()) {
|
||||
clipper.clipTrianglesUnpacked(
|
||||
positions,
|
||||
triangles,
|
||||
triangles.length,
|
||||
uvs
|
||||
);
|
||||
positions = clipper.clippedVertices;
|
||||
uvs = clipper.clippedUVs;
|
||||
triangles = clipper.clippedTriangles;
|
||||
}
|
||||
|
||||
let slotColor = slot.color;
|
||||
let finalColor = this.tempColor;
|
||||
finalColor.r = skeletonColor.r * slotColor.r * attachmentColor.r;
|
||||
finalColor.g = skeletonColor.g * slotColor.g * attachmentColor.g;
|
||||
finalColor.b = skeletonColor.b * slotColor.b * attachmentColor.b;
|
||||
finalColor.a = skeletonColor.a * slotColor.a * attachmentColor.a;
|
||||
|
||||
if (colors.length / 4 < numVertices)
|
||||
colors = Utils.newFloatArray(numVertices * 4);
|
||||
for (let i = 0, n = numVertices * 4; i < n; i += 4) {
|
||||
colors[i] = finalColor.r;
|
||||
colors[i + 1] = finalColor.g;
|
||||
colors[i + 2] = finalColor.b;
|
||||
colors[i + 3] = finalColor.a;
|
||||
}
|
||||
|
||||
const scaledUvs =
|
||||
this.scratchUVs.length < uvs.length
|
||||
? Utils.newFloatArray(uvs.length)
|
||||
: this.scratchUVs;
|
||||
const width = texture.getImage().image.width();
|
||||
const height = texture.getImage().image.height();
|
||||
for (let i = 0; i < uvs.length; i += 2) {
|
||||
scaledUvs[i] = uvs[i] * width;
|
||||
scaledUvs[i + 1] = uvs[i + 1] * height;
|
||||
}
|
||||
|
||||
const blendMode = slot.data.blendMode;
|
||||
const vertices = this.ck.MakeVertices(
|
||||
this.ck.VertexMode.Triangles,
|
||||
positions,
|
||||
scaledUvs,
|
||||
colors,
|
||||
triangles,
|
||||
false
|
||||
);
|
||||
canvas.drawVertices(
|
||||
vertices,
|
||||
this.ck.BlendMode.Modulate,
|
||||
texture.getImage().paintPerBlendMode.get(blendMode)!
|
||||
);
|
||||
vertices.delete();
|
||||
}
|
||||
|
||||
clipper.clipEndWithSlot(slot);
|
||||
}
|
||||
clipper.clipEnd();
|
||||
}
|
||||
}
|
||||
18
spine-ts/spine-canvaskit/tsconfig.json
Normal file
18
spine-ts/spine-canvaskit/tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@esotericsoftware/spine-core": ["../spine-core/src"]
|
||||
}
|
||||
},
|
||||
"include": ["**/*.ts"],
|
||||
"exclude": ["dist/**/*.d.ts"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../spine-core"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@esotericsoftware/spine-core",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"description": "The official Spine Runtimes for the web.",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@ -64,7 +64,7 @@ export class SkeletonBinary {
|
||||
this.attachmentLoader = attachmentLoader;
|
||||
}
|
||||
|
||||
readSkeletonData (binary: Uint8Array): SkeletonData {
|
||||
readSkeletonData (binary: Uint8Array | ArrayBuffer): SkeletonData {
|
||||
let scale = this.scale;
|
||||
|
||||
let skeletonData = new SkeletonData();
|
||||
@ -1115,7 +1115,7 @@ export class SkeletonBinary {
|
||||
}
|
||||
|
||||
export class BinaryInput {
|
||||
constructor (data: Uint8Array, public strings = new Array<string>(), private index: number = 0, private buffer = new DataView(data.buffer)) {
|
||||
constructor (data: Uint8Array | ArrayBuffer, public strings = new Array<string>(), private index: number = 0, private buffer = new DataView(data instanceof ArrayBuffer ? data : data.buffer)) {
|
||||
}
|
||||
|
||||
readByte (): number {
|
||||
|
||||
@ -37,6 +37,7 @@ export class SkeletonClipping {
|
||||
private clippingPolygon = new Array<number>();
|
||||
private clipOutput = new Array<number>();
|
||||
clippedVertices = new Array<number>();
|
||||
clippedUVs = new Array<number>();
|
||||
clippedTriangles = new Array<number>();
|
||||
private scratch = new Array<number>();
|
||||
|
||||
@ -303,6 +304,92 @@ export class SkeletonClipping {
|
||||
}
|
||||
}
|
||||
|
||||
public clipTrianglesUnpacked (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number, uvs: NumberArrayLike) {
|
||||
let clipOutput = this.clipOutput, clippedVertices = this.clippedVertices, clippedUVs = this.clippedUVs;
|
||||
let clippedTriangles = this.clippedTriangles;
|
||||
let polygons = this.clippingPolygons!;
|
||||
let polygonsCount = polygons.length;
|
||||
|
||||
let index = 0;
|
||||
clippedVertices.length = 0;
|
||||
clippedUVs.length = 0;
|
||||
clippedTriangles.length = 0;
|
||||
for (let i = 0; i < trianglesLength; i += 3) {
|
||||
let vertexOffset = triangles[i] << 1;
|
||||
let x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1];
|
||||
let u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1];
|
||||
|
||||
vertexOffset = triangles[i + 1] << 1;
|
||||
let x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1];
|
||||
let u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1];
|
||||
|
||||
vertexOffset = triangles[i + 2] << 1;
|
||||
let x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1];
|
||||
let u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1];
|
||||
|
||||
for (let p = 0; p < polygonsCount; p++) {
|
||||
let s = clippedVertices.length;
|
||||
if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) {
|
||||
let clipOutputLength = clipOutput.length;
|
||||
if (clipOutputLength == 0) continue;
|
||||
let d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1;
|
||||
let d = 1 / (d0 * d2 + d1 * (y1 - y3));
|
||||
|
||||
let clipOutputCount = clipOutputLength >> 1;
|
||||
let clipOutputItems = this.clipOutput;
|
||||
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * 2);
|
||||
let clippedUVsItems = Utils.setArraySize(clippedUVs, s + clipOutputCount * 2);
|
||||
for (let ii = 0; ii < clipOutputLength; ii += 2, s += 2) {
|
||||
let x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
|
||||
clippedVerticesItems[s] = x;
|
||||
clippedVerticesItems[s + 1] = y;
|
||||
let c0 = x - x3, c1 = y - y3;
|
||||
let a = (d0 * c0 + d1 * c1) * d;
|
||||
let b = (d4 * c0 + d2 * c1) * d;
|
||||
let c = 1 - a - b;
|
||||
clippedUVsItems[s] = u1 * a + u2 * b + u3 * c;
|
||||
clippedUVsItems[s + 1] = v1 * a + v2 * b + v3 * c;
|
||||
}
|
||||
|
||||
s = clippedTriangles.length;
|
||||
let clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3 * (clipOutputCount - 2));
|
||||
clipOutputCount--;
|
||||
for (let ii = 1; ii < clipOutputCount; ii++, s += 3) {
|
||||
clippedTrianglesItems[s] = index;
|
||||
clippedTrianglesItems[s + 1] = (index + ii);
|
||||
clippedTrianglesItems[s + 2] = (index + ii + 1);
|
||||
}
|
||||
index += clipOutputCount + 1;
|
||||
|
||||
} else {
|
||||
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + 3 * 2);
|
||||
clippedVerticesItems[s] = x1;
|
||||
clippedVerticesItems[s + 1] = y1;
|
||||
clippedVerticesItems[s + 2] = x2;
|
||||
clippedVerticesItems[s + 3] = y2;
|
||||
clippedVerticesItems[s + 4] = x3;
|
||||
clippedVerticesItems[s + 5] = y3;
|
||||
|
||||
let clippedUVSItems = Utils.setArraySize(clippedUVs, s + 3 * 2);
|
||||
clippedUVSItems[s] = u1;
|
||||
clippedUVSItems[s + 1] = v1;
|
||||
clippedUVSItems[s + 2] = u2;
|
||||
clippedUVSItems[s + 3] = v2;
|
||||
clippedUVSItems[s + 4] = u3;
|
||||
clippedUVSItems[s + 5] = v3;
|
||||
|
||||
s = clippedTriangles.length;
|
||||
let clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3);
|
||||
clippedTrianglesItems[s] = index;
|
||||
clippedTrianglesItems[s + 1] = (index + 1);
|
||||
clippedTrianglesItems[s + 2] = (index + 2);
|
||||
index += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping
|
||||
* area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */
|
||||
clip (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, clippingArea: Array<number>, output: Array<number>) {
|
||||
|
||||
@ -28,13 +28,13 @@
|
||||
*****************************************************************************/
|
||||
|
||||
export abstract class Texture {
|
||||
protected _image: HTMLImageElement | ImageBitmap;
|
||||
protected _image: HTMLImageElement | ImageBitmap | any;
|
||||
|
||||
constructor (image: HTMLImageElement | ImageBitmap) {
|
||||
constructor (image: HTMLImageElement | ImageBitmap | any) {
|
||||
this._image = image;
|
||||
}
|
||||
|
||||
getImage (): HTMLImageElement | ImageBitmap {
|
||||
getImage (): HTMLImageElement | ImageBitmap | any {
|
||||
return this._image;
|
||||
}
|
||||
|
||||
|
||||
@ -87,10 +87,8 @@ export class StringSet {
|
||||
}
|
||||
}
|
||||
|
||||
export interface NumberArrayLike {
|
||||
readonly length: number;
|
||||
[n: number]: number;
|
||||
}
|
||||
export type NumberArrayLike = Array<number> | Float32Array;
|
||||
export type IntArrayLike = Array<number> | Int16Array;
|
||||
|
||||
export interface Disposable {
|
||||
dispose (): void;
|
||||
@ -313,7 +311,7 @@ export class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
static newShortArray (size: number): NumberArrayLike {
|
||||
static newShortArray (size: number): IntArrayLike {
|
||||
if (Utils.SUPPORTS_TYPED_ARRAYS)
|
||||
return new Int16Array(size)
|
||||
else {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@esotericsoftware/spine-phaser",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"description": "The official Spine Runtimes for the Phaser.",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
@ -31,8 +31,8 @@
|
||||
},
|
||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-core": "4.2.48",
|
||||
"@esotericsoftware/spine-webgl": "4.2.48",
|
||||
"@esotericsoftware/spine-canvas": "4.2.48"
|
||||
"@esotericsoftware/spine-core": "4.2.56",
|
||||
"@esotericsoftware/spine-webgl": "4.2.56",
|
||||
"@esotericsoftware/spine-canvas": "4.2.56"
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@esotericsoftware/spine-pixi",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"description": "The official Spine Runtimes for the web.",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
@ -31,7 +31,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-core": "4.2.48"
|
||||
"@esotericsoftware/spine-core": "4.2.56"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@pixi/core": "^7.2.4",
|
||||
|
||||
@ -54,26 +54,30 @@ export class DarkSlotMesh extends DarkTintMesh implements ISlotMesh {
|
||||
|
||||
const vertLenght = (finalVerticesLength / (darkTint ? 12 : 8)) * 2;
|
||||
|
||||
if (this.geometry.getBuffer("aTextureCoord").data?.length !== vertLenght) {
|
||||
this.geometry.getBuffer("aTextureCoord").data = new Float32Array(vertLenght);
|
||||
const textureCoord = this.geometry.getBuffer("aTextureCoord");
|
||||
if (textureCoord.data?.length !== vertLenght) {
|
||||
textureCoord.data = new Float32Array(vertLenght);
|
||||
}
|
||||
|
||||
if (this.geometry.getBuffer("aVertexPosition").data?.length !== vertLenght) {
|
||||
this.geometry.getBuffer("aVertexPosition").data = new Float32Array(vertLenght);
|
||||
const vertexCoord = this.geometry.getBuffer("aVertexPosition");
|
||||
if (vertexCoord.data?.length !== vertLenght) {
|
||||
vertexCoord.data = new Float32Array(vertLenght);
|
||||
}
|
||||
|
||||
let vertIndex = 0;
|
||||
|
||||
let textureCoordData = textureCoord.data;
|
||||
let vertexCoordData = vertexCoord.data;
|
||||
for (let i = 0; i < finalVerticesLength; i += darkTint ? 12 : 8) {
|
||||
let auxi = i;
|
||||
|
||||
this.geometry.getBuffer("aVertexPosition").data[vertIndex] = finalVertices[auxi++];
|
||||
this.geometry.getBuffer("aVertexPosition").data[vertIndex + 1] = finalVertices[auxi++];
|
||||
vertexCoordData[vertIndex] = finalVertices[auxi++];
|
||||
vertexCoordData[vertIndex + 1] = finalVertices[auxi++];
|
||||
|
||||
auxi += 4; // color
|
||||
|
||||
this.geometry.getBuffer("aTextureCoord").data[vertIndex] = finalVertices[auxi++];
|
||||
this.geometry.getBuffer("aTextureCoord").data[vertIndex + 1] = finalVertices[auxi++];
|
||||
textureCoordData[vertIndex] = finalVertices[auxi++];
|
||||
textureCoordData[vertIndex + 1] = finalVertices[auxi++];
|
||||
|
||||
vertIndex += 2;
|
||||
}
|
||||
@ -101,18 +105,20 @@ export class DarkSlotMesh extends DarkTintMesh implements ISlotMesh {
|
||||
this.blendMode = SpineTexture.toPixiBlending(slotBlendMode);
|
||||
this.alpha = DarkSlotMesh.auxColor[3];
|
||||
|
||||
if (this.geometry.indexBuffer.data.length !== finalIndices.length) {
|
||||
this.geometry.indexBuffer.data = new Uint32Array(finalIndices);
|
||||
const indexBuffer = this.geometry.indexBuffer;
|
||||
if (indexBuffer.data.length !== finalIndices.length) {
|
||||
indexBuffer.data = new Uint32Array(finalIndices);
|
||||
} else {
|
||||
const indexBufferData = indexBuffer.data;
|
||||
for (let i = 0; i < finalIndicesLength; i++) {
|
||||
this.geometry.indexBuffer.data[i] = finalIndices[i];
|
||||
indexBufferData[i] = finalIndices[i];
|
||||
}
|
||||
}
|
||||
|
||||
this.name = slotName;
|
||||
|
||||
this.geometry.getBuffer("aVertexPosition").update();
|
||||
this.geometry.getBuffer("aTextureCoord").update();
|
||||
this.geometry.indexBuffer.update();
|
||||
textureCoord.update();
|
||||
vertexCoord.update();
|
||||
indexBuffer.update();
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,32 +62,34 @@ export class SlotMesh extends Mesh implements ISlotMesh {
|
||||
|
||||
const vertLenght = (finalVerticesLength / (darkTint ? 12 : 8)) * 2;
|
||||
|
||||
if (this.geometry.getBuffer("aTextureCoord").data?.length !== vertLenght) {
|
||||
this.geometry.getBuffer("aTextureCoord").data = new Float32Array(vertLenght);
|
||||
const textureCoord = this.geometry.getBuffer("aTextureCoord");
|
||||
if (textureCoord.data?.length !== vertLenght) {
|
||||
textureCoord.data = new Float32Array(vertLenght);
|
||||
}
|
||||
|
||||
if (this.geometry.getBuffer("aVertexPosition").data?.length !== vertLenght) {
|
||||
this.geometry.getBuffer("aVertexPosition").data = new Float32Array(vertLenght);
|
||||
const vertexCoord = this.geometry.getBuffer("aVertexPosition");
|
||||
if (vertexCoord.data?.length !== vertLenght) {
|
||||
vertexCoord.data = new Float32Array(vertLenght);
|
||||
}
|
||||
|
||||
let vertIndex = 0;
|
||||
|
||||
let textureCoordData = textureCoord.data;
|
||||
let vertexCoordData = vertexCoord.data;
|
||||
for (let i = 0; i < finalVerticesLength; i += darkTint ? 12 : 8) {
|
||||
let auxi = i;
|
||||
|
||||
this.geometry.getBuffer("aVertexPosition").data[vertIndex] = finalVertices[auxi++];
|
||||
this.geometry.getBuffer("aVertexPosition").data[vertIndex + 1] = finalVertices[auxi++];
|
||||
vertexCoordData[vertIndex] = finalVertices[auxi++];
|
||||
vertexCoordData[vertIndex + 1] = finalVertices[auxi++];
|
||||
|
||||
auxi += 4; // color
|
||||
|
||||
this.geometry.getBuffer("aTextureCoord").data[vertIndex] = finalVertices[auxi++];
|
||||
this.geometry.getBuffer("aTextureCoord").data[vertIndex + 1] = finalVertices[auxi++];
|
||||
textureCoordData[vertIndex] = finalVertices[auxi++];
|
||||
textureCoordData[vertIndex + 1] = finalVertices[auxi++];
|
||||
|
||||
vertIndex += 2;
|
||||
}
|
||||
|
||||
// console.log(vertLenght, auxVert.length);
|
||||
|
||||
if (darkTint && !this.warnedTwoTint) {
|
||||
console.warn("DarkTint is not enabled by default. To enable use a DarkSlotMesh factory while creating the Spine object.");
|
||||
this.warnedTwoTint = true;
|
||||
@ -102,18 +104,20 @@ export class SlotMesh extends Mesh implements ISlotMesh {
|
||||
this.alpha = SlotMesh.auxColor[3];
|
||||
this.blendMode = SpineTexture.toPixiBlending(slotBlendMode);
|
||||
|
||||
if (this.geometry.indexBuffer.data.length !== finalIndices.length) {
|
||||
this.geometry.indexBuffer.data = new Uint32Array(finalIndices);
|
||||
const indexBuffer = this.geometry.indexBuffer;
|
||||
if (indexBuffer.data.length !== finalIndices.length) {
|
||||
indexBuffer.data = new Uint32Array(finalIndices);
|
||||
} else {
|
||||
const indexBufferData = indexBuffer.data;
|
||||
for (let i = 0; i < finalIndicesLength; i++) {
|
||||
this.geometry.indexBuffer.data[i] = finalIndices[i];
|
||||
indexBufferData[i] = finalIndices[i];
|
||||
}
|
||||
}
|
||||
|
||||
this.name = slotName;
|
||||
|
||||
this.geometry.getBuffer("aVertexPosition").update();
|
||||
this.geometry.getBuffer("aTextureCoord").update();
|
||||
this.geometry.indexBuffer.update();
|
||||
textureCoord.update();
|
||||
vertexCoord.update();
|
||||
indexBuffer.update();
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,6 +199,7 @@ export class Spine extends Container {
|
||||
|
||||
/** Destroy Spine game object elements, then call the {@link Container.destroy} with the given options */
|
||||
public override destroy (options?: boolean | IDestroyOptions | undefined): void {
|
||||
if (this.autoUpdate) this.autoUpdate = false;
|
||||
for (const [, mesh] of this.meshesCache) {
|
||||
mesh?.destroy();
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@esotericsoftware/spine-player",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"description": "The official Spine Runtimes for the web.",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
@ -31,6 +31,6 @@
|
||||
},
|
||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-webgl": "4.2.48"
|
||||
"@esotericsoftware/spine-webgl": "4.2.56"
|
||||
}
|
||||
}
|
||||
@ -212,7 +212,7 @@ export class SpinePlayer implements Disposable {
|
||||
|
||||
private playTime = 0;
|
||||
private selectedBones: (Bone | null)[] = [];
|
||||
private cancelId = 0;
|
||||
private cancelId: any = 0;
|
||||
popup: Popup | null = null;
|
||||
|
||||
/* True if the player is unable to load or render the skeleton. */
|
||||
|
||||
@ -128,7 +128,7 @@ body { margin: 0px; }
|
||||
this.startPlayer();
|
||||
}
|
||||
|
||||
private timerId = 0;
|
||||
private timerId: any = 0;
|
||||
startPlayer () {
|
||||
clearTimeout(this.timerId);
|
||||
this.timerId = setTimeout(() => {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@esotericsoftware/spine-threejs",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"description": "The official Spine Runtimes for the web.",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
@ -31,6 +31,6 @@
|
||||
},
|
||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-core": "4.2.48"
|
||||
"@esotericsoftware/spine-core": "4.2.56"
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@esotericsoftware/spine-webgl",
|
||||
"version": "4.2.48",
|
||||
"version": "4.2.56",
|
||||
"description": "The official Spine Runtimes for the web.",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
@ -31,6 +31,6 @@
|
||||
},
|
||||
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
|
||||
"dependencies": {
|
||||
"@esotericsoftware/spine-core": "4.2.48"
|
||||
"@esotericsoftware/spine-core": "4.2.56"
|
||||
}
|
||||
}
|
||||
@ -1,26 +1,29 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./spine-core"
|
||||
},
|
||||
{
|
||||
"path": "./spine-canvas"
|
||||
},
|
||||
{
|
||||
"path": "./spine-webgl"
|
||||
},
|
||||
{
|
||||
"path": "./spine-phaser"
|
||||
},
|
||||
{
|
||||
"path": "./spine-player"
|
||||
},
|
||||
{
|
||||
"path": "./spine-threejs"
|
||||
},
|
||||
{
|
||||
"path": "./spine-pixi"
|
||||
}
|
||||
]
|
||||
}
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./spine-core"
|
||||
},
|
||||
{
|
||||
"path": "./spine-canvas"
|
||||
},
|
||||
{
|
||||
"path": "./spine-canvaskit"
|
||||
},
|
||||
{
|
||||
"path": "./spine-webgl"
|
||||
},
|
||||
{
|
||||
"path": "./spine-phaser"
|
||||
},
|
||||
{
|
||||
"path": "./spine-player"
|
||||
},
|
||||
{
|
||||
"path": "./spine-threejs"
|
||||
},
|
||||
{
|
||||
"path": "./spine-pixi"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ void USpineAtlasAsset::BeginDestroy() {
|
||||
|
||||
class UETextureLoader : public TextureLoader {
|
||||
void load(AtlasPage &page, const String &path) {
|
||||
page.texture = (void*)(uintptr_t)page.index;
|
||||
page.texture = (void *) (uintptr_t) page.index;
|
||||
}
|
||||
|
||||
void unload(void *texture) {
|
||||
|
||||
@ -268,7 +268,7 @@ void USpineSkeletonRendererComponent::UpdateMesh(USpineSkeletonComponent *compon
|
||||
// to the correct skeleton data yet, we won't find any regions.
|
||||
// ignore regions for which we can't find a material
|
||||
UMaterialInstanceDynamic *material = nullptr;
|
||||
int foundPageIndex = (int)(intptr_t)attachmentAtlasRegion->rendererObject;
|
||||
int foundPageIndex = (int) (intptr_t) attachmentAtlasRegion->rendererObject;
|
||||
if (foundPageIndex == -1) {
|
||||
clipper.clipEnd(*slot);
|
||||
continue;
|
||||
|
||||
@ -152,9 +152,24 @@ namespace Spine.Unity.Editor {
|
||||
Texture2D image = Icon;
|
||||
GUIStyle usedStyle = IsValueValid(property) ? EditorStyles.popup : ErrorPopupStyle;
|
||||
string propertyStringValue = (property.hasMultipleDifferentValues) ? SpineInspectorUtility.EmDash : property.stringValue;
|
||||
if (GUI.Button(position, string.IsNullOrEmpty(propertyStringValue) ? NoneLabel(image) :
|
||||
SpineInspectorUtility.TempContent(propertyStringValue, image), usedStyle))
|
||||
Selector(property);
|
||||
|
||||
if (!TargetAttribute.avoidGenericMenu) {
|
||||
if (GUI.Button(position, string.IsNullOrEmpty(propertyStringValue) ? NoneLabel(image) :
|
||||
SpineInspectorUtility.TempContent(propertyStringValue, image), usedStyle))
|
||||
Selector(property);
|
||||
} else {
|
||||
SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(false);
|
||||
List<GUIContent> contentList = new List<GUIContent>();
|
||||
List<string> valueList = new List<string>();
|
||||
PopulatePopupList(ref contentList, ref valueList, image, property, TargetAttribute, skeletonData);
|
||||
int currentIndex = valueList.IndexOf(propertyStringValue);
|
||||
int previousIndex = currentIndex;
|
||||
currentIndex = EditorGUI.Popup(position, currentIndex, contentList.ToArray());
|
||||
if (previousIndex != currentIndex) {
|
||||
property.stringValue = valueList[currentIndex];
|
||||
property.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ISkeletonComponent GetTargetSkeletonComponent (SerializedProperty property) {
|
||||
@ -192,6 +207,12 @@ namespace Spine.Unity.Editor {
|
||||
serializedProperty.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
protected virtual void PopulatePopupList (ref List<GUIContent> contentList, ref List<string> valueList,
|
||||
Texture2D image, SerializedProperty property, T targetAttribute, SkeletonData data) {
|
||||
contentList.Add(new GUIContent("Type Not Supported"));
|
||||
valueList.Add(string.Empty);
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight (SerializedProperty property, GUIContent label) {
|
||||
return 18;
|
||||
}
|
||||
@ -302,6 +323,25 @@ namespace Spine.Unity.Editor {
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PopulatePopupList (ref List<GUIContent> contentList, ref List<string> valueList,
|
||||
Texture2D image, SerializedProperty property, SpineSkin targetAttribute, SkeletonData data) {
|
||||
|
||||
if (targetAttribute.includeNone) {
|
||||
contentList.Add(new GUIContent(NoneStringConstant, image));
|
||||
valueList.Add(string.Empty);
|
||||
}
|
||||
|
||||
for (int i = 0; i < data.Skins.Count; i++) {
|
||||
string name = data.Skins.Items[i].Name;
|
||||
if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) {
|
||||
bool isDefault = string.Equals(name, DefaultSkinName, StringComparison.Ordinal);
|
||||
string choiceValue = TargetAttribute.defaultAsEmptyString && isDefault ? string.Empty : name;
|
||||
contentList.Add(new GUIContent(name, image));
|
||||
valueList.Add(choiceValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(SpineAnimation))]
|
||||
@ -348,6 +388,24 @@ namespace Spine.Unity.Editor {
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PopulatePopupList (ref List<GUIContent> contentList, ref List<string> valueList,
|
||||
Texture2D image, SerializedProperty property, SpineAnimation targetAttribute, SkeletonData data) {
|
||||
|
||||
ExposedList<Animation> animations = data.Animations;
|
||||
if (targetAttribute.includeNone) {
|
||||
contentList.Add(new GUIContent(NoneString, image));
|
||||
valueList.Add(string.Empty);
|
||||
}
|
||||
|
||||
for (int i = 0; i < animations.Count; i++) {
|
||||
string name = animations.Items[i].Name;
|
||||
if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) {
|
||||
contentList.Add(new GUIContent(name, image));
|
||||
valueList.Add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(SpineEvent))]
|
||||
|
||||
@ -128,10 +128,10 @@ namespace Spine.Unity.Editor {
|
||||
Mesh mesh = meshFilter.sharedMesh;
|
||||
if (mesh == null) continue;
|
||||
|
||||
string meshName = string.Format("Skeleton Prefab Mesh \"{0}\"", renderer.name);
|
||||
string meshName = string.Format("Skeleton Prefab Mesh [{0}]", renderer.name);
|
||||
if (nameUsageCount.ContainsKey(meshName)) {
|
||||
nameUsageCount[meshName]++;
|
||||
meshName = string.Format("Skeleton Prefab Mesh \"{0} ({1})\"", renderer.name, nameUsageCount[meshName]);
|
||||
meshName = string.Format("Skeleton Prefab Mesh [{0} ({1})]", renderer.name, nameUsageCount[meshName]);
|
||||
} else {
|
||||
nameUsageCount.Add(meshName, 0);
|
||||
}
|
||||
|
||||
@ -89,6 +89,7 @@ namespace Spine.Unity {
|
||||
public delegate void TextureLoadDelegate (OnDemandTextureLoader loader, Material material, int textureIndex);
|
||||
protected event TextureLoadDelegate onTextureRequested;
|
||||
protected event TextureLoadDelegate onTextureLoaded;
|
||||
protected event TextureLoadDelegate onTextureLoadFailed;
|
||||
protected event TextureLoadDelegate onTextureUnloaded;
|
||||
|
||||
public event TextureLoadDelegate TextureRequested {
|
||||
@ -99,6 +100,10 @@ namespace Spine.Unity {
|
||||
add { onTextureLoaded += value; }
|
||||
remove { onTextureLoaded -= value; }
|
||||
}
|
||||
public event TextureLoadDelegate TextureLoadFailed {
|
||||
add { onTextureLoadFailed += value; }
|
||||
remove { onTextureLoadFailed -= value; }
|
||||
}
|
||||
public event TextureLoadDelegate TextureUnloaded {
|
||||
add { onTextureUnloaded += value; }
|
||||
remove { onTextureUnloaded -= value; }
|
||||
@ -112,6 +117,10 @@ namespace Spine.Unity {
|
||||
if (onTextureLoaded != null)
|
||||
onTextureLoaded(this, material, textureIndex);
|
||||
}
|
||||
protected void OnTextureLoadFailed (Material material, int textureIndex) {
|
||||
if (onTextureLoadFailed != null)
|
||||
onTextureLoadFailed(this, material, textureIndex);
|
||||
}
|
||||
protected void OnTextureUnloaded (Material material, int textureIndex) {
|
||||
if (onTextureUnloaded != null)
|
||||
onTextureUnloaded(this, material, textureIndex);
|
||||
|
||||
@ -33,12 +33,13 @@ using UnityEngine;
|
||||
|
||||
namespace Spine.Unity {
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
|
||||
public abstract class SpineAttributeBase : PropertyAttribute {
|
||||
public string dataField = "";
|
||||
public string startsWith = "";
|
||||
public bool includeNone = true;
|
||||
public bool fallbackToTextField = false;
|
||||
public bool avoidGenericMenu = false;
|
||||
}
|
||||
|
||||
public class SpineBone : SpineAttributeBase {
|
||||
@ -103,11 +104,14 @@ namespace Spine.Unity {
|
||||
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
|
||||
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
|
||||
/// </param>
|
||||
public SpineAnimation (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) {
|
||||
public SpineAnimation (string startsWith = "", string dataField = "",
|
||||
bool includeNone = true, bool fallbackToTextField = false, bool avoidGenericMenu = false) {
|
||||
|
||||
this.startsWith = startsWith;
|
||||
this.dataField = dataField;
|
||||
this.includeNone = includeNone;
|
||||
this.fallbackToTextField = fallbackToTextField;
|
||||
this.avoidGenericMenu = avoidGenericMenu;
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,12 +209,15 @@ namespace Spine.Unity {
|
||||
|
||||
public bool defaultAsEmptyString = false;
|
||||
|
||||
public SpineSkin (string startsWith = "", string dataField = "", bool includeNone = false, bool fallbackToTextField = false, bool defaultAsEmptyString = false) {
|
||||
public SpineSkin (string startsWith = "", string dataField = "", bool includeNone = false,
|
||||
bool fallbackToTextField = false, bool defaultAsEmptyString = false, bool avoidGenericMenu = false) {
|
||||
|
||||
this.startsWith = startsWith;
|
||||
this.dataField = dataField;
|
||||
this.includeNone = includeNone;
|
||||
this.fallbackToTextField = fallbackToTextField;
|
||||
this.defaultAsEmptyString = defaultAsEmptyString;
|
||||
this.avoidGenericMenu = avoidGenericMenu;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"name": "com.esotericsoftware.spine.spine-unity",
|
||||
"displayName": "spine-unity Runtime",
|
||||
"description": "This plugin provides the spine-unity runtime core.",
|
||||
"version": "4.2.74",
|
||||
"version": "4.2.76",
|
||||
"unity": "2018.3",
|
||||
"author": {
|
||||
"name": "Esoteric Software",
|
||||
|
||||
@ -87,6 +87,8 @@ namespace Spine.Unity {
|
||||
materialToUpdate.mainTexture = loadedTexture;
|
||||
OnTextureLoaded(materialToUpdate, textureIndex);
|
||||
if (onTextureLoaded != null) onTextureLoaded(loadedTexture);
|
||||
} else {
|
||||
OnTextureLoadFailed(materialToUpdate, textureIndex);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"name": "com.esotericsoftware.spine.addressables",
|
||||
"displayName": "Spine Addressables Extensions [Experimental]",
|
||||
"description": "This experimental plugin provides integration of Addressables on-demand texture loading for the spine-unity runtime.\nPlease be sure to test this package first and create backups of your project before using.\n\nUsage: First declare your target Material textures as addressable. Then select the SpineAtlasAsset, right-click the SpineAtlasAsset Inspector heading and select 'Add Addressables Loader'. This generates an 'AddressableTextureLoader' asset providing configuration parameters and sets up low-resolution placeholder textures which are automatically assigned in a pre-build step when building your game executable.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime (via the spine-unity unitypackage), version 4.2.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)",
|
||||
"version": "4.2.0-preview.3",
|
||||
"version": "4.2.0-preview.4",
|
||||
"unity": "2018.3",
|
||||
"author": {
|
||||
"name": "Esoteric Software",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "com.esotericsoftware.spine.ui-toolkit",
|
||||
"displayName": "Spine UI Toolkit [Experimental]",
|
||||
"description": "This plugin provides UI Toolkit integration for the spine-unity runtime.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime, version 4.2.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)",
|
||||
"description": "This plugin provides UI Toolkit integration for the spine-unity runtime.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime, version 4.2.75 or newer.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)",
|
||||
"version": "4.2.0-preview.1",
|
||||
"unity": "2023.2",
|
||||
"author": {
|
||||
@ -11,7 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"com.unity.modules.uielements": "1.0.0",
|
||||
"com.esotericsoftware.spine.spine-unity": "4.2.21"
|
||||
"com.esotericsoftware.spine.spine-unity": "4.2.75"
|
||||
},
|
||||
"keywords": [
|
||||
"spine",
|
||||
|
||||
@ -109,7 +109,8 @@ half4 CombinedShapeLightFragment(VertexOutputSpriteURP2D input) : SV_Target
|
||||
#endif
|
||||
|
||||
half4 mask = SAMPLE_TEXTURE2D(_MaskTex, sampler_MaskTex, input.texcoord.xy);
|
||||
main.rgb = main.a == 0 ? main.rgb : main.rgb / main.a; // un-premultiply for additive lights in CombinedShapeLightShared, reapply afterwards
|
||||
// un-premultiply for additive lights in CombinedShapeLightShared, reapply afterwards
|
||||
main.rgb = main.a < 0.001 ? main.rgb : main.rgb / main.a; // < epsilon prevents imprecision issues on some HW.
|
||||
#if UNITY_VERSION < 202120
|
||||
half4 pixel = half4(CombinedShapeLightShared(half4(main.rgb, 1), mask, input.lightingUV).rgb * main.a, main.a);
|
||||
#else
|
||||
|
||||
@ -139,7 +139,7 @@ Shader "Universal Render Pipeline/2D/Spine/Skeleton Lit" {
|
||||
return main;
|
||||
#endif
|
||||
// un-premultiply for additive lights in CombinedShapeLightShared, reapply afterwards
|
||||
main.rgb = main.a == 0 ? main.rgb : main.rgb / main.a;
|
||||
main.rgb = main.a < 0.001 ? main.rgb : main.rgb / main.a;
|
||||
#else
|
||||
#if !defined(_LIGHT_AFFECTS_ADDITIVE)
|
||||
if (i.color.a == 0) {
|
||||
@ -149,10 +149,10 @@ Shader "Universal Render Pipeline/2D/Spine/Skeleton Lit" {
|
||||
|
||||
#if !defined(_STRAIGHT_ALPHA_INPUT)
|
||||
// un-premultiply for additive lights in CombinedShapeLightShared, reapply afterwards
|
||||
tex.rgb = tex.a == 0 ? tex.rgb : tex.rgb / tex.a;
|
||||
tex.rgb = tex.a < 0.001 ? tex.rgb : tex.rgb / tex.a; // < epsilon prevents imprecision issues on some HW.
|
||||
#endif
|
||||
half4 main = tex * i.color;
|
||||
#endif
|
||||
#endif
|
||||
half4 mask = SAMPLE_TEXTURE2D(_MaskTex, sampler_MaskTex, i.uv);
|
||||
#if UNITY_VERSION < 202120
|
||||
return half4(CombinedShapeLightShared(half4(main.rgb, 1), mask, i.lightingUV).rgb * main.a, main.a);
|
||||
|
||||
@ -49,6 +49,9 @@ VertexOutput vert(appdata v) {
|
||||
|
||||
half4 frag(VertexOutput i) : SV_Target{
|
||||
float4 texColor = tex2D(_MainTex, i.uv0);
|
||||
#if defined(_ZWRITE)
|
||||
clip(texColor.a * i.color.a - _Cutoff);
|
||||
#endif
|
||||
#if defined(_TINT_BLACK_ON)
|
||||
return fragTintedColor(texColor, i.darkColor, i.color, _Color.a, _Black.a);
|
||||
#else
|
||||
|
||||
@ -51,6 +51,7 @@ Shader "Universal Render Pipeline/Spine/Skeleton" {
|
||||
// Spine related keywords
|
||||
#pragma shader_feature _ _STRAIGHT_ALPHA_INPUT
|
||||
#pragma shader_feature _TINT_BLACK_ON
|
||||
#pragma shader_feature _ZWRITE
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"name": "com.esotericsoftware.spine.urp-shaders",
|
||||
"displayName": "Spine Universal RP Shaders",
|
||||
"description": "This plugin provides universal render pipeline (URP) shaders for the spine-unity runtime.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime, version 4.2.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)",
|
||||
"version": "4.2.33",
|
||||
"version": "4.2.35",
|
||||
"unity": "2019.3",
|
||||
"author": {
|
||||
"name": "Esoteric Software",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user