Browse Source

[phaser] Loading and caching of skeleton data, atlas, textures, creation of skeletons, animation state in game object.

# Conflicts:
#	spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp
#	spine-flutter/src/spine_flutter.cpp
Mario Zechner 2 năm trước cách đây
mục cha
commit
9ece84dc85

+ 1 - 0
spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp

@@ -1256,6 +1256,7 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
 
 				switch (timelineType) {
 					case ATTACHMENT_DEFORM: {
+						VertexAttachment *attachment = static_cast<VertexAttachment *>(baseAttachment);
 						bool weighted = attachment->_bones.size() > 0;
 						Vector<float> &vertices = attachment->_vertices;
 						int deformLength = weighted ? (int) vertices.size() / 3 * 2 : (int) vertices.size();

+ 34 - 34
spine-sfml/cpp/example/testbed.cpp

@@ -34,42 +34,42 @@
 using namespace spine;
 
 int main(void) {
-    String atlasFile("data/spineboy-pma.atlas");
-    String skeletonFile("data/spineboy-pro.skel");
-    float scale = 0.6f;
-    SFMLTextureLoader textureLoader;
-    Atlas *atlas = new Atlas(atlasFile, &textureLoader);
-    SkeletonData *skeletonData = nullptr;
-    if (strncmp(skeletonFile.buffer(), ".skel", skeletonFile.length()) > 0) {
-        SkeletonBinary binary(atlas);
-        binary.setScale(scale);
-        skeletonData = binary.readSkeletonDataFile(skeletonFile);
-    } else {
-        SkeletonJson json(atlas);
-        json.setScale(scale);
-        skeletonData = json.readSkeletonDataFile(skeletonFile);
-    }
+	String atlasFile("data/spineboy-pma.atlas");
+	String skeletonFile("data/spineboy-pro.skel");
+	float scale = 0.6f;
+	SFMLTextureLoader textureLoader;
+	Atlas *atlas = new Atlas(atlasFile, &textureLoader);
+	SkeletonData *skeletonData = nullptr;
+	if (strncmp(skeletonFile.buffer(), ".skel", skeletonFile.length()) > 0) {
+		SkeletonBinary binary(atlas);
+		binary.setScale(scale);
+		skeletonData = binary.readSkeletonDataFile(skeletonFile);
+	} else {
+		SkeletonJson json(atlas);
+		json.setScale(scale);
+		skeletonData = json.readSkeletonDataFile(skeletonFile);
+	}
 
-    AnimationStateData stateData(skeletonData);
-    SkeletonDrawable drawable(skeletonData, &stateData);
-    drawable.skeleton->setPosition(320, 590);
-    drawable.state->setAnimation(0, "walk", true);
+	AnimationStateData stateData(skeletonData);
+	SkeletonDrawable drawable(skeletonData, &stateData);
+	drawable.skeleton->setPosition(320, 590);
+	drawable.state->setAnimation(0, "walk", true);
 
-    sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - testbed");
-    window.setFramerateLimit(60);
-    sf::Event event;
-    sf::Clock deltaClock;
-    while (window.isOpen()) {
-        while (window.pollEvent(event))
-            if (event.type == sf::Event::Closed) window.close();
+	sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - testbed");
+	window.setFramerateLimit(60);
+	sf::Event event;
+	sf::Clock deltaClock;
+	while (window.isOpen()) {
+		while (window.pollEvent(event))
+			if (event.type == sf::Event::Closed) window.close();
 
-        float delta = deltaClock.getElapsedTime().asSeconds();
-        deltaClock.restart();
-        drawable.update(delta);
-        window.clear();
-        window.draw(drawable);
-        window.display();
-    }
+		float delta = deltaClock.getElapsedTime().asSeconds();
+		deltaClock.restart();
+		drawable.update(delta);
+		window.clear();
+		window.draw(drawable);
+		window.display();
+	}
 
-    return 0;
+	return 0;
 }

+ 2 - 0
spine-ts/package-lock.json

@@ -8419,7 +8419,9 @@
       "version": "4.2.10",
       "license": "LicenseRef-LICENSE",
       "dependencies": {
+        "@esotericsoftware/spine-canvas": "^4.2.10",
         "@esotericsoftware/spine-core": "^4.2.10",
+        "@esotericsoftware/spine-webgl": "^4.2.10",
         "phaser": "^3.55.2"
       }
     },

+ 4 - 3
spine-ts/spine-phaser/example/index.js

@@ -8,7 +8,7 @@ var config = {
     type: Phaser.AUTO,
     width: 800,
     height: 600,
-    // type: Phaser.CANVAS,
+    type: Phaser.CANVAS,
     scene: {
         preload: preload,
         create: create,
@@ -23,11 +23,12 @@ var config = {
 var game = new Phaser.Game(config);
 
 function preload () {
-    this.load.spine("raptor", "assets/raptor-pro.json", "assets/raptor.atlas", true);
+    this.load.spineJson("raptor-data", "assets/raptor-pro.json");
+    this.load.spineAtlas("raptor-atlas", "assets/raptor.atlas");
 }
 
 function create () {
     let plugin = this.spine;
-    var boy = this.add.spine(400, 600, 'raptor');
+    var boy = this.add.spine(400, 600, 'raptor-data', "raptor-atlas");
     this.add.text(10, 10, "Spine", { font: '16px Courier', fill: '#00ff00' });
 }

+ 2 - 0
spine-ts/spine-phaser/package.json

@@ -31,6 +31,8 @@
   "homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
   "dependencies": {
     "@esotericsoftware/spine-core": "^4.2.10",
+    "@esotericsoftware/spine-webgl": "^4.2.10",
+    "@esotericsoftware/spine-canvas": "^4.2.10",
     "phaser": "^3.55.2"
   }
 }

+ 0 - 13
spine-ts/spine-phaser/src/SpineFile.ts

@@ -1,13 +0,0 @@
-import { SPINE_ATLAS_CACHE_KEY, SPINE_FILE_TYPE, SPINE_LOADER_TYPE } from "./keys";
-
-export class SpineFile extends Phaser.Loader.MultiFile {
-    constructor(loader: Phaser.Loader.LoaderPlugin, key: string, jsonURL: string, atlasURL: string, premultipliedAlpha: boolean = false, jsonXhrSettings: Phaser.Types.Loader.XHRSettingsObject,  atlasXhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
-        let json = new Phaser.Loader.FileTypes.JSONFile(loader, key, jsonURL, jsonXhrSettings);
-        let atlas = new Phaser.Loader.FileTypes.TextFile(loader, key, atlasURL, atlasXhrSettings);
-        atlas.cache = loader.cacheManager.custom[SPINE_ATLAS_CACHE_KEY];
-        super(loader, SPINE_FILE_TYPE, key, [json, atlas]);
-    }
-
-    addToCache() {
-    }
-}

+ 26 - 14
spine-ts/spine-phaser/src/SpineGameObject.ts

@@ -1,11 +1,12 @@
 import { SPINE_GAME_OBJECT_TYPE } from "./keys";
 import { SpinePlugin } from "./SpinePlugin";
 import { ComputedSizeMixin, DepthMixin, FlipMixin, ScrollFactorMixin, TransformMixin, VisibleMixin } from "./mixins";
+import { AnimationState, AnimationStateData, Skeleton } from "@esotericsoftware/spine-core";
 
 class BaseSpineGameObject extends Phaser.GameObjects.GameObject {
-    constructor(scene: Phaser.Scene, type: string) {
-        super(scene, type);
-    }
+	constructor (scene: Phaser.Scene, type: string) {
+		super(scene, type);
+	}
 }
 
 interface SpineContainer {
@@ -13,21 +14,32 @@ interface SpineContainer {
 }
 
 export class SpineGameObject extends ComputedSizeMixin(DepthMixin(FlipMixin(ScrollFactorMixin(TransformMixin(VisibleMixin(BaseSpineGameObject)))))) {
-    blendMode = -1;
+	blendMode = -1;
+	skeleton: Skeleton | null = null;
+	animationState: AnimationState | null = null;
 
-    constructor(scene: Phaser.Scene, plugin: SpinePlugin, x: number, y: number, key: string) {
-        super(scene, SPINE_GAME_OBJECT_TYPE);
-        this.setPosition(x, y);
-    }
+	constructor (scene: Phaser.Scene, private plugin: SpinePlugin, x: number, y: number, dataKey: string, atlasKey: string) {
+		super(scene, SPINE_GAME_OBJECT_TYPE);
+		this.setPosition(x, y);
+	}
 
-    preUpdate(time: number, delta: number) {
-    }
+	setSkeleton (dataKey: string, atlasKey: string) {
+		if (dataKey && atlasKey) {
+			this.skeleton = this.plugin.createSkeleton(dataKey, atlasKey);
+			this.animationState = new AnimationState(new AnimationStateData(this.skeleton.data));
+		} else {
+			this.skeleton = null;
+		}
+	}
 
-    renderWebGL(renderer: Phaser.Renderer.WebGL.WebGLRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix, container: SpineContainer) {
+	preUpdate (time: number, delta: number) {
+	}
 
-    }
+	renderWebGL (renderer: Phaser.Renderer.WebGL.WebGLRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix, container: SpineContainer) {
 
-    renderCanvas(renderer: Phaser.Renderer.Canvas.CanvasRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix) {
+	}
 
-    }
+	renderCanvas (renderer: Phaser.Renderer.Canvas.CanvasRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix) {
+
+	}
 }

+ 278 - 127
spine-ts/spine-phaser/src/SpinePlugin.ts

@@ -28,135 +28,286 @@
  *****************************************************************************/
 
 import Phaser from "phaser";
-import { SPINE_ATLAS_CACHE_KEY, SPINE_CONTAINER_TYPE, SPINE_FILE_TYPE, SPINE_GAME_OBJECT_TYPE, SPINE_TEXTURE_CACHE_KEY } from "./keys";
-import { SceneRenderer, SkeletonDebugRenderer, SkeletonRenderer } from "@esotericsoftware/spine-webgl"
-import { SpineFile } from "./SpineFile";
+import { SPINE_ATLAS_CACHE_KEY, SPINE_CONTAINER_TYPE, SPINE_GAME_OBJECT_TYPE, SPINE_ATLAS_TEXTURE_CACHE_KEY, SPINE_SKELETON_DATA_FILE_TYPE, SPINE_ATLAS_FILE_TYPE, SPINE_SKELETON_FILE_CACHE_KEY as SPINE_SKELETON_DATA_CACHE_KEY } from "./keys";
+import { AtlasAttachmentLoader, GLTexture, SceneRenderer, Skeleton, SkeletonData, SkeletonDebugRenderer, SkeletonJson, SkeletonRenderer, TextureAtlas } from "@esotericsoftware/spine-webgl"
 import { SpineGameObject } from "./SpineGameObject";
+import { CanvasTexture } from "@esotericsoftware/spine-canvas";
 
 export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
-    game: Phaser.Game;
-    isWebGL: boolean;
-    atlasCache: Phaser.Cache.BaseCache;
-    spineTextureCache: Phaser.Cache.BaseCache;
-    jsonCache: Phaser.Cache.BaseCache;
-    textures: Phaser.Textures.TextureManager;
-    gl: WebGLRenderingContext | null;
-    phaserRenderer: Phaser.Renderer.Canvas.CanvasRenderer | Phaser.Renderer.WebGL.WebGLRenderer | null;
-    sceneRenderer: SceneRenderer | null;
-
-    constructor(scene: Phaser.Scene, pluginManager: Phaser.Plugins.PluginManager, pluginKey: string) {
-        super(scene, pluginManager, pluginKey);
-        var game = this.game = pluginManager.game;
-        this.isWebGL = this.game.config.renderType === 2;
-        this.atlasCache = this.game.cache.addCustom(SPINE_ATLAS_CACHE_KEY);
-        this.spineTextureCache = this.game.cache.addCustom(SPINE_TEXTURE_CACHE_KEY);
-        this.jsonCache = this.game.cache.json;
-        this.textures = this.game.textures;
-        this.gl = this.isWebGL ? (this.game.renderer as Phaser.Renderer.WebGL.WebGLRenderer).gl : null;
-        this.phaserRenderer = this.game.renderer;
-        this.sceneRenderer = null;
-
-        if (!this.phaserRenderer) {
-            this.phaserRenderer = {
-                width: game.scale.width,
-                height: game.scale.height,
-                preRender: () => { },
-                postRender: () => { },
-                render: () => { },
-                destroy: () => { }
-            } as unknown as Phaser.Renderer.Canvas.CanvasRenderer;
-        }
-
-        let fileCallback = function (this: any, key: string,
-            jsonURL: string,
-            atlasURL: string,
-            premultipliedAlpha: boolean,
-            jsonXhrSettings: Phaser.Types.Loader.XHRSettingsObject,
-            atlasXhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
-            let file = new SpineFile(this as any, key, jsonURL, atlasURL, premultipliedAlpha, jsonXhrSettings, atlasXhrSettings);
-            this.addFile(file.files);
-            return this;
-        };
-
-        let self = this;
-        let addSpineGameObject = function (this: Phaser.GameObjects.GameObjectFactory, x: number, y: number, key: string) {
-            let gameObject = new SpineGameObject(scene, self, x, y, key);
-            this.displayList.add(gameObject);
-            this.updateList.add(gameObject);
-        };
-
-        let makeSpineGameObject = function (this: Phaser.GameObjects.GameObjectFactory, config: any, addToScene: boolean) {
-            let key = config.key ? config.key : null;
-            let gameObject = new SpineGameObject(this.scene, self, 0, 0, key);
-            if (addToScene !== undefined) {
-                config.add = addToScene;
-            }
-            Phaser.GameObjects.BuildGameObject(this.scene, gameObject, config);
-        }
-
-        pluginManager.registerFileType(SPINE_FILE_TYPE, fileCallback, scene);
-        pluginManager.registerGameObject(SPINE_GAME_OBJECT_TYPE, addSpineGameObject, makeSpineGameObject);
-    }
-
-    boot() {
-        if (this.isWebGL) {
-            //  Monkeypatch the Spine setBlendMode functions, or batching is destroyed!
-            let setBlendMode = function (this: any, srcBlend: any, dstBlend: any) {
-                if (srcBlend !== this.srcBlend || dstBlend !== this.dstBlend) {
-                    let gl = this.context.gl;
-                    this.srcBlend = srcBlend;
-                    this.dstBlend = dstBlend;
-                    if (this.isDrawing) {
-                        this.flush();
-                        gl.blendFunc(this.srcBlend, this.dstBlend);
-                    }
-                }
-            };
-
-            var sceneRenderer = this.sceneRenderer;
-            if (!sceneRenderer) {
-                sceneRenderer = new SceneRenderer((this.phaserRenderer! as Phaser.Renderer.WebGL.WebGLRenderer).canvas, this.gl!, true);
-                sceneRenderer.batcher.setBlendMode = setBlendMode;
-                (sceneRenderer as any).shapes.setBlendMode = setBlendMode;
-            }
-
-            this.sceneRenderer = sceneRenderer;
-        }
-
-        var eventEmitter = this.systems.events;
-        eventEmitter.once('shutdown', this.shutdown, this);
-        eventEmitter.once('destroy', this.destroy, this);
-        this.game.events.once('destroy', this.gameDestroy, this);
-    }
-
-    onResize() {
-        var phaserRenderer = this.phaserRenderer;
-        var sceneRenderer = this.sceneRenderer;
-
-        if (phaserRenderer && sceneRenderer) {
-            var viewportWidth = phaserRenderer.width;
-            var viewportHeight = phaserRenderer.height;
-            sceneRenderer.camera.position.x = viewportWidth / 2;
-            sceneRenderer.camera.position.y = viewportHeight / 2;
-            sceneRenderer.camera.setViewport(viewportWidth, viewportHeight);
-        }
-    }
-
-    shutdown() {
-        this.systems.events.off("shutdown", this.shutdown, this);
-        if (this.isWebGL) {
-            this.game.scale.off(Phaser.Scale.Events.RESIZE, this.onResize, this);
-        }
-    }
-
-    destroy() {
-        this.shutdown()
-    }
-
-    gameDestroy() {
-        this.pluginManager.removeGameObject(SPINE_GAME_OBJECT_TYPE, true, true);
-        this.pluginManager.removeGameObject(SPINE_CONTAINER_TYPE, true, true);
-        if (this.sceneRenderer) this.sceneRenderer.dispose();
-    }
+	game: Phaser.Game;
+	isWebGL: boolean;
+	gl: WebGLRenderingContext | null;
+	textureManager: Phaser.Textures.TextureManager;
+	phaserRenderer: Phaser.Renderer.Canvas.CanvasRenderer | Phaser.Renderer.WebGL.WebGLRenderer | null;
+	sceneRenderer: SceneRenderer | null;
+	skeletonDataCache: Phaser.Cache.BaseCache;
+	atlasCache: Phaser.Cache.BaseCache;
+
+	constructor (scene: Phaser.Scene, pluginManager: Phaser.Plugins.PluginManager, pluginKey: string) {
+		super(scene, pluginManager, pluginKey);
+		var game = this.game = pluginManager.game;
+		this.isWebGL = this.game.config.renderType === 2;
+		this.gl = this.isWebGL ? (this.game.renderer as Phaser.Renderer.WebGL.WebGLRenderer).gl : null;
+		this.textureManager = this.game.textures;
+		this.phaserRenderer = this.game.renderer;
+		this.sceneRenderer = null;
+		this.skeletonDataCache = this.game.cache.addCustom(SPINE_SKELETON_DATA_CACHE_KEY);
+		this.atlasCache = this.game.cache.addCustom(SPINE_ATLAS_CACHE_KEY);
+
+		if (!this.phaserRenderer) {
+			this.phaserRenderer = {
+				width: game.scale.width,
+				height: game.scale.height,
+				preRender: () => { },
+				postRender: () => { },
+				render: () => { },
+				destroy: () => { }
+			} as unknown as Phaser.Renderer.Canvas.CanvasRenderer;
+		}
+
+		let skeletonJsonFileCallback = function (this: any, key: string,
+			url: string,
+			xhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
+			let file = new SpineSkeletonDataFile(this as any, key, url, SpineSkeletonDataFileType.json, xhrSettings);
+			this.addFile(file.files);
+			return this;
+		};
+		pluginManager.registerFileType("spineJson", skeletonJsonFileCallback, scene);
+
+
+		let skeletonBinaryFileCallback = function (this: any, key: string,
+			url: string,
+			xhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
+			let file = new SpineSkeletonDataFile(this as any, key, url, SpineSkeletonDataFileType.binary, xhrSettings);
+			this.addFile(file.files);
+			return this;
+		};
+		pluginManager.registerFileType("spineBinary", skeletonBinaryFileCallback, scene);
+
+
+		let atlasFileCallback = function (this: any, key: string,
+			url: string,
+			premultipliedAlpha: boolean,
+			xhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
+			let file = new SpineAtlasFile(this as any, key, url, premultipliedAlpha, xhrSettings);
+			this.addFile(file.files);
+			return this;
+		};
+		pluginManager.registerFileType("spineAtlas", atlasFileCallback, scene);
+
+		let self = this;
+		let addSpineGameObject = function (this: Phaser.GameObjects.GameObjectFactory, x: number, y: number, dataKey: string, atlasKey: string) {
+			let gameObject = new SpineGameObject(scene, self, x, y, dataKey, atlasKey);
+			this.displayList.add(gameObject);
+			this.updateList.add(gameObject);
+			return gameObject;
+		};
+
+		let makeSpineGameObject = function (this: Phaser.GameObjects.GameObjectFactory, config: any, addToScene: boolean) {
+			let dataKey = config.dataKey ? config.dataKey : null;
+			let atlasKey = config.atlasKey ? config.atlasKey : null;
+			let gameObject = new SpineGameObject(this.scene, self, 0, 0, dataKey, atlasKey);
+			if (addToScene !== undefined) {
+				config.add = addToScene;
+			}
+			Phaser.GameObjects.BuildGameObject(this.scene, gameObject, config);
+		}
+		pluginManager.registerGameObject(SPINE_GAME_OBJECT_TYPE, addSpineGameObject, makeSpineGameObject);
+	}
+
+	boot () {
+		if (this.isWebGL) {
+			//  Monkeypatch the Spine setBlendMode functions, or batching is destroyed!
+			let setBlendMode = function (this: any, srcBlend: any, dstBlend: any) {
+				if (srcBlend !== this.srcBlend || dstBlend !== this.dstBlend) {
+					let gl = this.context.gl;
+					this.srcBlend = srcBlend;
+					this.dstBlend = dstBlend;
+					if (this.isDrawing) {
+						this.flush();
+						gl.blendFunc(this.srcBlend, this.dstBlend);
+					}
+				}
+			};
+
+			var sceneRenderer = this.sceneRenderer;
+			if (!sceneRenderer) {
+				sceneRenderer = new SceneRenderer((this.phaserRenderer! as Phaser.Renderer.WebGL.WebGLRenderer).canvas, this.gl!, true);
+				sceneRenderer.batcher.setBlendMode = setBlendMode;
+				(sceneRenderer as any).shapes.setBlendMode = setBlendMode;
+			}
+
+			this.sceneRenderer = sceneRenderer;
+		}
+
+		var eventEmitter = this.systems.events;
+		eventEmitter.once('shutdown', this.shutdown, this);
+		eventEmitter.once('destroy', this.destroy, this);
+		this.game.events.once('destroy', this.gameDestroy, this);
+	}
+
+	onResize () {
+		var phaserRenderer = this.phaserRenderer;
+		var sceneRenderer = this.sceneRenderer;
+
+		if (phaserRenderer && sceneRenderer) {
+			var viewportWidth = phaserRenderer.width;
+			var viewportHeight = phaserRenderer.height;
+			sceneRenderer.camera.position.x = viewportWidth / 2;
+			sceneRenderer.camera.position.y = viewportHeight / 2;
+			sceneRenderer.camera.setViewport(viewportWidth, viewportHeight);
+		}
+	}
+
+	shutdown () {
+		this.systems.events.off("shutdown", this.shutdown, this);
+		if (this.isWebGL) {
+			this.game.scale.off(Phaser.Scale.Events.RESIZE, this.onResize, this);
+		}
+	}
+
+	destroy () {
+		this.shutdown()
+	}
+
+	gameDestroy () {
+		this.pluginManager.removeGameObject(SPINE_GAME_OBJECT_TYPE, true, true);
+		this.pluginManager.removeGameObject(SPINE_CONTAINER_TYPE, true, true);
+		if (this.sceneRenderer) this.sceneRenderer.dispose();
+	}
+
+	createSkeleton (dataKey: string, atlasKey: string) {
+		let atlas: TextureAtlas;
+		if (this.atlasCache.exists(atlasKey)) {
+			atlas = this.atlasCache.get(atlasKey);
+		} else {
+			let atlasFile = this.game.cache.text.get(atlasKey) as string;
+			atlas = new TextureAtlas(atlasFile);
+			if (this.isWebGL) {
+				let gl = this.gl!;
+				gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
+				for (let atlasPage of atlas.pages) {
+					atlasPage.setTexture(new GLTexture(gl, this.textureManager.get(atlasKey + "!" + atlasPage.name).getSourceImage() as HTMLImageElement | ImageBitmap, false));
+				}
+			} else {
+				for (let atlasPage of atlas.pages) {
+					atlasPage.setTexture(new CanvasTexture(this.textureManager.get(atlasKey + "!" + atlasPage.name).getSourceImage() as HTMLImageElement | ImageBitmap));
+				}
+			}
+			this.atlasCache.add(atlasKey, atlas);
+		}
+
+		let skeletonData: SkeletonData;
+		if (this.skeletonDataCache.exists(dataKey)) {
+			skeletonData = this.skeletonDataCache.get(dataKey);
+		} else {
+			if (this.game.cache.json.exists(dataKey)) {
+				let jsonFile = this.game.cache.json.get(dataKey) as any;
+				let json = new SkeletonJson(new AtlasAttachmentLoader(atlas));
+				skeletonData = json.readSkeletonData(jsonFile);
+			} else {
+				let binaryFile = this.game.cache.binary.get(dataKey) as ArrayBuffer;
+				let binary = new SkeletonJson(new AtlasAttachmentLoader(atlas));
+				skeletonData = binary.readSkeletonData(binaryFile);
+			}
+			this.skeletonDataCache.add(dataKey, skeletonData);
+		}
+
+		return new Skeleton(skeletonData);
+	}
+}
+
+export enum SpineSkeletonDataFileType {
+	json,
+	binary
+}
+
+export class SpineSkeletonDataFile extends Phaser.Loader.MultiFile {
+	constructor (loader: Phaser.Loader.LoaderPlugin, key: string, url: string, public fileType: SpineSkeletonDataFileType, xhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
+		let file = null;
+		let isJson = fileType == SpineSkeletonDataFileType.json;
+		if (isJson) {
+			file = new Phaser.Loader.FileTypes.JSONFile(loader, {
+				key: key,
+				url: url,
+				extension: "json",
+				xhrSettings: xhrSettings,
+			} as Phaser.Types.Loader.FileTypes.JSONFileConfig);
+		} else {
+			file = new Phaser.Loader.FileTypes.BinaryFile(loader, {
+				key: key,
+				url: url,
+				extension: "skel",
+				xhrSettings: xhrSettings,
+			} as Phaser.Types.Loader.FileTypes.BinaryFileConfig);
+		}
+		super(loader, SPINE_SKELETON_DATA_FILE_TYPE, key, [file]);
+	}
+
+	onFileComplete (file: Phaser.Loader.File) {
+		this.pending--;
+	}
+
+	addToCache () {
+		if (this.isReadyToProcess()) this.files[0].addToCache();
+	}
 }
 
+export class SpineAtlasFile extends Phaser.Loader.MultiFile {
+	constructor (loader: Phaser.Loader.LoaderPlugin, key: string, url: string, public premultipliedAlpha: boolean, xhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
+		super(loader, SPINE_ATLAS_FILE_TYPE, key, [
+			new Phaser.Loader.FileTypes.TextFile(loader, {
+				key: key,
+				url: url,
+				xhrSettings: xhrSettings,
+				extension: "atlas"
+			})
+		]);
+	}
+
+	onFileComplete (file: Phaser.Loader.File) {
+		if (this.files.indexOf(file) != -1) {
+			this.pending--;
+
+			if (file.type == "text") {
+				var lines = file.data.split('\n');
+				let textures = [];
+				textures.push(lines[0]);
+				for (var t = 1; t < lines.length; t++) {
+					var line = lines[t];
+					if (line.trim() === '' && t < lines.length - 1) {
+						line = lines[t + 1];
+						textures.push(line);
+					}
+				}
+
+				let basePath = file.src.match(/^.*\//);
+				for (var i = 0; i < textures.length; i++) {
+					var url = basePath + textures[i];
+					var key = file.key + "!" + textures[i];
+					var image = new Phaser.Loader.FileTypes.ImageFile(this.loader, key, url);
+
+					if (!this.loader.keyExists(image)) {
+						this.addToMultiFile(image);
+						this.loader.addFile(image);
+					}
+				}
+			}
+		}
+	}
+
+	addToCache () {
+		if (this.isReadyToProcess()) {
+			let textureManager = this.loader.textureManager;
+			for (let file of this.files) {
+				if (file.type == "image") {
+					if (!textureManager.exists(file.key)) {
+						textureManager.addImage(file.key, file.data);
+					}
+				} else {
+					file.addToCache();
+				}
+			}
+		}
+	}
+}

+ 0 - 1
spine-ts/spine-phaser/src/index.ts

@@ -1,6 +1,5 @@
 export * from "./require-shim"
 export * from "./SpinePlugin"
-export * from "./SpineFile"
 export * from "./mixins"
 export * from "@esotericsoftware/spine-core";
 export * from "@esotericsoftware/spine-canvas";

+ 4 - 2
spine-ts/spine-phaser/src/keys.ts

@@ -1,6 +1,8 @@
+export const SPINE_SKELETON_FILE_CACHE_KEY = "esotericsoftware.spine.skeletonFile.cache";
 export const SPINE_ATLAS_CACHE_KEY = "esotericsoftware.spine.atlas.cache";
-export const SPINE_TEXTURE_CACHE_KEY = "esotericsoftware.spine.texture.cache";
+export const SPINE_ATLAS_TEXTURE_CACHE_KEY = "esotericsoftware.spine.atlas.texture.cache";
 export const SPINE_LOADER_TYPE = "spine";
-export const SPINE_FILE_TYPE = "spine";
+export const SPINE_SKELETON_DATA_FILE_TYPE = "spineSkeletonData";
+export const SPINE_ATLAS_FILE_TYPE = "spineAtlasData";
 export const SPINE_GAME_OBJECT_TYPE = "spine";
 export const SPINE_CONTAINER_TYPE = "spineContainer";

+ 26 - 26
spine-ts/spine-phaser/src/mixins.ts

@@ -33,41 +33,41 @@ export const Transform = components.Transform;
 export const Visible = components.Visible;
 
 export interface Type<
-    T,
-    P extends any[] = any[]
-> extends Function {
-    new(...args: P): T;
+	T,
+	P extends any[] = any[]
+	> extends Function {
+	new(...args: P): T;
 }
 
 export type Mixin<GameObjectComponent, GameObjectConstraint extends Phaser.GameObjects.GameObject> = <
-    GameObjectType extends Type<GameObjectConstraint>
->(
-    BaseGameObject: GameObjectType
+	GameObjectType extends Type<GameObjectConstraint>
+	>(
+	BaseGameObject: GameObjectType
 ) => GameObjectType & Type<GameObjectComponent>;
 
 export function createMixin<
-    GameObjectComponent,
-    GameObjectConstraint extends Phaser.GameObjects.GameObject = Phaser.GameObjects.GameObject
->(
-    ...component: GameObjectComponent[]
+	GameObjectComponent,
+	GameObjectConstraint extends Phaser.GameObjects.GameObject = Phaser.GameObjects.GameObject
+> (
+	...component: GameObjectComponent[]
 ): Mixin<GameObjectComponent, GameObjectConstraint> {
-    return (BaseGameObject) => {
-        applyMixins(BaseGameObject, component);
-        return BaseGameObject as any;
-    };
+	return (BaseGameObject) => {
+		applyMixins(BaseGameObject, component);
+		return BaseGameObject as any;
+	};
 }
 
-function applyMixins(derivedCtor: any, constructors: any[]) {
-    constructors.forEach((baseCtor) => {
-        Object.getOwnPropertyNames(baseCtor.prototype || baseCtor).forEach((name) => {
-            Object.defineProperty(
-                derivedCtor.prototype,
-                name,
-                Object.getOwnPropertyDescriptor(baseCtor.prototype || baseCtor, name) ||
-                Object.create(null)
-            );
-        });
-    });
+function applyMixins (derivedCtor: any, constructors: any[]) {
+	constructors.forEach((baseCtor) => {
+		Object.getOwnPropertyNames(baseCtor.prototype || baseCtor).forEach((name) => {
+			Object.defineProperty(
+				derivedCtor.prototype,
+				name,
+				Object.getOwnPropertyDescriptor(baseCtor.prototype || baseCtor, name) ||
+				Object.create(null)
+			);
+		});
+	});
 }
 
 type ComputedSizeMixin = Mixin<Phaser.GameObjects.Components.Transform, Phaser.GameObjects.GameObject>;

+ 6 - 6
spine-ts/spine-phaser/src/require-shim.ts

@@ -2,10 +2,10 @@ declare global {
 	var require: any;
 }
 if (window.Phaser) {
-    let prevRequire = window.require;
-    window.require = (x: string) => {
-        if (prevRequire) return prevRequire(x);
-        else if (x === "Phaser") return window.Phaser;
-    }
+	let prevRequire = window.require;
+	window.require = (x: string) => {
+		if (prevRequire) return prevRequire(x);
+		else if (x === "Phaser") return window.Phaser;
+	}
 }
-export {}
+export { }