Merge branch '3.7-beta' into 3.7-beta-cpp

This commit is contained in:
badlogic 2018-12-17 15:32:59 +01:00
commit 2e29006764
32 changed files with 74 additions and 699 deletions

View File

@ -189,7 +189,7 @@ package spine {
var pathLength : Number = lengths[curveCount];
if (percentPosition) position *= pathLength;
if (percentSpacing) {
for (var i : int = 0; i < spacesCount; i++)
for (var i : int = 1; i < spacesCount; i++)
spaces[i] *= pathLength;
}
this._world.length = 8;
@ -305,7 +305,7 @@ package spine {
else
position *= pathLength / path.lengths[curveCount - 1];
if (percentSpacing) {
for (i = 0; i < spacesCount; i++)
for (i = 1; i < spacesCount; i++)
spaces[i] *= pathLength;
}

View File

@ -267,7 +267,7 @@ float* spPathConstraint_computeWorldPositions(spPathConstraint* self, spPathAtta
pathLength = lengths[curveCount];
if (percentPosition) position *= pathLength;
if (percentSpacing) {
for (i = 0; i < spacesCount; i++)
for (i = 1; i < spacesCount; i++)
spaces[i] *= pathLength;
}
if (self->worldCount != 8) {
@ -397,7 +397,7 @@ float* spPathConstraint_computeWorldPositions(spPathConstraint* self, spPathAtta
else
position *= pathLength / path->lengths[curveCount - 1];
if (percentSpacing) {
for (i = 0; i < spacesCount; i++)
for (i = 1; i < spacesCount; i++)
spaces[i] *= pathLength;
}

View File

@ -288,7 +288,7 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
}
if (percentSpacing) {
for (int i = 0; i < spacesCount; ++i) {
for (int i = 1; i < spacesCount; ++i) {
_spaces[i] *= pathLength;
}
}
@ -417,7 +417,7 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
}
if (percentSpacing) {
for (int i = 0; i < spacesCount; ++i) {
for (int i = 1; i < spacesCount; ++i) {
_spaces[i] *= pathLength;
}
}

View File

@ -252,7 +252,7 @@ function PathConstraint:computeWorldPositions (path, spacesCount, tangents, perc
local pathLength = lengths[curveCount + 1];
if percentPosition then position = position * pathLength end
if percentSpacing then
i = 0
i = 1
while i < spacesCount do
spaces[i + 1] = spaces[i + 1] * pathLength
i = i + 1
@ -395,7 +395,7 @@ function PathConstraint:computeWorldPositions (path, spacesCount, tangents, perc
position = position * pathLength / path.lengths[curveCount];
end
if percentSpacing then
i = 0
i = 1
while i < spacesCount do
spaces[i + 1] = spaces[i + 1] * pathLength
i = i + 1

View File

@ -5,5 +5,5 @@ tsc -p tsconfig.core.json
tsc -p tsconfig.webgl.json
tsc -p tsconfig.canvas.json
tsc -p tsconfig.threejs.json
tsc -p tsconfig.widget.json
tsc -p tsconfig.player.json
ls build/*.js build/*.ts | awk '{print "unexpand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh

View File

@ -3039,7 +3039,7 @@ var spine;
if (percentPosition)
position *= pathLength_1;
if (percentSpacing) {
for (var i = 0; i < spacesCount; i++)
for (var i = 1; i < spacesCount; i++)
spaces[i] *= pathLength_1;
}
world = spine.Utils.setArraySize(this.world, 8);
@ -3148,7 +3148,7 @@ var spine;
else
position *= pathLength / path.lengths[curveCount - 1];
if (percentSpacing) {
for (var i = 0; i < spacesCount; i++)
for (var i = 1; i < spacesCount; i++)
spaces[i] *= pathLength;
}
var segments = this.segments;

View File

@ -3039,7 +3039,7 @@ var spine;
if (percentPosition)
position *= pathLength_1;
if (percentSpacing) {
for (var i = 0; i < spacesCount; i++)
for (var i = 1; i < spacesCount; i++)
spaces[i] *= pathLength_1;
}
world = spine.Utils.setArraySize(this.world, 8);
@ -3148,7 +3148,7 @@ var spine;
else
position *= pathLength / path.lengths[curveCount - 1];
if (percentSpacing) {
for (var i = 0; i < spacesCount; i++)
for (var i = 1; i < spacesCount; i++)
spaces[i] *= pathLength;
}
var segments = this.segments;

View File

@ -3039,7 +3039,7 @@ var spine;
if (percentPosition)
position *= pathLength_1;
if (percentSpacing) {
for (var i = 0; i < spacesCount; i++)
for (var i = 1; i < spacesCount; i++)
spaces[i] *= pathLength_1;
}
world = spine.Utils.setArraySize(this.world, 8);
@ -3148,7 +3148,7 @@ var spine;
else
position *= pathLength / path.lengths[curveCount - 1];
if (percentSpacing) {
for (var i = 0; i < spacesCount; i++)
for (var i = 1; i < spacesCount; i++)
spaces[i] *= pathLength;
}
var segments = this.segments;

View File

@ -3039,7 +3039,7 @@ var spine;
if (percentPosition)
position *= pathLength_1;
if (percentSpacing) {
for (var i = 0; i < spacesCount; i++)
for (var i = 1; i < spacesCount; i++)
spaces[i] *= pathLength_1;
}
world = spine.Utils.setArraySize(this.world, 8);
@ -3148,7 +3148,7 @@ var spine;
else
position *= pathLength / path.lengths[curveCount - 1];
if (percentSpacing) {
for (var i = 0; i < spacesCount; i++)
for (var i = 1; i < spacesCount; i++)
spaces[i] *= pathLength;
}
var segments = this.segments;

View File

@ -3039,7 +3039,7 @@ var spine;
if (percentPosition)
position *= pathLength_1;
if (percentSpacing) {
for (var i = 0; i < spacesCount; i++)
for (var i = 1; i < spacesCount; i++)
spaces[i] *= pathLength_1;
}
world = spine.Utils.setArraySize(this.world, 8);
@ -3148,7 +3148,7 @@ var spine;
else
position *= pathLength / path.lengths[curveCount - 1];
if (percentSpacing) {
for (var i = 0; i < spacesCount; i++)
for (var i = 1; i < spacesCount; i++)
spaces[i] *= pathLength;
}
var segments = this.segments;

View File

@ -3039,7 +3039,7 @@ var spine;
if (percentPosition)
position *= pathLength_1;
if (percentSpacing) {
for (var i = 0; i < spacesCount; i++)
for (var i = 1; i < spacesCount; i++)
spaces[i] *= pathLength_1;
}
world = spine.Utils.setArraySize(this.world, 8);
@ -3148,7 +3148,7 @@ var spine;
else
position *= pathLength / path.lengths[curveCount - 1];
if (percentSpacing) {
for (var i = 0; i < spacesCount; i++)
for (var i = 1; i < spacesCount; i++)
spaces[i] *= pathLength;
}
var segments = this.segments;

View File

@ -178,7 +178,7 @@ module spine {
let pathLength = lengths[curveCount];
if (percentPosition) position *= pathLength;
if (percentSpacing) {
for (let i = 0; i < spacesCount; i++)
for (let i = 1; i < spacesCount; i++)
spaces[i] *= pathLength;
}
world = Utils.setArraySize(this.world, 8);
@ -289,7 +289,7 @@ module spine {
else
position *= pathLength / path.lengths[curveCount - 1];
if (percentSpacing) {
for (let i = 0; i < spacesCount; i++)
for (let i = 1; i < spacesCount; i++)
spaces[i] *= pathLength;
}

View File

Before

Width:  |  Height:  |  Size: 470 KiB

After

Width:  |  Height:  |  Size: 470 KiB

View File

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 257 KiB

View File

@ -0,0 +1,48 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script src="../../build/spine-player.js"></script>
<link rel="stylesheet" href="../css/spine-player.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<style>
body {
background: gray;
margin: 0px;
}
</style>
<body>
<div id="container" style="width: 640px; height: 380px;"></div>
<div id="container-raptor" style="width: 640px; height: 380px;"></div>
</body>
<script>
// Creates a new spine player. The debugRender option enables
// rendering of viewports and padding for debugging purposes.
new spine.SpinePlayer(document.getElementById("container"), {
jsonUrl: "assets/spineboy-pro.json",
atlasUrl: "assets/spineboy-pma.atlas",
animation: "run",
premultipliedAlpha: true,
backgroundColor: "#cccccc",
viewport: {
debugRender: true,
}
});
// Creates a new spine player with a transparent background,
// so content from the website shines through. Hides the controls.
new spine.SpinePlayer(document.getElementById("container-raptor"), {
jsonUrl: "assets/raptor-pro.json",
atlasUrl: "assets/raptor-pma.atlas",
animation: "walk",
showControls: false,
premultipliedAlpha: true,
backgroundColor: "#00000000",
alpha: true
});
</script>
</body>
</html>

View File

@ -4,18 +4,18 @@
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"outFile": "build/spine-widget.js",
"outFile": "build/spine-player.js",
"sourceMap": true,
"declaration": true
},
"include": [
"core/src/**/*",
"webgl/src/**/*",
"widget/src/**/*"
"player/src/**/*"
],
"exclude": [
"canvas",
"threejs",
"build"
"build"
]
}

View File

@ -1,29 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="../../build/spine-widget.js"></script>
<link rel="stylesheet" href="../css/spine-player.css">
</head>
<style>
html, body {
margin: 0;
padding: 0;
}
* {
box-sizing: border-box;
}
</style>
<body>
<div id="player"></div>
</body>
<script>
window.addEventListener("message", function (event) {
new spine.SpinePlayer(document.getElementById("player"), event.data);
}, false);
</script>
</body>
</html>

View File

@ -1,29 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<style>
iframe {
border: none;
}
</style>
<body>
<!--<iframe id="player" src="http://esotericsoftware.com/spine-player/3.7/iframe.html" width="600" height="480"></iframe>-->
<iframe id="player" src="http://localhost:8000/widget/example/iframe-local.html" width="600" height="480"></iframe>
</body>
<script>
var player = document.getElementById("player");
var playerConfig = {
atlasUrl: "http://esotericsoftware.com/files/examples/spineboy/export/spineboy-pma.atlas",
jsonUrl: "http://esotericsoftware.com/files/examples/spineboy/export/spineboy-pro.json",
premultipliedAlpha: true
};
player.addEventListener("load", function () {
player.contentWindow.postMessage(playerConfig, "*");
});
</script>
</html>

View File

@ -1,29 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="./spine-widget.js"></script>
<link rel="stylesheet" href="./spine-player.css">
</head>
<style>
html, body {
margin: 0;
padding: 0;
}
* {
box-sizing: border-box;
}
</style>
<body>
<div id="player"></div>
</body>
<script>
window.addEventListener("message", function (event) {
new spine.SpinePlayer(document.getElementById("player"), event.data);
}, false);
</script>
</body>
</html>

View File

@ -1,130 +0,0 @@
<html>
<script src="../../build/spine-widget.js"></script>
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<body>
<center>
<!-- You can programmatically initialize a widget -->
<div id="spine-widget" style="margin-bottom: 20px; width: 640px; height: 480px;"></div>
<!-- You can also specify your own x/y and scale for the skeleton -->
<div style="margin-bottom: 20px; width: 100%; height: 150px;" class="spine-widget" data-json="assets/raptor-pro.json" data-atlas="assets/raptor-pma.atlas" data-animation="jump" data-fit-to-canvas="false" data-scale="0.1" data-x="200" data-y="10"></div>
<!-- Or make things real small -->
<div style="margin-bottom: 20px; width: 320px; height: 240px;" class="spine-widget" data-json="assets/raptor-pro.json" data-atlas="assets/raptor-pma.atlas" data-animation="walk" data-background-color="#cc0000"></div>
<!-- You can also specify the .JSON and .atlas files inline to reduce the number of requests made to the server -->
<div id="spine-widget-inline" style="margin-bottom: 20px; width: 640px; height: 480px;"></div>
</center>
</body>
<script id="atlas" type="text/plain">
spine-logo.png
size: 128,64
format: RGBA8888
filter: Linear,Linear
repeat: none
logo
rotate: false
xy: 2, 2
size: 104, 32
orig: 104, 32
offset: 0, 0
index: -1
</script>
<script id="atlasPage" type="text/plain">
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAABACAYAAADS1n9/AAAFLElEQVR42u2bTWhVRxTH//myJtTmGT+SYEvTFGvTRBOTV8tLbXwL0e3bZCEiBtRuzUa6ElJX2QgKiqibiG4MfiSCiHaToohowQQEUShY/MCNNQUXLtSUs7gwDHPvzLx737svef8fDNHk3nnnnPnP3DNn7gMIIWTR0NKAlpPrcXKqB1Py80A7DvSvQH9DDRoYnSpgvBPjC3ks6O3DNnx4NYhXc1nM3dmMO5PdmDz4JQ4ur8VyRm0Jcbkbl00CCGsn1uMEo7aEuNSNSz4CmOnDDKNWBH19fQOjo6O/qU1+53p/Pp/fLq2jo6Mzqt9CoTDsY9fhr3HYRwAXunDBp/8wu8XOOHab0Pv0jXHJBn5qavqP+fn/FkxtZubPv2xGSgCD68WpoF+519Tns2f//Osa0NZlaH3zM964CmDXWuzyGfzApomJc5OB3bOzc38XGwsTEhPxOSzGEv9UhCCq1w0T5/UABIPqKoCRkZFfw5xVm6sIJOt/sgVPbIP/MIuHPrsD1W4ZBBe7JTaZTGal62ccO3b8tEssZBzKLgLVOJkB6jIoTkpAxGEfAaizXu6VPoJlVp8J8m/XYNbXoH54DYbPd+H805/w9P0Q3gcD/3Yr3p7ZgDOrGrDKd/lXbQmzW2LjMyHUma/2L/9X/dVXX19xxUZ1KuyD5fc2o9RAqjPKdJ9+rWswTTTXozlTj0yx95vslsE22a2KQAbTZXVV+5XPCrtWnTRx4hFLAHH60QMpDrmuPLZrS4lut4jWdUBty/XY2O/jwbXiry05TCUePg75BNK2YqgOxxVfkgKw2a3mRlEz2vfaJCejF/qyJs+9JAKZ5PIo5L5A7sYm3Li9Gbevb8T1iz/gohSI7vXj3sscXs5vxfz9AdyPkwO42K0+r202+w5oUpPRewto2gXoyUrSgdQdtgXzag+uumwBd7didyUIwPRosTWfeCQuAtO+15S1piWA6R5ML2YB+LZUagKy/JsKNyIOn0JQKQRw5BscsQ3+pzw+dTaisxIFoFf/olqxj+FEVwS9eGHLTEstAKkGvsjhRZQAxjowVurcpVgBLMqzAb0qlqTDIjKf7FtY9xnWnd2As68H8VotAF3biGs7WrCjHMmrqwDEH7Vf/Zxh0aA6HFWg0ANpc1itkMkjxteupjo0NdaiMeltYJK7ADWvSn1ZT0IAUU6YCkFRlUU1OLYiSTnrAEkKQC0EpVnsiizGSPDDZqsMlLpFjEoETVmvOG3qW88v4ma8ckaQXYFsYTUKlZIEmmodLkKXSVa2HUCwDMsgi3EiiODwIzgEcimRRm17pG+ZCcE5uL7flb/52l1Xg7p97dg38T0m7vbj7rtf8C7ICaQgJOcDlSAA/VGn1liCOAexlvgHk61sZwGuR7ZimO2ZrgdSBjzq/Nv2mIhiZwt2Ru0GfBLCUgvAtOLZWlkPg8L2/urKUGwhKOxFk6DAVKzNg80YjBLA9pVwrqKpuxGXEz61dO5TrZNro166kc+WvlNLFmWQ1WXJtxQZNZMkyMX2a6K2BrWzWcyaBv9KD67II6JSk2pTnFN/JSyNbDoubcvQduo7nHowgAe3enHr6Lc4OpTBEEh1CIBQAIQCIFUtgN7P0Ssvhz76EY/kq2GPt+Dx8xyeH/oKhzgiZUZ9vbxcZc+9bdhr2gnIN4k4IlVAVxO6Pm7DR10A+9uxn9GpEqQqKO8IyjeEb27CzT2t2MOoEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQpYG/wPiudlO23hTHQAAAABJRU5ErkJggg==
</script>
<script id="json" type="text/plain">
{
"skeleton": { "hash": "MYXtB/H4Z2fp6J+/XZiyN+f9Hfc", "spine": "3.5.49", "width": 104, "height": 32, "images": "" },
"bones": [
{ "name": "root" }
],
"slots": [
{ "name": "spine-logo", "bone": "root", "attachment": "logo" }
],
"skins": {
"default": {
"spine-logo": {
"logo": { "width": 104, "height": 32 }
}
}
},
"animations": {
"animation": {
"bones": {
"root": {
"rotate": [
{ "time": 0, "angle": 0 },
{ "time": 0.8333, "angle": 39.96 },
{ "time": 1.6667, "angle": 0 }
],
"scale": [
{ "time": 0, "x": 1, "y": 1 },
{ "time": 0.8333, "x": 3, "y": 3 },
{ "time": 1.6667, "x": 1, "y": 1 }
]
}
}
}
}
}
</script>
<script>
function supportsWebGL() {
try {
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
return ctx != null;
} catch(e) {
return false;
}
}
if (!supportsWebGL()) {
alert('WebGL is unavailable.');
}
spineWidget = new spine.SpineWidget("spine-widget", {
json: "assets/spineboy-pro.json",
atlas: "assets/spineboy-pma.atlas",
animation: "run",
backgroundColor: "#00000000",
debug: true,
success: function (widget) {
var animIndex = 0;
widget.canvas.onclick = function () {
animIndex++;
var animations = widget.skeleton.data.animations;
if (animIndex >= animations.length) animIndex = 0;
widget.setAnimation(animations[animIndex].name);
}
}
});
json = document.getElementById("json").innerHTML;
atlas = document.getElementById("atlas").innerHTML;
atlasPageContent = document.getElementById("atlasPage").innerHTML;
spineWidgetInline = new spine.SpineWidget("spine-widget-inline", {
jsonContent: json,
atlasContent: atlas,
atlasPages: ["spine-logo.png"],
atlasPagesContent: [atlasPageContent],
animation: "animation",
backgroundColor: "#00000000",
debug: true,
success: function (widget) {
var animIndex = 0;
widget.canvas.onclick = function () {
animIndex++;
let animations = widget.skeleton.data.animations;
if (animIndex >= animations.length) animIndex = 0;
widget.setAnimation(animations[animIndex].name);
}
}
});
</script>
</body>
</html>

View File

@ -1,32 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script src="../../build/spine-widget.js"></script>
<link rel="stylesheet" href="../css/spine-player.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<style>
body {
background: gray;
margin: 0px;
}
</style>
<body>
<div id="container" style="width: 100%; height: 90vh;"></div>
</body>
<script>
new spine.SpinePlayer(document.getElementById("container"), {
jsonUrl: "assets/spineboy-pro.json",
atlasUrl: "assets/spineboy-pma.atlas",
premultipliedAlpha: true,
backgroundColor: "#cccccc",
viewport: {
debugRender: true,
}
});
</script>
</body>
</html>

View File

@ -1,58 +0,0 @@
<html>
<script src="../../build/spine-widget.js"></script>
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<body>
<center>
<!-- You can programmatically initialize a widget -->
<button id="reload">Reload</button>
</center>
</body>
<script>
var numWidgets = 0;
var spineWidgets = []
function reload() {
// tear down old widgets if any
for (var i = 0; i < spineWidgets.length; i++) {
spineWidgets[i].pause();
var widgetDiv = document.getElementById("spine-widget-" + spineWidgets[i].id);
if (widgetDiv) widgetDiv.parentNode.removeChild(widgetDiv)
}
spineWidgets = []
for (var i = 0; i < 5; i++) {
var widgetDiv = document.createElement("div");
widgetDiv.id = "spine-widget-" + numWidgets;
widgetDiv.style = "margin-bottom: 20px; width: 640px; height: 480px;"
document.body.appendChild(widgetDiv)
spineWidget = new spine.SpineWidget("spine-widget-" + numWidgets, {
json: "assets/spineboy.json",
atlas: "assets/spineboy.atlas",
animation: "run",
backgroundColor: "#00000000",
debug: true,
success: function (widget) {
var animIndex = 0;
widget.canvas.onclick = function () {
animIndex++;
var animations = widget.skeleton.data.animations;
if (animIndex >= animations.length) animIndex = 0;
widget.setAnimation(animations[animIndex].name);
}
}
});
spineWidget.id = numWidgets++;
spineWidgets.push(spineWidget);
}
}
var reloadButton = document.getElementById("reload");
reloadButton.onclick = reload;
</script>
</body>
</html>

View File

@ -1,366 +0,0 @@
/******************************************************************************
* Spine Runtimes Software License v2.5
*
* Copyright (c) 2013-2016, Esoteric Software
* All rights reserved.
*
* You are granted a perpetual, non-exclusive, non-sublicensable, and
* non-transferable license to use, install, execute, and perform the Spine
* Runtimes software and derivative works solely for personal or internal
* use. Without the written permission of Esoteric Software (see Section 2 of
* the Spine Software License Agreement), you may not (a) modify, translate,
* adapt, or develop new applications using the Spine Runtimes or otherwise
* create derivative works or improvements of the Spine Runtimes or (b) remove,
* delete, alter, or obscure any trademarks or any copyright, trademark, patent,
* or other intellectual property or proprietary rights notices on or in the
* Software, including any copy thereof. Redistributions in binary or source
* form must include this license and terms.
*
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 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.
*****************************************************************************/
module spine {
export class SpineWidget {
skeleton: Skeleton;
state: AnimationState;
context: spine.webgl.ManagedWebGLRenderingContext;
canvas: HTMLCanvasElement;
debugRenderer: spine.webgl.SkeletonDebugRenderer;
private config: SpineWidgetConfig;
private assetManager: spine.webgl.AssetManager;
private shader: spine.webgl.Shader;
private batcher: spine.webgl.PolygonBatcher;
private shapes: spine.webgl.ShapeRenderer;
private debugShader: spine.webgl.Shader;
private mvp = new spine.webgl.Matrix4();
private skeletonRenderer: spine.webgl.SkeletonRenderer;
private paused = false;
private lastFrameTime = Date.now() / 1000.0;
private backgroundColor = new Color();
private loaded = false;
private bounds = { offset: new Vector2(), size: new Vector2() };
constructor (element: HTMLElement | string, config: SpineWidgetConfig) {
if (!element) throw new Error("Please provide a DOM element, e.g. document.getElementById('myelement')");
if (!config) throw new Error("Please provide a configuration, specifying at least the json file, atlas file and animation name");
let elementId = element as string;
if (typeof(element) === "string") element = document.getElementById(element as string);
if (element == null) throw new Error(`Element ${elementId} does not exist`);
this.validateConfig(config);
let existingCanvas = <HTMLCanvasElement>element.children[0];
let canvas = this.canvas = existingCanvas || document.createElement("canvas");
canvas.style.width = "100%";
canvas.style.height = "100%";
if (!existingCanvas) {
(<HTMLElement> element).appendChild(canvas);
}
canvas.width = (<HTMLElement>element).clientWidth;
canvas.height = (<HTMLElement>element).clientHeight;
var webglConfig = { alpha: config.alpha };
this.context = new spine.webgl.ManagedWebGLRenderingContext(canvas, webglConfig);
this.shader = spine.webgl.Shader.newTwoColoredTextured(this.context);
this.batcher = new spine.webgl.PolygonBatcher(this.context);
this.mvp.ortho2d(0, 0, canvas.width - 1, canvas.height - 1);
this.skeletonRenderer = new spine.webgl.SkeletonRenderer(this.context);
this.debugShader = spine.webgl.Shader.newColored(this.context);
this.debugRenderer = new spine.webgl.SkeletonDebugRenderer(this.context);
this.shapes = new spine.webgl.ShapeRenderer(this.context);
let assets = this.assetManager = new spine.webgl.AssetManager(this.context, config.imagesPath ? config.imagesPath : "");
if (!config.atlasContent) {
assets.loadText(config.atlas);
}
if (!config.jsonContent) {
assets.loadText(config.json);
}
if (config.atlasPages == null) {
if (config.atlas) {
var atlasPage = config.atlas.replace(".atlas", ".png");
if (atlasPage.lastIndexOf(config.imagesPath) == 0) {
atlasPage = atlasPage.substr(config.imagesPath.length);
}
assets.loadTexture(atlasPage);
} else {
let firstLine = config.atlasContent.trim().split("\n")[0];
assets.loadTexture(firstLine);
}
} else {
for (let i = 0; i < config.atlasPages.length; i++) {
if (config.atlasPagesContent && config.atlasPagesContent[i]) {
assets.loadTextureData(config.atlasPages[i], config.atlasPagesContent[i]);
} else {
assets.loadTexture(config.atlasPages[i]);
}
}
}
requestAnimationFrame(() => { this.load(); });
}
private validateConfig (config: SpineWidgetConfig) {
if (!config.atlas && !config.atlasContent) throw new Error("Please specify config.atlas or config.atlasContent");
if (!config.json && !config.jsonContent) throw new Error("Please specify config.json or config.jsonContent");
if (!config.animation) throw new Error("Please specify config.animationName");
if (!config.scale) config.scale = 1.0;
if (!config.skin) config.skin = "default";
if (config.loop === undefined) config.loop = true;
if (!config.x) config.x = 0;
if (!config.y) config.y = 0;
if (config.fitToCanvas === undefined) config.fitToCanvas = true;
if (!config.backgroundColor) config.backgroundColor = "#555555";
if (!config.imagesPath) {
if (config.atlas) {
let index = config.atlas.lastIndexOf("/");
if (index != -1) {
config.imagesPath = config.atlas.substr(0, index) + "/";
} else {
config.imagesPath = "";
}
} else {
config.imagesPath = "";
}
}
if (config.json && config.json.lastIndexOf(config.imagesPath) == 0) {
config.json = config.json.substr(config.imagesPath.length);
}
if (config.atlas && config.atlas.lastIndexOf(config.imagesPath) == 0) {
config.atlas = config.atlas.substr(config.imagesPath.length);
}
if (!config.premultipliedAlpha === undefined) config.premultipliedAlpha = false;
if (!config.debug === undefined) config.debug = false;
if (!config.alpha === undefined) config.alpha = true;
this.backgroundColor.setFromString(config.backgroundColor);
this.config = config;
}
private load () {
let assetManager = this.assetManager;
let imagesPath = this.config.imagesPath;
let config = this.config;
if (assetManager.isLoadingComplete()) {
if (assetManager.hasErrors()) {
if (config.error) config.error(this, "Failed to load assets: " + JSON.stringify(assetManager.getErrors()));
else throw new Error("Failed to load assets: " + JSON.stringify(assetManager.getErrors()));
}
let atlasContent = config.atlasContent === undefined ? this.assetManager.get(this.config.atlas) as string : config.atlasContent;
let atlas = new spine.TextureAtlas(atlasContent, (path: string) => {
let texture = assetManager.get(path) as spine.webgl.GLTexture;
return texture;
});
let atlasLoader = new spine.AtlasAttachmentLoader(atlas);
var skeletonJson = new spine.SkeletonJson(atlasLoader);
// Set the scale to apply during parsing, parse the file, and create a new skeleton.
skeletonJson.scale = config.scale;
let jsonContent = config.jsonContent === undefined ? assetManager.get(config.json) as string : config.jsonContent;
var skeletonData = skeletonJson.readSkeletonData(jsonContent);
var skeleton = this.skeleton = new spine.Skeleton(skeletonData);
var bounds = this.bounds;
skeleton.setSkinByName(config.skin);
skeleton.setToSetupPose();
skeleton.updateWorldTransform();
skeleton.getBounds(bounds.offset, bounds.size, []);
if (!config.fitToCanvas) {
skeleton.x = config.x;
skeleton.y = config.y;
}
var animationState = this.state = new spine.AnimationState(new spine.AnimationStateData(skeleton.data));
animationState.setAnimation(0, config.animation, config.loop);
this.loaded = true;
if (config.success) config.success(this);
requestAnimationFrame(() => { this.render(); });
} else
requestAnimationFrame(() => { this.load(); });
}
private render () {
var now = Date.now() / 1000;
var delta = now - this.lastFrameTime;
if (delta > 0.1) delta = 0;
this.lastFrameTime = now;
let gl = this.context.gl;
let color = this.backgroundColor;
this.resize();
gl.clearColor(color.r, color.g, color.b, color.a);
gl.clear(gl.COLOR_BUFFER_BIT);
// Apply the animation state based on the delta time.
var state = this.state;
var skeleton = this.skeleton;
var premultipliedAlpha = this.config.premultipliedAlpha;
state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
// Draw the skeleton
let shader = this.shader;
let batcher = this.batcher;
let skeletonRenderer = this.skeletonRenderer;
shader.bind();
shader.setUniformi(spine.webgl.Shader.SAMPLER, 0);
shader.setUniform4x4f(spine.webgl.Shader.MVP_MATRIX, this.mvp.values);
batcher.begin(shader);
skeletonRenderer.premultipliedAlpha = premultipliedAlpha;
skeletonRenderer.draw(batcher, skeleton);
batcher.end();
shader.unbind();
// Draw debug information if requested via config
if (this.config.debug) {
let shader = this.debugShader;
let shapes = this.shapes;
let renderer = this.debugRenderer;
shader.bind();
shader.setUniform4x4f(spine.webgl.Shader.MVP_MATRIX, this.mvp.values);
renderer.premultipliedAlpha = premultipliedAlpha;
shapes.begin(shader);
renderer.draw(shapes, skeleton);
shapes.end();
shader.unbind();
}
if (!this.paused) requestAnimationFrame(() => { this.render(); });
}
private resize () {
let canvas = this.canvas;
let w = canvas.clientWidth;
let h = canvas.clientHeight;
let bounds = this.bounds;
var devicePixelRatio = window.devicePixelRatio || 1;
if (canvas.width != Math.floor(w * devicePixelRatio) || canvas.height != Math.floor(h * devicePixelRatio)) {
canvas.width = Math.floor(w * devicePixelRatio);
canvas.height = Math.floor(h * devicePixelRatio);
}
// magic
if (this.config.fitToCanvas) {
var centerX = bounds.offset.x + bounds.size.x / 2;
var centerY = bounds.offset.y + bounds.size.y / 2;
var scaleX = bounds.size.x / w;
var scaleY = bounds.size.y / h;
var scale = Math.max(scaleX, scaleY) * 1.2;
if (scale < 1) scale = 1;
var width = w * scale;
var height = h * scale;
this.skeleton.x = this.skeleton.y = 0;
this.mvp.ortho2d(centerX - width / 2, centerY - height / 2, width, height);
} else {
this.mvp.ortho2d(0, 0, w - 1, h - 1);
}
this.context.gl.viewport(0, 0, canvas.width, canvas.height);
}
pause () {
this.paused = true;
}
play () {
this.paused = false;
requestAnimationFrame(() => { this.render(); });
}
isPlaying () {
return !this.paused;
}
setAnimation (animationName: string, animationStateListener: AnimationStateListener2 = null) {
if (!this.loaded) throw new Error("Widget isn't loaded yet");
this.skeleton.setToSetupPose();
let entry = this.state.setAnimation(0, animationName, this.config.loop);
entry.listener = animationStateListener
}
static loadWidgets() {
let widgets = document.getElementsByClassName("spine-widget");
for (var i = 0; i < widgets.length; i++) {
SpineWidget.loadWidget(<HTMLElement>widgets[i]);
}
}
static loadWidget(widget: HTMLElement) {
let config = new SpineWidgetConfig();
config.atlas = widget.getAttribute("data-atlas");
config.json = widget.getAttribute("data-json");
config.animation = widget.getAttribute("data-animation");
if (widget.getAttribute("data-images-path")) config.imagesPath = widget.getAttribute("data-images-path");
if (widget.getAttribute("data-atlas-pages")) config.atlasPages = widget.getAttribute("data-atlas-pages").split(",");
if (widget.getAttribute("data-skin")) config.skin = widget.getAttribute("data-skin");
if (widget.getAttribute("data-loop")) config.loop = widget.getAttribute("data-loop") === "true";
if (widget.getAttribute("data-scale")) config.scale = parseFloat(widget.getAttribute("data-scale"));
if (widget.getAttribute("data-x")) config.x = parseFloat(widget.getAttribute("data-x"));
if (widget.getAttribute("data-y")) config.y = parseFloat(widget.getAttribute("data-y"));
if (widget.getAttribute("data-fit-to-canvas")) config.fitToCanvas = widget.getAttribute("data-fit-to-canvas") === "true";
if (widget.getAttribute("data-background-color")) config.backgroundColor = widget.getAttribute("data-background-color");
if (widget.getAttribute("data-premultiplied-alpha")) config.premultipliedAlpha = widget.getAttribute("data-premultiplied-alpha") === "true";
if (widget.getAttribute("data-debug")) config.debug = widget.getAttribute("data-debug") === "true";
if (widget.getAttribute("data-alpha")) config.alpha = widget.getAttribute("data-alpha") === "true";
new spine.SpineWidget(widget, config);
}
static pageLoaded = false;
private static ready () {
if (SpineWidget.pageLoaded) return;
SpineWidget.pageLoaded = true;
SpineWidget.loadWidgets();
}
static setupDOMListener() {
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", SpineWidget.ready, false);
window.addEventListener("load", SpineWidget.ready, false);
} else {
(<any>document).attachEvent("onreadystatechange", function readyStateChange() {
if (document.readyState === "complete" ) SpineWidget.ready();
});
(<any>window).attachEvent("onload", SpineWidget.ready);
}
}
}
export class SpineWidgetConfig {
json: string;
jsonContent: any;
atlas: string;
atlasContent: string;
animation: string;
imagesPath: string;
atlasPages: string[];
atlasPagesContent: string[];
skin = "default";
loop = true;
scale = 1.0;
x = 0;
y = 0;
alpha = true;
fitToCanvas = true;
backgroundColor = "#555555";
premultipliedAlpha = false;
debug = false;
success: (widget: SpineWidget) => void;
error: (widget: SpineWidget, msg: string) => void;
}
}
spine.SpineWidget.setupDOMListener();