Browse Source

[ts] More demo work

badlogic 9 years ago
parent
commit
348b11e275

+ 28 - 0
spine-ts/build/spine-all.d.ts

@@ -785,6 +785,16 @@ declare module spine {
         length(): number;
         normalize(): this;
     }
+    class TimeKeeper {
+        maxDelta: number;
+        framesPerSecond: number;
+        delta: number;
+        totalTime: number;
+        private lastTime;
+        private frameCount;
+        private frameTime;
+        update(): void;
+    }
 }
 declare module spine {
     abstract class Attachment {
@@ -1026,6 +1036,23 @@ declare module spine.webgl {
         dragged(x: number, y: number): void;
     }
 }
+declare module spine.webgl {
+    class LoadingScreen {
+        private renderer;
+        private logo;
+        private spinner;
+        private angle;
+        private timeKeeper;
+        backgroundColor: Color;
+        static useDark: boolean;
+        private static SPINNER_DATA;
+        private static SPINNER_DARK_DATA;
+        private static SPINE_LOGO_DATA;
+        private static SPINE_LOGO_DARK_DATA;
+        constructor(renderer: SceneRenderer);
+        draw(): void;
+    }
+}
 declare module spine.webgl {
     const M00: number;
     const M01: number;
@@ -1163,6 +1190,7 @@ declare module spine.webgl {
         drawSkeleton(skeleton: Skeleton, premultipliedAlpha?: boolean): void;
         drawSkeletonDebug(skeleton: Skeleton, premultipliedAlpha?: boolean, ignoredBones?: Array<string>): void;
         drawTexture(texture: GLTexture, x: number, y: number, width: number, height: number, color?: Color): void;
+        drawTextureRotated(texture: GLTexture, x: number, y: number, width: number, height: number, pivotX: number, pivotY: number, angle: number, color?: Color, premultipliedAlpha?: boolean): void;
         drawRegion(region: TextureAtlasRegion, x: number, y: number, width: number, height: number, color?: Color, premultipliedAlpha?: boolean): void;
         line(x: number, y: number, x2: number, y2: number, color?: Color, color2?: Color): void;
         triangle(filled: boolean, x: number, y: number, x2: number, y2: number, x3: number, y3: number, color?: Color, color2?: Color, color3?: Color): void;

File diff suppressed because it is too large
+ 82 - 0
spine-ts/build/spine-all.js


File diff suppressed because it is too large
+ 0 - 0
spine-ts/build/spine-all.js.map


+ 10 - 0
spine-ts/build/spine-core.d.ts

@@ -759,6 +759,16 @@ declare module spine {
         length(): number;
         normalize(): this;
     }
+    class TimeKeeper {
+        maxDelta: number;
+        framesPerSecond: number;
+        delta: number;
+        totalTime: number;
+        private lastTime;
+        private frameCount;
+        private frameTime;
+        update(): void;
+    }
 }
 declare module spine {
     abstract class Attachment {

+ 28 - 0
spine-ts/build/spine-core.js

@@ -3991,6 +3991,34 @@ var spine;
         return Vector2;
     }());
     spine.Vector2 = Vector2;
+    var TimeKeeper = (function () {
+        function TimeKeeper() {
+            this.maxDelta = 0.064;
+            this.framesPerSecond = 0;
+            this.delta = 0;
+            this.totalTime = 0;
+            this.lastTime = Date.now() / 1000;
+            this.frameCount = 0;
+            this.frameTime = 0;
+        }
+        TimeKeeper.prototype.update = function () {
+            var now = Date.now() / 1000;
+            this.delta = now - this.lastTime;
+            this.frameTime += this.delta;
+            this.totalTime += this.delta;
+            if (this.delta > this.maxDelta)
+                this.delta = this.maxDelta;
+            this.lastTime = now;
+            this.frameCount++;
+            if (this.frameTime > 1) {
+                this.framesPerSecond = this.frameCount / this.frameTime;
+                this.frameTime = 0;
+                this.frameCount = 0;
+            }
+        };
+        return TimeKeeper;
+    }());
+    spine.TimeKeeper = TimeKeeper;
 })(spine || (spine = {}));
 var spine;
 (function (spine) {

File diff suppressed because it is too large
+ 0 - 0
spine-ts/build/spine-core.js.map


+ 10 - 0
spine-ts/build/spine-threejs.d.ts

@@ -759,6 +759,16 @@ declare module spine {
         length(): number;
         normalize(): this;
     }
+    class TimeKeeper {
+        maxDelta: number;
+        framesPerSecond: number;
+        delta: number;
+        totalTime: number;
+        private lastTime;
+        private frameCount;
+        private frameTime;
+        update(): void;
+    }
 }
 declare module spine {
     abstract class Attachment {

+ 28 - 0
spine-ts/build/spine-threejs.js

@@ -3991,6 +3991,34 @@ var spine;
         return Vector2;
     }());
     spine.Vector2 = Vector2;
+    var TimeKeeper = (function () {
+        function TimeKeeper() {
+            this.maxDelta = 0.064;
+            this.framesPerSecond = 0;
+            this.delta = 0;
+            this.totalTime = 0;
+            this.lastTime = Date.now() / 1000;
+            this.frameCount = 0;
+            this.frameTime = 0;
+        }
+        TimeKeeper.prototype.update = function () {
+            var now = Date.now() / 1000;
+            this.delta = now - this.lastTime;
+            this.frameTime += this.delta;
+            this.totalTime += this.delta;
+            if (this.delta > this.maxDelta)
+                this.delta = this.maxDelta;
+            this.lastTime = now;
+            this.frameCount++;
+            if (this.frameTime > 1) {
+                this.framesPerSecond = this.frameCount / this.frameTime;
+                this.frameTime = 0;
+                this.frameCount = 0;
+            }
+        };
+        return TimeKeeper;
+    }());
+    spine.TimeKeeper = TimeKeeper;
 })(spine || (spine = {}));
 var spine;
 (function (spine) {

File diff suppressed because it is too large
+ 0 - 0
spine-ts/build/spine-threejs.js.map


+ 28 - 0
spine-ts/build/spine-webgl.d.ts

@@ -759,6 +759,16 @@ declare module spine {
         length(): number;
         normalize(): this;
     }
+    class TimeKeeper {
+        maxDelta: number;
+        framesPerSecond: number;
+        delta: number;
+        totalTime: number;
+        private lastTime;
+        private frameCount;
+        private frameTime;
+        update(): void;
+    }
 }
 declare module spine {
     abstract class Attachment {
@@ -956,6 +966,23 @@ declare module spine.webgl {
         dragged(x: number, y: number): void;
     }
 }
+declare module spine.webgl {
+    class LoadingScreen {
+        private renderer;
+        private logo;
+        private spinner;
+        private angle;
+        private timeKeeper;
+        backgroundColor: Color;
+        static useDark: boolean;
+        private static SPINNER_DATA;
+        private static SPINNER_DARK_DATA;
+        private static SPINE_LOGO_DATA;
+        private static SPINE_LOGO_DARK_DATA;
+        constructor(renderer: SceneRenderer);
+        draw(): void;
+    }
+}
 declare module spine.webgl {
     const M00: number;
     const M01: number;
@@ -1093,6 +1120,7 @@ declare module spine.webgl {
         drawSkeleton(skeleton: Skeleton, premultipliedAlpha?: boolean): void;
         drawSkeletonDebug(skeleton: Skeleton, premultipliedAlpha?: boolean, ignoredBones?: Array<string>): void;
         drawTexture(texture: GLTexture, x: number, y: number, width: number, height: number, color?: Color): void;
+        drawTextureRotated(texture: GLTexture, x: number, y: number, width: number, height: number, pivotX: number, pivotY: number, angle: number, color?: Color, premultipliedAlpha?: boolean): void;
         drawRegion(region: TextureAtlasRegion, x: number, y: number, width: number, height: number, color?: Color, premultipliedAlpha?: boolean): void;
         line(x: number, y: number, x2: number, y2: number, color?: Color, color2?: Color): void;
         triangle(filled: boolean, x: number, y: number, x2: number, y2: number, x3: number, y3: number, color?: Color, color2?: Color, color3?: Color): void;

File diff suppressed because it is too large
+ 82 - 0
spine-ts/build/spine-webgl.js


File diff suppressed because it is too large
+ 0 - 0
spine-ts/build/spine-webgl.js.map


+ 28 - 0
spine-ts/build/spine-widget.d.ts

@@ -759,6 +759,16 @@ declare module spine {
         length(): number;
         normalize(): this;
     }
+    class TimeKeeper {
+        maxDelta: number;
+        framesPerSecond: number;
+        delta: number;
+        totalTime: number;
+        private lastTime;
+        private frameCount;
+        private frameTime;
+        update(): void;
+    }
 }
 declare module spine {
     abstract class Attachment {
@@ -956,6 +966,23 @@ declare module spine.webgl {
         dragged(x: number, y: number): void;
     }
 }
+declare module spine.webgl {
+    class LoadingScreen {
+        private renderer;
+        private logo;
+        private spinner;
+        private angle;
+        private timeKeeper;
+        backgroundColor: Color;
+        static useDark: boolean;
+        private static SPINNER_DATA;
+        private static SPINNER_DARK_DATA;
+        private static SPINE_LOGO_DATA;
+        private static SPINE_LOGO_DARK_DATA;
+        constructor(renderer: SceneRenderer);
+        draw(): void;
+    }
+}
 declare module spine.webgl {
     const M00: number;
     const M01: number;
@@ -1093,6 +1120,7 @@ declare module spine.webgl {
         drawSkeleton(skeleton: Skeleton, premultipliedAlpha?: boolean): void;
         drawSkeletonDebug(skeleton: Skeleton, premultipliedAlpha?: boolean, ignoredBones?: Array<string>): void;
         drawTexture(texture: GLTexture, x: number, y: number, width: number, height: number, color?: Color): void;
+        drawTextureRotated(texture: GLTexture, x: number, y: number, width: number, height: number, pivotX: number, pivotY: number, angle: number, color?: Color, premultipliedAlpha?: boolean): void;
         drawRegion(region: TextureAtlasRegion, x: number, y: number, width: number, height: number, color?: Color, premultipliedAlpha?: boolean): void;
         line(x: number, y: number, x2: number, y2: number, color?: Color, color2?: Color): void;
         triangle(filled: boolean, x: number, y: number, x2: number, y2: number, x3: number, y3: number, color?: Color, color2?: Color, color3?: Color): void;

File diff suppressed because it is too large
+ 82 - 0
spine-ts/build/spine-widget.js


File diff suppressed because it is too large
+ 0 - 0
spine-ts/build/spine-widget.js.map


+ 27 - 0
spine-ts/core/src/Utils.ts

@@ -230,4 +230,31 @@ module spine {
 			return this;
 		}
 	}
+
+	export class TimeKeeper {
+		maxDelta = 0.064;		
+		framesPerSecond = 0;
+		delta = 0;
+		totalTime = 0;
+
+		private lastTime = Date.now() / 1000;
+		private frameCount = 0;
+		private frameTime = 0;
+
+		update () {
+			var now = Date.now() / 1000;
+			this.delta = now -this.lastTime;
+			this.frameTime += this.delta;
+			this.totalTime += this.delta;
+			if (this.delta > this.maxDelta) this.delta = this.maxDelta;
+			this.lastTime = now;
+			
+			this.frameCount++;
+			if (this.frameTime > 1) {
+				this.framesPerSecond = this.frameCount / this.frameTime;
+				this.frameTime = 0;
+				this.frameCount = 0;
+			}
+		}
+	}
 }

+ 37 - 23
spine-ts/webgl/demos/animationmixing.js

@@ -1,10 +1,13 @@
-var animationMixingDemo = function(pathPrefix, loadingComplete) {
+var animationMixingDemo = function(pathPrefix, loadingComplete, bgColor) {
 	var OUTLINE_COLOR = new spine.Color(0, 0.8, 0, 1);	
 
 	var canvas, gl, renderer, input, assetManager;
 	var skeleton, skeletonNoMix, state, stateNoMix, bounds;
 	var timeSlider, timeSliderLabel;
-	var lastFrameTime = Date.now() / 1000	
+	var timeKeeper;
+	var loadingScreen;
+
+	if (!bgColor) bgColor = new spine.Color(0, 0, 0, 1);
 
 	function init () {
 		timeSlider = $("#animationmixingdemo-timeslider");
@@ -15,25 +18,21 @@ var animationMixingDemo = function(pathPrefix, loadingComplete) {
 		gl = canvas.getContext("webgl", { alpha: false }) || canvas.getContext("experimental-webgl", { alpha: false });	
 
 		renderer = new spine.webgl.SceneRenderer(canvas, gl);
-		assetManager = new spine.webgl.AssetManager(gl, pathPrefix);		
+		assetManager = new spine.webgl.AssetManager(gl, pathPrefix);
+		
 		assetManager.loadTexture("spineboy.png");
 		assetManager.loadText("spineboy.json");
-		assetManager.loadText("spineboy.atlas");		
-		requestAnimationFrame(load);
-
+		assetManager.loadText("spineboy.atlas");
+	
+		requestAnimationFrame(load);		
 		input = new spine.webgl.Input(canvas);
-		input.addListener({
-			down: function(x, y) {
-				state.setAnimation(1, "shoot", false);
-				stateNoMix.setAnimation(1, "shoot", false);	
-			},
-			up: function(x, y) { },
-			moved: function(x, y) {	},
-			dragged: function(x, y) { }
-		});
-	}	
+		timeKeeper = new spine.TimeKeeper();		
+		loadingScreen = new spine.webgl.LoadingScreen(renderer);
+		loadingScreen.backgroundColor = bgColor;	
+	}
 
 	function load () {
+		timeKeeper.update();
 		if (assetManager.isLoadingComplete()) {
 			skeleton = loadSkeleton("spineboy");
 			skeletonNoMix = new spine.Skeleton(skeleton.data);					
@@ -46,8 +45,26 @@ var animationMixingDemo = function(pathPrefix, loadingComplete) {
 			skeleton.updateWorldTransform();
 			bounds = { offset: new spine.Vector2(), size: new spine.Vector2() };
 			skeleton.getBounds(bounds.offset, bounds.size);
+			setupInput();
+			$("#animationmixingdemo-overlay").removeClass("overlay-hide");
+			$("#animationmixingdemo-overlay").addClass("overlay");	
 			loadingComplete(canvas, render);						
-		} else requestAnimationFrame(load);
+		} else {
+			loadingScreen.draw();			
+			requestAnimationFrame(load);
+		}
+	}
+
+	function setupInput() {
+		input.addListener({
+			down: function(x, y) {
+				state.setAnimation(1, "shoot", false);
+				stateNoMix.setAnimation(1, "shoot", false);	
+			},
+			up: function(x, y) { },
+			moved: function(x, y) {	},
+			dragged: function(x, y) { }
+		});
 	}
 
 	function createState(mix) {
@@ -90,11 +107,8 @@ var animationMixingDemo = function(pathPrefix, loadingComplete) {
 	}
 
 	function render () {
-		var now = Date.now() / 1000;
-		var delta = now - lastFrameTime;
-		lastFrameTime = now;
-		if (delta > 0.032) delta = 0.032;
-		delta *= (timeSlider.slider("value") / 100);
+		timeKeeper.update();
+		var delta = timeKeeper.delta * (timeSlider.slider("value") / 100);
 		if (timeSliderLabel) timeSliderLabel.text(timeSlider.slider("value") + "%");	
 
 		var offset = bounds.offset;
@@ -106,7 +120,7 @@ var animationMixingDemo = function(pathPrefix, loadingComplete) {
 		renderer.camera.viewportHeight = size.y * 1.2;
 		renderer.resize(spine.webgl.ResizeMode.Fit);
 
-		gl.clearColor(0.2, 0.2, 0.2, 1);
+		gl.clearColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a);
 		gl.clear(gl.COLOR_BUFFER_BIT);		
 
 		renderer.begin();

+ 7 - 1
spine-ts/webgl/demos/ikconstraint.html

@@ -1,5 +1,9 @@
 <html>
 <script src="../../build/spine-webgl.js"></script>
+<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
+<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.0/themes/base/jquery-ui.css">
+<script src="https://code.jquery.com/ui/1.12.0/jquery-ui.js"></script>
+<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js"></script>
 <style>
 	* { margin: 0; padding: 0; }
 	body, html { height: 100% }
@@ -7,7 +11,9 @@
 </style>
 <body>
 <canvas id="ikdemo-canvas"></canvas>
-<center><div id="info" style="color: #f00; position: fixed; top: 0; width: 100%"></div></center>
+<center>
+<div>Display Bones</div><input id="stretchydemo-drawbones" type="checkbox"></input>
+</center>
 <script src="utils.js"></script>
 <script src="ikconstraint.js"></script>
 <script>

+ 74 - 34
spine-ts/webgl/demos/ikconstraint.js

@@ -1,11 +1,20 @@
-var ikConstraintDemo = function(pathPrefix, loadingComplete) {
+var ikConstraintDemo = function(pathPrefix, loadingComplete, bgColor) {
+	var COLOR_INNER = new spine.Color(0.8, 0, 0, 0.5);
+	var COLOR_OUTER = new spine.Color(0.8, 0, 0, 0.8);
+	var COLOR_INNER_SELECTED = new spine.Color(0.0, 0, 0.8, 0.5);
+	var COLOR_OUTER_SELECTED = new spine.Color(0.0, 0, 0.8, 0.8);
+
 	var canvas, gl, renderer, input, assetManager;
 	var skeleton, bounds;		
-	var lastFrameTime = Date.now() / 1000;
-	var target = null;
-	var isHover = false;
-	var boneName = "hip";
-	var coords = new spine.webgl.Vector3(), temp = new spine.webgl.Vector3(), temp2 = new spine.Vector2();		
+	var timeKeeper, loadingScreen;
+	var target = null;	
+	var hoverTargets = [];
+	var controlBones = ["hoverboard controller", "hip", "board target"];
+	var coords = new spine.webgl.Vector3(), temp = new spine.webgl.Vector3(), temp2 = new spine.Vector2(), temp3 = new spine.webgl.Vector3();
+	var kneePos = new spine.Vector2();
+	var playButton, timeLine, spacing, isPlaying = true, playTime = 0;
+
+	if (!bgColor) bgColor = new spine.Color(1, 1, 1, 1);	
 
 	function init () {
 
@@ -17,43 +26,68 @@ var ikConstraintDemo = function(pathPrefix, loadingComplete) {
 		assetManager = new spine.webgl.AssetManager(gl, pathPrefix);
 		input = new spine.webgl.Input(canvas);		
 		assetManager.loadTexture("spineboy.png");
-		assetManager.loadText("spineboy-mesh.json");
+		assetManager.loadText("spineboy-hover.json");
 		assetManager.loadText("spineboy.atlas");
+		timeKeeper = new spine.TimeKeeper();		
+		loadingScreen = new spine.webgl.LoadingScreen(renderer);
+		loadingScreen.backgroundColor = bgColor;
 		requestAnimationFrame(load);
 	}
 
 	function load () {
+		timeKeeper.update();
 		if (assetManager.isLoadingComplete()) {
 			var atlas = new spine.TextureAtlas(assetManager.get("spineboy.atlas"), function(path) {
 				return assetManager.get(path);		
 			});
 			var atlasLoader = new spine.TextureAtlasAttachmentLoader(atlas);
 			var skeletonJson = new spine.SkeletonJson(atlasLoader);
-			var skeletonData = skeletonJson.readSkeletonData(assetManager.get("spineboy-mesh.json"));
+			var skeletonData = skeletonJson.readSkeletonData(assetManager.get("spineboy-hover.json"));
 			skeleton = new spine.Skeleton(skeletonData);
 			skeleton.setToSetupPose();
 			skeleton.updateWorldTransform();
 			var offset = new spine.Vector2();
 			bounds = new spine.Vector2();
 			skeleton.getBounds(offset, bounds);
+			for (var i = 0; i < controlBones.length; i++) hoverTargets.push(null);			
 
 			renderer.camera.position.x = offset.x + bounds.x / 2;
 			renderer.camera.position.y = offset.y + bounds.y / 2;
 
+			renderer.skeletonDebugRenderer.drawMeshHull = false;
+			renderer.skeletonDebugRenderer.drawMeshTriangles = false;
+
+			setupUI();
 			setupInput();
 
 			loadingComplete(canvas, render);
-		} else requestAnimationFrame(load);
+		} else {
+			loadingScreen.draw();
+			requestAnimationFrame(load);
+		}
+	}
+
+	function setupUI() {		
+		var checkbox = $("#ikdemo-drawbones");
+		renderer.skeletonDebugRenderer.drawRegionAttachments = false;
+		renderer.skeletonDebugRenderer.drawPaths = false;
+		renderer.skeletonDebugRenderer.drawBones = false;
+		checkbox.change(function() {
+			renderer.skeletonDebugRenderer.drawPaths = this.checked;
+			renderer.skeletonDebugRenderer.drawBones = this.checked;			
+		});
 	}
 
-	function setupInput() {
+	function setupInput (){
 		input.addListener({
-			down: function(x, y) {			
-				var bone = skeleton.findBone(boneName);				
-				renderer.camera.screenToWorld(coords.set(x, y, 0), canvas.width, canvas.height);				
-				if (temp.set(skeleton.x + bone.worldX, skeleton.y + bone.worldY, 0).distance(coords) < 20) {
-					target = bone;
-				}				
+			down: function(x, y) {
+				for (var i = 0; i < controlBones.length; i++) {	
+					var bone = skeleton.findBone(controlBones[i]);				
+					renderer.camera.screenToWorld(coords.set(x, y, 0), canvas.width, canvas.height);				
+					if (temp.set(skeleton.x + bone.worldX, skeleton.y + bone.worldY, 0).distance(coords) < 20) {
+						target = bone;
+					}				
+				}
 			},
 			up: function(x, y) {
 				target = null;
@@ -72,40 +106,46 @@ var ikConstraintDemo = function(pathPrefix, loadingComplete) {
 				}
 			},
 			moved: function (x, y) { 
-				var bone = skeleton.findBone(boneName);				
-				renderer.camera.screenToWorld(coords.set(x, y, 0), canvas.width, canvas.height);				
-				isHover = temp.set(skeleton.x + bone.worldX, skeleton.y + bone.worldY, 0).distance(coords) < 20;					
+				for (var i = 0; i < controlBones.length; i++) {	
+					var bone = skeleton.findBone(controlBones[i]);				
+					renderer.camera.screenToWorld(coords.set(x, y, 0), canvas.width, canvas.height);				
+					if (temp.set(skeleton.x + bone.worldX, skeleton.y + bone.worldY, 0).distance(coords) < 20) {
+						hoverTargets[i] = bone;
+					} else {
+						hoverTargets[i] = null;
+					}
+				}	
 			}
 		});
 	}
 
 	function render () {
-		var now = Date.now() / 1000;
-		var delta = now - lastFrameTime;
-		lastFrameTime = now;
-		if (delta > 0.032) delta = 0.032;
+		timeKeeper.update();
+		var delta = timeKeeper.delta;	
+
+		skeleton.updateWorldTransform();
 
 		renderer.camera.viewportWidth = bounds.x * 1.2;
 		renderer.camera.viewportHeight = bounds.y * 1.2;
 		renderer.resize(spine.webgl.ResizeMode.Fit);
 
-		gl.clearColor(0.2, 0.2, 0.2, 1);
-		gl.clear(gl.COLOR_BUFFER_BIT);
-
-		skeleton.updateWorldTransform();
+		gl.clearColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a);
+		gl.clear(gl.COLOR_BUFFER_BIT);			
 
 		renderer.begin();				
 		renderer.drawSkeleton(skeleton, true);
-		var bone = skeleton.findBone(boneName);
-
-		var colorInner = isHover ? spineDemos.HOVER_COLOR_INNER : spineDemos.NON_HOVER_COLOR_INNER;
-		var colorOuter = isHover ? spineDemos.HOVER_COLOR_OUTER : spineDemos.NON_HOVER_COLOR_OUTER;
-
-		renderer.circle(true, skeleton.x + bone.worldX, skeleton.y + bone.worldY, 20, colorInner);
+		renderer.drawSkeletonDebug(skeleton, false, ["root"]);
 		gl.lineWidth(2);
-		renderer.circle(false, skeleton.x + bone.worldX, skeleton.y + bone.worldY, 20, colorOuter);			
+		for (var i = 0; i < controlBones.length; i++) {		
+			var bone = skeleton.findBone(controlBones[i]);
+			var colorInner = hoverTargets[i] !== null ? spineDemos.HOVER_COLOR_INNER : spineDemos.NON_HOVER_COLOR_INNER;
+			var colorOuter = hoverTargets[i] !== null ? spineDemos.HOVER_COLOR_OUTER : spineDemos.NON_HOVER_COLOR_OUTER;
+			renderer.circle(true, skeleton.x + bone.worldX, skeleton.y + bone.worldY, 20, colorInner);			
+			renderer.circle(false, skeleton.x + bone.worldX, skeleton.y + bone.worldY, 20, colorOuter);			
+		}
 		renderer.end();
 		gl.lineWidth(1);
 	}
+
 	init();
 };

+ 15 - 9
spine-ts/webgl/demos/imagesequences.js

@@ -1,14 +1,15 @@
-var imageSequencesDemo = function(pathPrefix, loadingComplete) {
+var imageSequencesDemo = function(pathPrefix, loadingComplete, bgColor) {
 	var OUTLINE_COLOR = new spine.Color(0, 0.8, 0, 1);	
 
 	var canvas, gl, renderer, input, assetManager;
 	var skeleton, bounds;		
-	var lastFrameTime = Date.now() / 1000;
+	var timeKeeper, loadingScreen;
 	var skeletons = {};
 	var activeSkeleton = "alien";
-
 	var playButton, timeLine, isPlaying = true;
 
+	if (!bgColor) bgColor = new spine.Color(0, 0, 0, 1);
+
 	function init () {
 		canvas = document.getElementById("imagesequencesdemo-canvas");
 		canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight;
@@ -22,16 +23,23 @@ var imageSequencesDemo = function(pathPrefix, loadingComplete) {
 		assetManager.loadTexture("dragon.png");		
 		assetManager.loadText("dragon.json");
 		assetManager.loadText("dragon.atlas");
+		timeKeeper = new spine.TimeKeeper();		
+		loadingScreen = new spine.webgl.LoadingScreen(renderer);
+		loadingScreen.backgroundColor = bgColor;
 		requestAnimationFrame(load);
 	}	
 
 	function load () {
+		timeKeeper.update();
 		if (assetManager.isLoadingComplete()) {
 			skeletons["alien"] = loadSkeleton("alien", "death", ["head", "splat01"]);
 			skeletons["dragon"] = loadSkeleton("dragon", "flying", ["R_wing"])
 			setupUI();
 			loadingComplete(canvas, render);			
-		} else requestAnimationFrame(load);
+		} else {
+			loadingScreen.draw();
+			requestAnimationFrame(load);
+		}
 	}
 
 	function setupUI() {
@@ -127,10 +135,8 @@ var imageSequencesDemo = function(pathPrefix, loadingComplete) {
 	}
 
 	function render () {
-		var now = Date.now() / 1000;
-		var delta = now - lastFrameTime;
-		lastFrameTime = now;	
-		if (delta > 0.032) delta = 0.032;	
+		timeKeeper.update();
+		var delta = timeKeeper.delta;	
 
 		var active = skeletons[activeSkeleton];
 		var skeleton = active.skeleton;
@@ -144,7 +150,7 @@ var imageSequencesDemo = function(pathPrefix, loadingComplete) {
 		renderer.camera.viewportHeight = size.y * 1.4;
 		renderer.resize(spine.webgl.ResizeMode.Fit);
 
-		gl.clearColor(0.2, 0.2, 0.2, 1);
+		gl.clearColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a);
 		gl.clear(gl.COLOR_BUFFER_BIT);
 
 		if (isPlaying) {

+ 15 - 9
spine-ts/webgl/demos/meshes.js

@@ -1,12 +1,13 @@
-var meshesDemo = function(pathPrefix, loadingComplete) {
+var meshesDemo = function(pathPrefix, loadingComplete, bgColor) {
 	var canvas, gl, renderer, input, assetManager;
 	var skeleton, bounds;		
-	var lastFrameTime = Date.now() / 1000;
+	var timeKeeper, loadingScreen;
 	var skeletons = {};
 	var activeSkeleton = "Orange Girl";
-
 	var playButton, timeLine, isPlaying = true;
 
+	if (!bgColor) bgColor = new spine.Color(0, 0, 0, 1);
+
 	function init () {
 		canvas = document.getElementById("meshesdemo-canvas");
 		canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight;
@@ -24,17 +25,24 @@ var meshesDemo = function(pathPrefix, loadingComplete) {
 		assetManager.loadTexture("armorgirl.png");		
 		assetManager.loadText("armorgirl.json");
 		assetManager.loadText("armorgirl.atlas");
+		timeKeeper = new spine.TimeKeeper();		
+		loadingScreen = new spine.webgl.LoadingScreen(renderer);
+		loadingScreen.backgroundColor = bgColor;
 		requestAnimationFrame(load);
 	}	
 
 	function load () {
+		timeKeeper.update();
 		if (assetManager.isLoadingComplete()) {
 			skeletons["Orange Girl"] = loadSkeleton("orangegirl", "animation");
 			skeletons["Green Girl"] = loadSkeleton("greengirl", "animation");
 			skeletons["Armor Girl"] = loadSkeleton("armorgirl", "animation");
 			setupUI();
 			loadingComplete(canvas, render);			
-		} else requestAnimationFrame(load);
+		} else {
+			loadingScreen.draw();
+			requestAnimationFrame(load);
+		}
 	}
 
 	function setupUI() {
@@ -124,10 +132,8 @@ var meshesDemo = function(pathPrefix, loadingComplete) {
 	}
 
 	function render () {
-		var now = Date.now() / 1000;
-		var delta = now - lastFrameTime;
-		lastFrameTime = now;	
-		if (delta > 0.032) delta = 0.032;	
+		timeKeeper.update();
+		var delta = timeKeeper.delta;	
 
 		var active = skeletons[activeSkeleton];
 		var skeleton = active.skeleton;
@@ -141,7 +147,7 @@ var meshesDemo = function(pathPrefix, loadingComplete) {
 		renderer.camera.viewportHeight = size.y * 1.2;
 		renderer.resize(spine.webgl.ResizeMode.Fit);
 
-		gl.clearColor(0.2, 0.2, 0.2, 1);
+		gl.clearColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a);
 		gl.clear(gl.COLOR_BUFFER_BIT);
 
 		if (isPlaying) {

+ 16 - 9
spine-ts/webgl/demos/skins.js

@@ -1,8 +1,10 @@
-var skinsDemo = function(pathPrefix, loadingComplete) {	
+var skinsDemo = function(pathPrefix, loadingComplete, bgColor) {	
 	var canvas, gl, renderer, input, assetManager;
 	var skeleton, state, offset, bounds;		
-	var lastFrameTime = Date.now() / 1000;	
-	var playButton, timeLine, isPlaying = true, playTime = 0;		
+	var timeKeeper, loadingScreen;
+	var playButton, timeLine, isPlaying = true, playTime = 0;
+
+	if (!bgColor) bgColor = new spine.Color(0, 0, 0, 1);		
 
 	function init () {
 		if (pathPrefix === undefined) pathPrefix = "";		
@@ -16,10 +18,14 @@ var skinsDemo = function(pathPrefix, loadingComplete) {
 		assetManager.loadTexture("heroes.png");
 		assetManager.loadText("heroes.json");
 		assetManager.loadText("heroes.atlas");
+		timeKeeper = new spine.TimeKeeper();		
+		loadingScreen = new spine.webgl.LoadingScreen(renderer);
+		loadingScreen.backgroundColor = bgColor;
 		requestAnimationFrame(load);
 	}
 
 	function load () {
+		timeKeeper.update();
 		if (assetManager.isLoadingComplete()) {
 			var atlas = new spine.TextureAtlas(assetManager.get("heroes.atlas"), function(path) {
 				return assetManager.get(path);		
@@ -42,7 +48,10 @@ var skinsDemo = function(pathPrefix, loadingComplete) {
 			skeleton.getBounds(offset, bounds);
 			setupUI();
 			loadingComplete(canvas, render);
-		} else requestAnimationFrame(load);
+		} else {
+			loadingScreen.draw();
+			requestAnimationFrame(load);
+		}
 	}
 
 	function setupAnimations(state) {
@@ -152,10 +161,8 @@ var skinsDemo = function(pathPrefix, loadingComplete) {
 	}
 
 	function render () {
-		var now = Date.now() / 1000;
-		var delta = now - lastFrameTime;
-		lastFrameTime = now;
-		if (delta > 0.032) delta = 0.032;
+		timeKeeper.update();
+		var delta = timeKeeper.delta;
 
 		renderer.camera.position.x = offset.x + bounds.x * 1.5 - 150;
 		renderer.camera.position.y = offset.y + bounds.y / 2;
@@ -163,7 +170,7 @@ var skinsDemo = function(pathPrefix, loadingComplete) {
 		renderer.camera.viewportHeight = bounds.y * 1.2;
 		renderer.resize(spine.webgl.ResizeMode.Fit);
 
-		gl.clearColor(0.2, 0.2, 0.2, 1);
+		gl.clearColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a);
 		gl.clear(gl.COLOR_BUFFER_BIT);			
 
 		state.update(delta);

+ 18 - 9
spine-ts/webgl/demos/spritesheet.js

@@ -1,4 +1,4 @@
-var spritesheetDemo = function(pathPrefix, loadingComplete) {
+var spritesheetDemo = function(pathPrefix, loadingComplete, bgColor) {
 	var SKELETON_ATLAS_COLOR = new spine.Color(0, 0.8, 0, 0.8);
 	var FRAME_ATLAS_COLOR = new spine.Color(0.8, 0, 0, 0.8);
 
@@ -8,9 +8,11 @@ var spritesheetDemo = function(pathPrefix, loadingComplete) {
 	var skeletonAtlas;	
 	var viewportWidth, viewportHeight;
 	var frames = [], currFrame = 0, frameTime = 0, frameScale = 0, FPS = 30;
-	var lastFrameTime = Date.now() / 1000;	
+	var timeKeeper, loadingScreen;	
 	var playTime = 0, framePlaytime = 0;
 
+	if (!bgColor) bgColor = new spine.Color(0, 0, 0, 1);
+
 	function init () {
 		if (pathPrefix === undefined) pathPrefix = "";		
 
@@ -22,11 +24,15 @@ var spritesheetDemo = function(pathPrefix, loadingComplete) {
 		assetManager = new spine.webgl.AssetManager(gl, pathPrefix);		
 		assetManager.loadTexture("raptor.png");
 		assetManager.loadText("raptor.json");
-		assetManager.loadText("raptor.atlas");		
+		assetManager.loadText("raptor.atlas");
+		timeKeeper = new spine.TimeKeeper();		
+		loadingScreen = new spine.webgl.LoadingScreen(renderer);
+		loadingScreen.backgroundColor = bgColor;
 		requestAnimationFrame(load);
 	}
 
 	function load () {
+		timeKeeper.update();
 		if (assetManager.isLoadingComplete()) {
 			skeletonAtlas = new spine.TextureAtlas(assetManager.get("raptor.atlas"), function(path) {
 				return assetManager.get("" + path);		
@@ -52,8 +58,13 @@ var spritesheetDemo = function(pathPrefix, loadingComplete) {
 			viewportHeight = ((0 + bounds.y) - offset.y);						
 
 			setupUI();
+			$("#spritesheetdemo-overlay").removeClass("overlay-hide");
+			$("#spritesheetdemo-overlay").addClass("overlay");			
 			loadingComplete(canvas, render);
-		} else requestAnimationFrame(load);
+		} else {
+			loadingScreen.draw();
+			requestAnimationFrame(load);
+		}
 	}
 
 	function setupUI() {
@@ -63,10 +74,8 @@ var spritesheetDemo = function(pathPrefix, loadingComplete) {
 	}
 
 	function render () {
-		var now = Date.now() / 1000;
-		var delta = now - lastFrameTime;
-		lastFrameTime = now;
-		if (delta > 0.032) delta = 0.032;
+		timeKeeper.update();
+		var delta = timeKeeper.delta;
 
 		delta *= (timeSlider.slider("value") / 100);
 		if (timeSliderLabel) timeSliderLabel.text(timeSlider.slider("value") + "%");	
@@ -94,7 +103,7 @@ var spritesheetDemo = function(pathPrefix, loadingComplete) {
 		renderer.camera.viewportWidth = viewportWidth * 1.2;
 		renderer.camera.viewportHeight = viewportHeight * 1.2;
 		renderer.resize(spine.webgl.ResizeMode.Fit);
-		gl.clearColor(0.2, 0.2, 0.2, 1);
+		gl.clearColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a);
 		gl.clear(gl.COLOR_BUFFER_BIT);	
 
 		renderer.begin();		

+ 20 - 13
spine-ts/webgl/demos/stretchy.js

@@ -1,4 +1,4 @@
-var stretchyDemo = function(pathPrefix, loadingComplete) {
+var stretchyDemo = function(pathPrefix, loadingComplete, bgColor) {
 	var COLOR_INNER = new spine.Color(0.8, 0, 0, 0.5);
 	var COLOR_OUTER = new spine.Color(0.8, 0, 0, 0.8);
 	var COLOR_INNER_SELECTED = new spine.Color(0.0, 0, 0.8, 0.5);
@@ -6,14 +6,16 @@ var stretchyDemo = function(pathPrefix, loadingComplete) {
 
 	var canvas, gl, renderer, input, assetManager;
 	var skeleton, bounds;		
-	var lastFrameTime = Date.now() / 1000;
+	var timeKeeper, loadingScreen;
 	var target = null;
 	var kneeFront, kneeBack;
-	var hoverTargets = [null, null, null];
-	var controlBones = ["front leg controller", "back leg controller", "hip"];
+	var hoverTargets = [];
+	var controlBones = ["front leg controller", "back leg controller", "hip", "back hand controller", "front hand controller", "spine control"];
 	var coords = new spine.webgl.Vector3(), temp = new spine.webgl.Vector3(), temp2 = new spine.Vector2(), temp3 = new spine.webgl.Vector3();
 	var kneePos = new spine.Vector2();
-	var playButton, timeLine, spacing, isPlaying = true, playTime = 0;		
+	var playButton, timeLine, spacing, isPlaying = true, playTime = 0;
+
+	if (!bgColor) bgColor = new spine.Color(1, 1, 1, 1);	
 
 	function init () {
 
@@ -27,10 +29,14 @@ var stretchyDemo = function(pathPrefix, loadingComplete) {
 		assetManager.loadTexture("stretchyman.png");
 		assetManager.loadText("stretchyman.json");
 		assetManager.loadText("stretchyman.atlas");
+		timeKeeper = new spine.TimeKeeper();		
+		loadingScreen = new spine.webgl.LoadingScreen(renderer);
+		loadingScreen.backgroundColor = bgColor;
 		requestAnimationFrame(load);
 	}
 
 	function load () {
+		timeKeeper.update();
 		if (assetManager.isLoadingComplete()) {
 			var atlas = new spine.TextureAtlas(assetManager.get("stretchyman.atlas"), function(path) {
 				return assetManager.get(path);		
@@ -44,9 +50,9 @@ var stretchyDemo = function(pathPrefix, loadingComplete) {
 			var offset = new spine.Vector2();
 			bounds = new spine.Vector2();
 			skeleton.getBounds(offset, bounds);
-
+			for (var i = 0; i < controlBones.length; i++) hoverTargets.push(null);
 			kneeFront = skeleton.findBone("front leg middle");
-			kneeBack = skeleton.findBone("back leg middle");
+			kneeBack = skeleton.findBone("back leg middle");			
 
 			renderer.camera.position.x = offset.x + bounds.x / 2;
 			renderer.camera.position.y = offset.y + bounds.y / 2;
@@ -58,7 +64,10 @@ var stretchyDemo = function(pathPrefix, loadingComplete) {
 			setupInput();
 
 			loadingComplete(canvas, render);
-		} else requestAnimationFrame(load);
+		} else {
+			loadingScreen.draw();
+			requestAnimationFrame(load);
+		}
 	}
 
 	function setupUI() {		
@@ -123,10 +132,8 @@ var stretchyDemo = function(pathPrefix, loadingComplete) {
 	}
 
 	function render () {
-		var now = Date.now() / 1000;
-		var delta = now - lastFrameTime;
-		lastFrameTime = now;
-		if (delta > 0.032) delta = 0.032;		
+		timeKeeper.update();
+		var delta = timeKeeper.delta;	
 
 		skeleton.updateWorldTransform();
 		centerKnee(kneeBack, skeleton.findBone("back leg root"), skeleton.findBone("back leg controller"));
@@ -137,7 +144,7 @@ var stretchyDemo = function(pathPrefix, loadingComplete) {
 		renderer.camera.viewportHeight = bounds.y * 1.2;
 		renderer.resize(spine.webgl.ResizeMode.Fit);
 
-		gl.clearColor(0.2, 0.2, 0.2, 1);
+		gl.clearColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a);
 		gl.clear(gl.COLOR_BUFFER_BIT);			
 
 		renderer.begin();				

+ 16 - 9
spine-ts/webgl/demos/tank.js

@@ -1,8 +1,10 @@
-var tankDemo = function(pathPrefix, loadingComplete) {	
+var tankDemo = function(pathPrefix, loadingComplete, bgColor) {	
 	var canvas, gl, renderer, input, assetManager;
 	var skeleton, state, offset, bounds;		
-	var lastFrameTime = Date.now() / 1000;	
-	var playButton, timeLine, isPlaying = true, playTime = 0;		
+	var timeKeeper, loadingScreen;
+	var playButton, timeLine, isPlaying = true, playTime = 0;
+
+	if (!bgColor) bgColor = new spine.Color(0, 0, 0, 1);		
 
 	function init () {
 		if (pathPrefix === undefined) pathPrefix = "";		
@@ -16,10 +18,14 @@ var tankDemo = function(pathPrefix, loadingComplete) {
 		assetManager.loadTexture("tank.png");
 		assetManager.loadText("tank.json");
 		assetManager.loadText("tank.atlas");
+		timeKeeper = new spine.TimeKeeper();		
+		loadingScreen = new spine.webgl.LoadingScreen(renderer);
+		loadingScreen.backgroundColor = bgColor;
 		requestAnimationFrame(load);
 	}
 
 	function load () {
+		timeKeeper.update();
 		if (assetManager.isLoadingComplete()) {
 			var atlas = new spine.TextureAtlas(assetManager.get("tank.atlas"), function(path) {
 				return assetManager.get(path);		
@@ -37,7 +43,10 @@ var tankDemo = function(pathPrefix, loadingComplete) {
 			skeleton.getBounds(offset, bounds);
 			setupUI();
 			loadingComplete(canvas, render);
-		} else requestAnimationFrame(load);
+		} else {
+			loadingScreen.draw();
+			requestAnimationFrame(load);
+		}
 	}
 
 	function setupUI() {
@@ -70,10 +79,8 @@ var tankDemo = function(pathPrefix, loadingComplete) {
 	}
 
 	function render () {
-		var now = Date.now() / 1000;
-		var delta = now - lastFrameTime;
-		lastFrameTime = now;
-		if (delta > 0.032) delta = 0.032;
+		timeKeeper.update();
+		var delta = timeKeeper.delta;
 
 		if (isPlaying) {
 			var animationDuration = state.getCurrent(0).animation.duration;
@@ -97,7 +104,7 @@ var tankDemo = function(pathPrefix, loadingComplete) {
 		renderer.camera.viewportHeight = bounds.y * 1.2;
 		renderer.resize(spine.webgl.ResizeMode.Fit);
 
-		gl.clearColor(0.2, 0.2, 0.2, 1);
+		gl.clearColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a);
 		gl.clear(gl.COLOR_BUFFER_BIT);			
 
 		renderer.begin();				

+ 15 - 8
spine-ts/webgl/demos/transformconstraint.js

@@ -1,4 +1,4 @@
-var transformConstraintDemo = function(pathPrefix, loadingComplete) {
+var transformConstraintDemo = function(pathPrefix, loadingComplete, bgColor) {
 	var COLOR_INNER = new spine.Color(0.8, 0, 0, 0.5);
 	var COLOR_OUTER = new spine.Color(0.8, 0, 0, 0.8);
 	var COLOR_INNER_SELECTED = new spine.Color(0.0, 0, 0.8, 0.5);
@@ -6,7 +6,7 @@ var transformConstraintDemo = function(pathPrefix, loadingComplete) {
 
 	var canvas, gl, renderer, input, assetManager;
 	var skeleton, state, bounds;		
-	var lastFrameTime = Date.now() / 1000;		
+	var timeKeeper, loadingScreen;	
 	var rotateHandle;	
 	var target = null;
 	var hoverTargets = [null, null, null];
@@ -15,6 +15,8 @@ var transformConstraintDemo = function(pathPrefix, loadingComplete) {
 	var lastRotation = 0;
 	var rotationOffset, mix, lastOffset = 0, lastMix = 50;
 
+	if (!bgColor) bgColor = new spine.Color(0, 0, 0, 1);
+
 	function init () {
 
 		canvas = document.getElementById("transformdemo-canvas");
@@ -27,10 +29,14 @@ var transformConstraintDemo = function(pathPrefix, loadingComplete) {
 		assetManager.loadTexture("tank.png");
 		assetManager.loadText("transformConstraint.json");
 		assetManager.loadText("tank.atlas");
+		timeKeeper = new spine.TimeKeeper();		
+		loadingScreen = new spine.webgl.LoadingScreen(renderer);
+		loadingScreen.backgroundColor = bgColor;
 		requestAnimationFrame(load);
 	}
 
 	function load () {
+		timeKeeper.update();
 		if (assetManager.isLoadingComplete()) {
 			var atlas = new spine.TextureAtlas(assetManager.get("tank.atlas"), function(path) {
 				return assetManager.get(path);		
@@ -60,7 +66,10 @@ var transformConstraintDemo = function(pathPrefix, loadingComplete) {
 			setupInput();
 
 			loadingComplete(canvas, render);
-		} else requestAnimationFrame(load);
+		} else {
+			loadingScreen.draw();
+			requestAnimationFrame(load);
+		}
 	}
 
 	function setupUI() {
@@ -142,10 +151,8 @@ var transformConstraintDemo = function(pathPrefix, loadingComplete) {
 	}
 
 	function render () {
-		var now = Date.now() / 1000;
-		var delta = now - lastFrameTime;
-		lastFrameTime = now;
-		if (delta > 0.032) delta = 0.032;
+		timeKeeper.update();
+		var delta = timeKeeper.delta;
 
 		skeleton.updateWorldTransform();
 
@@ -153,7 +160,7 @@ var transformConstraintDemo = function(pathPrefix, loadingComplete) {
 		renderer.camera.viewportHeight = bounds.y * 1.2;
 		renderer.resize(spine.webgl.ResizeMode.Fit);
 
-		gl.clearColor(0.2, 0.2, 0.2, 1);
+		gl.clearColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a);
 		gl.clear(gl.COLOR_BUFFER_BIT);
 
 		renderer.begin();				

+ 2 - 2
spine-ts/webgl/demos/utils.js

@@ -3,7 +3,7 @@ var spineDemos;
 	spineDemos.HOVER_COLOR_INNER = new spine.Color(0.478, 0, 0, 0.25);
 	spineDemos.HOVER_COLOR_OUTER = new spine.Color(1, 1, 1, 1);
 	spineDemos.NON_HOVER_COLOR_INNER = new spine.Color(0.478, 0, 0, 0.5);
-	spineDemos.NON_HOVER_COLOR_OUTER = new spine.Color(1, 0, 0, 0.8);
+	spineDemos.NON_HOVER_COLOR_OUTER = new spine.Color(1, 0, 0, 0.8);	
 
 	spineDemos.setupRendering = function (canvas, renderFunc) {
 		var isVisible = false;
@@ -34,5 +34,5 @@ var spineDemos;
 		var width = (window.innerHeight || document.documentElement.clientHeight);
 		var height = (window.innerWidth || document.documentElement.clientWidth);
 		return rect.left < x + width && rect.right > x && rect.top < y + height && rect.bottom > y;		 
-	};
+	};	
 })(spineDemos || (spineDemos = { }));

+ 16 - 10
spine-ts/webgl/demos/vine.js

@@ -1,4 +1,4 @@
-var vineDemo = function(pathPrefix, loadingComplete) {
+var vineDemo = function(pathPrefix, loadingComplete, bgColor) {
 	var COLOR_INNER = new spine.Color(0.8, 0, 0, 0.5);
 	var COLOR_OUTER = new spine.Color(0.8, 0, 0, 0.8);
 	var COLOR_INNER_SELECTED = new spine.Color(0.0, 0, 0.8, 0.5);
@@ -6,15 +6,16 @@ var vineDemo = function(pathPrefix, loadingComplete) {
 
 	var canvas, gl, renderer, input, assetManager;
 	var skeleton, state, bounds;		
-	var lastFrameTime = Date.now() / 1000;
+	var timeKeeper, loadingScreen;
 	var target = null;
 	var hoverTargets = [null, null, null, null, null];
 	var controlBones = ["vine-control1", "vine-control2", "vine-control3", "vine-control4"];
 	var coords = new spine.webgl.Vector3(), temp = new spine.webgl.Vector3(), temp2 = new spine.Vector2();
-	var playButton, timeLine, isPlaying = true, playTime = 0;		
+	var playButton, timeLine, isPlaying = true, playTime = 0;
 
-	function init () {
+	if (!bgColor) bgColor = new spine.Color(0, 0, 0, 1);	
 
+	function init () {
 		canvas = document.getElementById("vinedemo-canvas");
 		canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight;
 		gl = canvas.getContext("webgl", { alpha: false }) || canvas.getContext("experimental-webgl", { alpha: false });	
@@ -25,10 +26,14 @@ var vineDemo = function(pathPrefix, loadingComplete) {
 		assetManager.loadTexture("vine.png");
 		assetManager.loadText("vine.json");
 		assetManager.loadText("vine.atlas");
+		timeKeeper = new spine.TimeKeeper();		
+		loadingScreen = new spine.webgl.LoadingScreen(renderer);
+		loadingScreen.backgroundColor = bgColor;
 		requestAnimationFrame(load);
 	}
 
 	function load () {
+		timeKeeper.update();
 		if (assetManager.isLoadingComplete()) {
 			var atlas = new spine.TextureAtlas(assetManager.get("vine.atlas"), function(path) {
 				return assetManager.get(path);		
@@ -57,7 +62,10 @@ var vineDemo = function(pathPrefix, loadingComplete) {
 			setupInput();
 
 			loadingComplete(canvas, render);
-		} else requestAnimationFrame(load);
+		} else {
+			loadingScreen.draw(); 
+			requestAnimationFrame(load);
+		}
 	}
 
 	function setupUI() {
@@ -139,10 +147,8 @@ var vineDemo = function(pathPrefix, loadingComplete) {
 	}
 
 	function render () {
-		var now = Date.now() / 1000;
-		var delta = now - lastFrameTime;
-		lastFrameTime = now;
-		if (delta > 0.032) delta = 0.032;
+		timeKeeper.update();
+		var delta = timeKeeper.delta;
 
 		if (isPlaying) {
 			var animationDuration = state.getCurrent(0).animation.duration;
@@ -162,7 +168,7 @@ var vineDemo = function(pathPrefix, loadingComplete) {
 		renderer.camera.viewportHeight = bounds.y * 1.2;
 		renderer.resize(spine.webgl.ResizeMode.Fit);
 
-		gl.clearColor(0.2, 0.2, 0.2, 1);
+		gl.clearColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a);
 		gl.clear(gl.COLOR_BUFFER_BIT);			
 
 		renderer.begin();				

File diff suppressed because it is too large
+ 12 - 0
spine-ts/webgl/src/LoadingScreen.ts


+ 106 - 0
spine-ts/webgl/src/SceneRenderer.ts

@@ -118,6 +118,112 @@ module spine.webgl {
 			this.batcher.draw(texture, quad, this.QUAD_TRIANGLES);
 		}
 
+		drawTextureRotated (texture: GLTexture, x: number, y: number, width: number, height: number, pivotX: number, pivotY: number, angle: number, color: Color = null, premultipliedAlpha: boolean = false) {
+			this.enableRenderer(this.batcher);
+			if (color === null) color = this.WHITE;
+			let quad = this.QUAD;
+
+			// bottom left and top right corner points relative to origin
+			let worldOriginX = x + pivotX;
+			let worldOriginY = y + pivotY;
+			let  fx = -pivotX;
+			let  fy = -pivotY;
+			let  fx2 = width - pivotX;
+			let  fy2 = height - pivotY;			
+
+			// construct corner points, start from top left and go counter clockwise
+			let p1x = fx;
+			let p1y = fy;
+			let p2x = fx;
+			let p2y = fy2;
+			let p3x = fx2;
+			let p3y = fy2;
+			let p4x = fx2;
+			let p4y = fy;
+
+			let x1 = 0;
+			let y1 = 0;
+			let x2 = 0;
+			let y2 = 0;
+			let x3 = 0;
+			let y3 = 0;
+			let x4 = 0;
+			let y4 = 0;
+
+			// rotate
+			if (angle != 0) {
+				let cos = MathUtils.cosDeg(angle);
+				let sin = MathUtils.sinDeg(angle);
+
+				x1 = cos * p1x - sin * p1y;
+				y1 = sin * p1x + cos * p1y;
+
+				x4 = cos * p2x - sin * p2y;
+				y4 = sin * p2x + cos * p2y;
+
+				x3 = cos * p3x - sin * p3y;
+				y3 = sin * p3x + cos * p3y;
+
+				x2 = x3 + (x1 - x4);
+				y2 = y3 + (y1 - y4);
+			} else {
+				x1 = p1x;
+				y1 = p1y;
+
+				x4 = p2x;
+				y4 = p2y;
+
+				x3 = p3x;
+				y3 = p3y;
+
+				x2 = p4x;
+				y2 = p4y;
+			}
+
+			x1 += worldOriginX;
+			y1 += worldOriginY;
+			x2 += worldOriginX;
+			y2 += worldOriginY;
+			x3 += worldOriginX;
+			y3 += worldOriginY;
+			x4 += worldOriginX;
+			y4 += worldOriginY;
+
+			quad[0] = x1;
+			quad[1] = y1;
+			quad[2] = color.r;
+			quad[3] = color.g;
+			quad[4] = color.b;
+			quad[5] = color.a;
+			quad[6] = 0;
+			quad[7] = 1;
+			quad[8] = x2;
+			quad[9] = y2;
+			quad[10] = color.r;
+			quad[11] = color.g;
+			quad[12] = color.b;
+			quad[13] = color.a;
+			quad[14] = 1;
+			quad[15] = 1;
+			quad[16] = x3;
+			quad[17] = y3;
+			quad[18] = color.r;
+			quad[19] = color.g;
+			quad[20] = color.b;
+			quad[21] = color.a;
+			quad[22] = 1;
+			quad[23] = 0;
+			quad[24] = x4;
+			quad[25] = y4;
+			quad[26] = color.r;
+			quad[27] = color.g;
+			quad[28] = color.b;
+			quad[29] = color.a;
+			quad[30] = 0;
+			quad[31] = 0;
+			this.batcher.draw(texture, quad, this.QUAD_TRIANGLES);
+		}
+
 		drawRegion (region: TextureAtlasRegion, x: number, y: number, width: number, height: number, color: Color = null, premultipliedAlpha: boolean = false) {
 			this.enableRenderer(this.batcher);
 			if (color === null) color = this.WHITE;

Some files were not shown because too many files changed in this diff