123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- <!-----------------------------------------------------------------------------
- -- Spine Runtimes Software License
- -- Version 2.1
- --
- -- Copyright (c) 2013, Esoteric Software
- -- All rights reserved.
- --
- -- You are granted a perpetual, non-exclusive, non-sublicensable and
- -- non-transferable license to install, execute and perform the Spine Runtimes
- -- Software (the "Software") solely for internal use. Without the written
- -- permission of Esoteric Software (typically granted by licensing Spine), you
- -- may not (a) modify, translate, adapt or otherwise create derivative works,
- -- improvements of the Software or develop new applications using the Software
- -- 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 SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- -- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- -- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- -- OR BUSINESS INTERRUPTION) 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.
- ------------------------------------------------------------------------------>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>spine-threejs</title>
- <script src="../spine-js/spine.js"></script>
- <script src="../three.min.js"></script>
- <style>body, input { font-family: tahoma; font-size: 11pt }</style>
- </head>
- <body>
- <script>
- (function () {
- var loadText = function (url, callback) {
- var req = new XMLHttpRequest();
- req.open("GET", url, true);
- req.responseType = 'text';
- req.addEventListener('error', function (event) {}, false);
- req.addEventListener('abort', function (event) {}, false);
- req.addEventListener('load', function (event) { callback(req.response); }, false);
- req.send();
- return req;
- };
- var loadImage = function (url, callback) {
- var image = new Image();
- image.addEventListener('error', function (event) {}, false);
- image.addEventListener('abort', function (event) {}, false);
- image.addEventListener('load', function (event) { callback (image); }, false);
- image.src = url;
- return image;
- };
- SpineAnimation = function (name, path, scale) {
- THREE.Object3D.call(this);
- this.name = name;
- path = path ? (path +
- ((path.substr(-1) != '/') ? '/' : '')
- ) : '';
- var self = this;
- loadText(path + name + '.atlas', function (atlasText) {
- self.atlas = new spine.Atlas(atlasText, {
- load: function (page, image, atlas) {
- loadImage(path + image, function (image) {
- // calculate UVs in atlas regions
- page.width = image.width;
- page.height = image.height;
- atlas.updateUVs(page);
- // propagate new UVs to attachments, if they were already created
- if (self.skeleton) {
- var skins = self.skeleton.data.skins;
- for (var s = 0, n = skins.length; s < n; s++) {
- var attachments = skins[s].attachments;
- for (var k in attachments) {
- var attachment = attachments[k];
- if (attachment instanceof spine.RegionAttachment) {
- var region = attachment.rendererObject;
- attachment.setUVs(region.u, region.v, region.u2, region.v2, region.rotate);
- }
- }
- }
- }
- // create basic material for the page
- var texture = new THREE.Texture(image);
- texture.needsUpdate = true;
- page.rendererObject = [
- new THREE.MeshBasicMaterial({
- //color: 0xff00, wireframe: true,
- map : texture, side : THREE.DoubleSide, transparent : true, alphaTest : 0.5
- })
- ];
- });
- },
- unload: function (materials) {
- for (var i = 0, n = materials.length; i < n; i++) {
- var material = materials[i];
- if (material.meshes) {
- for (var name in material.meshes) {
- var mesh = material.meshes[name];
- if (mesh.parent) mesh.parent.remove(mesh);
- mesh.geometry.dispose();
- }
- }
- material.map.dispose();
- material.dispose();
- }
- // will be called multiple times
- materials.length = 0;
- }
- });
- loadText(path + name + '.json', function (skeletonText) {
- var json = new spine.SkeletonJson(new spine.AtlasAttachmentLoader(self.atlas));
- json.scale = scale || 1;
- var skeletonData = json.readSkeletonData(JSON.parse(skeletonText));
- self.skeleton = new spine.Skeleton(skeletonData);
- self.stateData = new spine.AnimationStateData(skeletonData);
- self.state = new spine.AnimationState(self.stateData);
- self.dispatchEvent({
- type : SpineAnimation.SKELETON_DATA_LOADED
- });
- });
- });
- var matrix = new THREE.Matrix4();
- // if given, dt must be in ms
- this.update = function (dt, dz) {
- if (!this.state) return;
- this.state.update(dt || (1.0 / 60));
- this.state.apply(this.skeleton);
- this.skeleton.updateWorldTransform();
- this.traverse(function (object) {
- if (object instanceof THREE.Mesh) {
- object.visible = false;
- }
- });
- var Z = 0;
- var drawOrder = this.skeleton.drawOrder;
- for (var i = 0, n = drawOrder.length; i < n; i++) {
- var slot = drawOrder[i];
- var attachment = slot.attachment;
- if (!(attachment instanceof spine.RegionAttachment)) continue;
- var materials = attachment.rendererObject.page.rendererObject;
- // texture was not loaded yet
- if (!materials) continue;
- if (slot.data.additiveBlending && (materials.length == 1)) {
- // create separate material for additive blending
- materials.push(new THREE.MeshBasicMaterial({
- map : materials[0].map,
- side : THREE.DoubleSide,
- transparent : true,
- blending : THREE.AdditiveBlending,
- depthWrite : false
- }));
- }
- var material = materials[ slot.data.additiveBlending ? 1 : 0 ];
- material.meshes = material.meshes || {};
- var mesh = material.meshes[slot.data.name];
- var geometry;
- if (mesh) {
- geometry = mesh.geometry;
- mesh.visible = true;
- } else {
- geometry = new THREE.PlaneGeometry(
- attachment.regionOriginalWidth,
- attachment.regionOriginalHeight
- );
- geometry.dynamic = true;
- mesh = new THREE.Mesh(geometry, material);
- mesh.matrixAutoUpdate = false;
- material.meshes[slot.data.name] = mesh;
- this.add(mesh);
- }
- if (mesh.attachmentTime && (slot.getAttachmentTime () > mesh.attachmentTime)) {
- // do nothing
- } else {
- // update UVs
- geometry.faceVertexUvs[0][0][0].set(attachment.uvs[6], 1- attachment.uvs[7]);
- geometry.faceVertexUvs[0][0][1].set(attachment.uvs[4], 1- attachment.uvs[5]);
- geometry.faceVertexUvs[0][0][2].set(attachment.uvs[0], 1- attachment.uvs[1]);
- geometry.faceVertexUvs[0][1][0].set(attachment.uvs[4], 1- attachment.uvs[5]);
- geometry.faceVertexUvs[0][1][1].set(attachment.uvs[2], 1- attachment.uvs[3]);
- geometry.faceVertexUvs[0][1][2].set(attachment.uvs[0], 1- attachment.uvs[1]);
- geometry.uvsNeedUpdate = true;
- geometry.vertices[1].set(attachment.offset[0], attachment.offset[1], 0);
- geometry.vertices[3].set(attachment.offset[2], attachment.offset[3], 0);
- geometry.vertices[2].set(attachment.offset[4], attachment.offset[5], 0);
- geometry.vertices[0].set(attachment.offset[6], attachment.offset[7], 0);
- geometry.verticesNeedUpdate = true;
- mesh.attachmentTime = slot.getAttachmentTime();
- }
- matrix.makeTranslation(
- this.skeleton.x + slot.bone.worldX,
- this.skeleton.y + slot.bone.worldY,
- (dz || 0.1) * Z++
- );
- matrix.elements[0] = slot.bone.a; matrix.elements[4] = slot.bone.b;
- matrix.elements[1] = slot.bone.c; matrix.elements[5] = slot.bone.d;
- mesh.matrix.copy(matrix);
- /* TODO slot.r,g,b,a ?? turbulenz example code:
- batch.add(
- attachment.rendererObject.page.rendererObject,
- vertices[0], vertices[1],
- vertices[6], vertices[7],
- vertices[2], vertices[3],
- vertices[4], vertices[5],
- skeleton.r * slot.r,
- skeleton.g * slot.g,
- skeleton.b * slot.b,
- skeleton.a * slot.a,
- attachment.uvs[0], attachment.uvs[1],
- attachment.uvs[4], attachment.uvs[5]
- );
- */
- }
- };
- };
- SpineAnimation.SKELETON_DATA_LOADED = 'skeletonDataLoaded';
- SpineAnimation.prototype = Object.create(THREE.Object3D.prototype);
- SpineAnimation.prototype.dispose = function () {
- if (this.parent) this.parent.remove(this); this.atlas.dispose();
- };
- }) ();
- var scene, camera, renderer;
- var geometry, material, mesh;
- var anim;
- function load (name, scale) {
- if (anim) anim.dispose();
- anim = new SpineAnimation(name, 'data/', scale);
- anim.addEventListener(SpineAnimation.SKELETON_DATA_LOADED, function () {
- var canvas = renderer.domElement;
- switch (anim.name) {
- case 'spineboy':
- anim.stateData.setMixByName('walk', 'jump', 0.2);
- anim.stateData.setMixByName('run', 'jump', 0.2);
- anim.stateData.setMixByName('jump', 'run', 0.2);
- anim.state.setAnimationByName(0, 'walk', true);
- canvas.onmousedown = function () {
- anim.state.setAnimationByName(0, 'jump', false);
- anim.state.addAnimationByName(0, 'run', true, 0);
- }
- break;
- case 'hero':
- anim.stateData.defaultMix = 0.2;
- anim.stateData.setMixByName('Walk', 'Attack', 0);
- anim.stateData.setMixByName('Attack', 'Run', 0);
- anim.stateData.setMixByName('Run', 'Attack', 0);
- anim.state.setAnimationByName(0, 'Idle', true);
- canvas.onmousedown = function () {
- var name = anim.state.getCurrent(0).animation.name;
- if (name == 'Idle')
- anim.state.setAnimationByName(0, 'Crouch', true);
- else if (name == 'Crouch')
- anim.state.setAnimationByName(0, 'Walk', true);
- else {
- anim.state.setAnimationByName(0, 'Attack', false);
- anim.state.addAnimationByName(0, 'Run', true, 0);
- }
- }
- break;
- case 'goblins':
- anim.skeleton.setSkinByName('goblingirl'); // TODO - spine.Skeleton.prototype.setSkin doesn't work?
- anim.skeleton.setSlotsToSetupPose();
- anim.state.setAnimationByName(0, 'walk', true);
- canvas.onmousedown = function () {
- anim.skeleton.setSkinByName(anim.skeleton.skin.name == 'goblin' ? 'goblingirl' : 'goblin');
- anim.skeleton.setSlotsToSetupPose();
- }
- break;
- case 'skeleton':
- anim.state.setAnimationByName(0, 'walk test', true);
- canvas.onmousedown = function () {
- var name = anim.state.getCurrent(0).animation.name;
- if (name == 'walk test')
- anim.state.setAnimationByName(0, 'idle test', true);
- else
- anim.state.setAnimationByName(0, 'walk test', true);
- }
- break;
- }
- //anim.skeleton.y = -200;
- });
- mesh.add(anim);
- }
- function init() {
- scene = new THREE.Scene();
- var width = 640, height = 480;
- camera = new THREE.PerspectiveCamera(75, width / height, 1, 3000);
- camera.position.z = 400;
- geometry = new THREE.BoxGeometry(200, 200, 200);
- material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true });
- mesh = new THREE.Mesh(geometry, material);
- scene.add(mesh);
- load('spineboy', 0.4);
- renderer = new THREE.WebGLRenderer();
- renderer.setSize(width, height);
- document.body.appendChild(renderer.domElement);
- }
- var lastTime = Date.now();
- function animate() {
- requestAnimationFrame(animate);
- var t = Date.now();
- var a = Math.sin(t * 6e-4);
- var b = Math.cos(t * 3e-4);
- mesh.rotation.x = a * Math.PI * 0.2;
- mesh.rotation.y = b * Math.PI * 0.4;
- anim.update((t - lastTime) / 1000);
- lastTime = t;
- renderer.render(scene, camera);
- }
- init();
- animate();
- </script>
- <br><br>
- <input type="button" value="Spineboy" onclick="load('spineboy', 0.6)">
- <input type="button" value="Hero" onclick="load('hero', 1)">
- <input type="button" value="Goblins" onclick="load('goblins', 1)">
- Click to change the animation or skin.
- </body>
- </html>
|