Add animation to ImageGPULayer.

This commit is contained in:
Ben Richards 2024-07-18 10:52:06 +12:00
parent 625e2fa042
commit 11449662e8
10 changed files with 1088 additions and 73 deletions

View file

@ -0,0 +1,74 @@
/**
* @author Benjamin D. Richards <benjamindrichards@gmail.com>
* @copyright 2013-2024 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
/**
* Easing function identifiers.
*
* @ignore
*/
var EasingEncoding = {
None: 0,
Power0: 1,
Power1: 10,
Power2: 20,
Power3: 30,
Power4: 40,
Linear: 1,
Quad: 10,
'Quad.easeOut': 10,
'Quad.easeIn': 11,
'Quad.easeInOut': 12,
Cubic: 20,
'Cubic.easeOut': 20,
'Cubic.easeIn': 21,
'Cubic.easeInOut': 22,
Quart: 30,
'Quart.easeOut': 30,
'Quart.easeIn': 31,
'Quart.easeInOut': 32,
Quint: 40,
'Quint.easeOut': 40,
'Quint.easeIn': 41,
'Quint.easeInOut': 42,
Sine: 50,
'Sine.easeOut': 50,
'Sine.easeIn': 51,
'Sine.easeInOut': 52,
Expo: 60,
'Expo.easeOut': 60,
'Expo.easeIn': 61,
'Expo.easeInOut': 62,
Circ: 70,
'Circ.easeOut': 70,
'Circ.easeIn': 71,
'Circ.easeInOut': 72,
// // Elastic requires extra parameters, so we skip it.
// Elastic: 80,
// 'Elastic.easeOut': 80,
// 'Elastic.easeIn': 81,
// 'Elastic.easeInOut': 82,
Back: 90,
'Back.easeOut': 90,
'Back.easeIn': 91,
'Back.easeInOut': 92,
Bounce: 100,
'Bounce.easeOut': 100,
'Bounce.easeIn': 101,
'Bounce.easeInOut': 102
};
module.exports = EasingEncoding;

View file

@ -80,6 +80,26 @@ var ImageGPULayer = new Class({
*/
this.needsUpdate = false;
/**
* The time elapsed since timer initialization.
* This drives the animation of the ImageGPULayer.
*
* @name Phaser.GameObjects.ImageGPULayer#timeElapsed
* @type {number}
* @since 3.90.0
*/
this.timeElapsed = 0;
/**
* Whether the ImageGPULayer is paused.
*
* @name Phaser.GameObjects.ImageGPULayer#timePaused
* @type {boolean}
* @since 3.90.0
* @default false
*/
this.timePaused = false;
this.setTexture(texture);
this.initRenderNodes('ImageGPULayer');
this.initPostPipeline(true);
@ -103,6 +123,26 @@ var ImageGPULayer = new Class({
this.renderNodeData[this.submitterNode.name] = {};
},
// Overrides Game Object method
addedToScene: function ()
{
this.scene.sys.updateList.add(this);
},
// Overrides Game Object method
removedFromScene: function ()
{
this.scene.sys.updateList.remove(this);
},
preUpdate: function (time, delta)
{
if (!this.timePaused)
{
this.timeElapsed += delta;
}
},
/**
* Adds an ImageGPULayerMember to this ImageGPULayer.
*
@ -151,6 +191,19 @@ var ImageGPULayer = new Class({
this.images.splice(index, 1);
}
},
/**
* Reset the animation timer for this ImageGPULayer.
*
* @method Phaser.GameObjects.ImageGPULayer#resetTimer
* @since 3.90.0
* @param {number} [ms=0] - The time to reset the timer to.
*/
resetTimer: function (ms)
{
if (ms === undefined) { ms = 0; }
this.timeElapsed = ms;
}
});

View file

@ -5,6 +5,7 @@
*/
var Class = require('../../utils/Class');
var ImageGPULayerMemberAnimation = require('./ImageGPULayerMemberAnimation');
/**
* @classdesc
@ -52,6 +53,15 @@ var ImageGPULayerMember = new Class({
*/
this.x = x;
/**
* The horizontal position animation of this ImageGPULayerMember.
*
* @name Phaser.GameObjects.ImageGPULayerMember#xAnimation
* @type {Phaser.GameObjects.ImageGPULayerMemberAnimation}
* @since 3.90.0
*/
this.xAnimation = new ImageGPULayerMemberAnimation();
/**
* The vertical position of this ImageGPULayerMember.
*
@ -61,6 +71,15 @@ var ImageGPULayerMember = new Class({
*/
this.y = y;
/**
* The vertical position animation of this ImageGPULayerMember.
*
* @name Phaser.GameObjects.ImageGPULayerMember#yAnimation
* @type {Phaser.GameObjects.ImageGPULayerMemberAnimation}
* @since 3.90.0
*/
this.yAnimation = new ImageGPULayerMemberAnimation();
/**
* The rotation of this ImageGPULayerMember, in radians.
*
@ -71,6 +90,15 @@ var ImageGPULayerMember = new Class({
*/
this.rotation = 0;
/**
* The rotation animation of this ImageGPULayerMember.
*
* @name Phaser.GameObjects.ImageGPULayerMember#rotationAnimation
* @type {Phaser.GameObjects.ImageGPULayerMemberAnimation}
* @since 3.90.0
*/
this.rotationAnimation = new ImageGPULayerMemberAnimation();
/**
* The horizontal scale of this ImageGPULayerMember.
*
@ -81,6 +109,15 @@ var ImageGPULayerMember = new Class({
*/
this.scaleX = 1;
/**
* The horizontal scale animation of this ImageGPULayerMember.
*
* @name Phaser.GameObjects.ImageGPULayerMember#scaleXAnimation
* @type {Phaser.GameObjects.ImageGPULayerMemberAnimation}
* @since 3.90.0
*/
this.scaleXAnimation = new ImageGPULayerMemberAnimation();
/**
* The vertical scale of this ImageGPULayerMember.
*
@ -91,6 +128,15 @@ var ImageGPULayerMember = new Class({
*/
this.scaleY = 1;
/**
* The vertical scale animation of this ImageGPULayerMember.
*
* @name Phaser.GameObjects.ImageGPULayerMember#scaleYAnimation
* @type {Phaser.GameObjects.ImageGPULayerMemberAnimation}
* @since 3.90.0
*/
this.scaleYAnimation = new ImageGPULayerMemberAnimation();
/**
* The horizontal origin of this ImageGPULayerMember.
*
@ -121,6 +167,15 @@ var ImageGPULayerMember = new Class({
*/
this.scrollFactorX = 1;
/**
* The horizontal scroll factor animation of this ImageGPULayerMember.
*
* @name Phaser.GameObjects.ImageGPULayerMember#scrollFactorXAnimation
* @type {Phaser.GameObjects.ImageGPULayerMemberAnimation}
* @since 3.90.0
*/
this.scrollFactorXAnimation = new ImageGPULayerMemberAnimation();
/**
* The vertical scroll factor of this ImageGPULayerMember.
*
@ -131,6 +186,15 @@ var ImageGPULayerMember = new Class({
*/
this.scrollFactorY = 1;
/**
* The vertical scroll factor animation of this ImageGPULayerMember.
*
* @name Phaser.GameObjects.ImageGPULayerMember#scrollFactorYAnimation
* @type {Phaser.GameObjects.ImageGPULayerMemberAnimation}
* @since 3.90.0
*/
this.scrollFactorYAnimation = new ImageGPULayerMemberAnimation();
/**
* Whether to tint the fill of this ImageGPULayerMember.
*
@ -152,6 +216,15 @@ var ImageGPULayerMember = new Class({
*/
this.tintBlend = 1;
/**
* The tint blend animation of this ImageGPULayerMember.
*
* @name Phaser.GameObjects.ImageGPULayerMember#tintBlendAnimation
* @type {Phaser.GameObjects.ImageGPULayerMemberAnimation}
* @since 3.90.0
*/
this.tintBlendAnimation = new ImageGPULayerMemberAnimation();
/**
* The bottom-left tint color of this ImageGPULayerMember.
*
@ -243,6 +316,15 @@ var ImageGPULayerMember = new Class({
* @default 1
*/
this.alpha = 1;
/**
* The alpha animation of this ImageGPULayerMember.
*
* @name Phaser.GameObjects.ImageGPULayerMember#alphaAnimation
* @type {Phaser.GameObjects.ImageGPULayerMemberAnimation}
* @since 3.90.0
*/
this.alphaAnimation = new ImageGPULayerMemberAnimation();
}
});

View file

@ -0,0 +1,170 @@
/**
* @author Benjamin D. Richards <benjamindrichards@gmail.com>
* @copyright 2013-2024 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Class = require('../../utils/Class');
var EasingEncoding = require('./EasingEncoding');
/**
* @classdesc
* An animation used by ImageGPULayerMember objects.
* This is a simple animation to be stored and processed
* in 3 values on the GPU.
*
* If you set the values of this animation using direct properties,
* you must call `getEncoded` to update the encoding object.
* It is called automatically by the `setTo` method.
*
* @class ImageGPULayerMemberAnimation
* @memberof Phaser.GameObjects
* @constructor
* @since 3.90.0
* @param {string} [type='None'] - The type of animation this is. This should be the name of an easing function or 'None'.
* @param {number} [amplitude=0] - The amplitude of the animation.
* @param {number} [duration=0] - The duration of the animation, in milliseconds.
* @param {number} [offset=0] - The offset of the animation, in milliseconds.
* @param {boolean} [yoyo=true] - Whether the animation should 'yoyo' (reverse) after completing.
*/
var ImageGPULayerMemberAnimation = new Class({
initialize: function ImageGPULayerMemberAnimation (type, amplitude, duration, offset, yoyo)
{
/**
* The type of animation this is. This should be the name of an easing function or 'none'.
*
* @name Phaser.GameObjects.ImageGPULayerMemberAnimation#type
* @type {string}
* @since 3.90.0
*/
this.type = type || 'None';
/**
* The amplitude of the animation.
*
* @name Phaser.GameObjects.ImageGPULayerMemberAnimation#amplitude
* @type {number}
* @private
* @since 3.90.0
*/
this.amplitude = amplitude || 0;
/**
* The duration of the animation, in milliseconds. Must be positive.
*
* @name Phaser.GameObjects.ImageGPULayerMemberAnimation#duration
* @type {number}
* @private
* @since 3.90.0
*/
this.duration = duration || 0;
/**
* The offset of the animation, in milliseconds.
*
* @name Phaser.GameObjects.ImageGPULayerMemberAnimation#offset
* @type {number}
* @private
* @since 3.90.0
*/
this.offset = offset || 0;
/**
* Whether the animation should 'yoyo' (reverse) after completing.
*
* @name Phaser.GameObjects.ImageGPULayerMemberAnimation#yoyo
* @type {boolean}
* @since 3.90.0
* @default true
*/
this.yoyo = (yoyo !== undefined) ? yoyo : true;
/**
* The encoded values of this animation.
* This is used to store the animation in a format
* that can be sent to the GPU.
*
* @name Phaser.GameObjects.ImageGPULayerMemberAnimation#encoding
* @type {Float32Array}
* @since 3.90.0
*/
this.encoding = this.getEncoded();
},
/**
* Sets the values of this animation and update the encoding.
*
* @method Phaser.GameObjects.ImageGPULayerMemberAnimation#setTo
* @since 3.90.0
* @param {string} type - The type of animation this is. This should be the name of an easing function or 'none'.
* @param {number} [amplitude] - The amplitude of the animation.
* @param {number} [duration] - The duration of the animation, in milliseconds. Must not be negative.
* @param {number} [offset] - The offset of the animation, in milliseconds.
* @param {boolean} [yoyo] - Whether the animation should 'yoyo' (reverse) after completing.
* @return {Phaser.GameObjects.ImageGPULayerMemberAnimation} This ImageGPULayerMemberAnimation object.
*/
setTo: function (type, amplitude, duration, offset, yoyo)
{
this.type = type;
if (amplitude !== undefined)
{
this.amplitude = amplitude;
}
if (duration !== undefined)
{
this.duration = Math.max(duration, 0);
}
if (offset !== undefined)
{
this.offset = offset;
}
if (yoyo !== undefined)
{
this.yoyo = yoyo;
}
this.encoding = this.getEncoded();
return this;
},
/**
* Encodes this animation into an object for the GPU.
* This modifies the values to encode type for the GPU.
*
* @method Phaser.GameObjects.ImageGPULayerMemberAnimation#getEncoded
* @since 3.90.0
* @return {Float32Array} The encoded animation object.
*/
getEncoded: function ()
{
// Normalize offset.
var offset = (this.offset / this.duration) % 1;
if (offset < 0)
{
offset += 1;
}
// Add an integer to encode the type.
var type = EasingEncoding[this.type];
if (type === undefined)
{
type = 0;
}
// Encode yoyo in the sign of duration, which must be positive.
var duration = this.duration;
if (this.yoyo)
{
duration = -duration;
}
return new Float32Array([ this.amplitude, duration, offset + type ]);
}
});
module.exports = ImageGPULayerMemberAnimation;

View file

@ -90,24 +90,63 @@ var BakeImageGPULayer = new Class({
var image = images[i];
var frame = image.frame;
var xAnimation = image.xAnimation.encoding;
viewF32[instanceBufferOffset++] = image.x;
viewF32[instanceBufferOffset++] = xAnimation[0];
viewF32[instanceBufferOffset++] = xAnimation[1];
viewF32[instanceBufferOffset++] = xAnimation[2];
var yAnimation = image.yAnimation.encoding;
viewF32[instanceBufferOffset++] = image.y;
viewF32[instanceBufferOffset++] = yAnimation[0];
viewF32[instanceBufferOffset++] = yAnimation[1];
viewF32[instanceBufferOffset++] = yAnimation[2];
var rotationAnimation = image.rotationAnimation.encoding;
viewF32[instanceBufferOffset++] = image.rotation;
viewF32[instanceBufferOffset++] = rotationAnimation[0];
viewF32[instanceBufferOffset++] = rotationAnimation[1];
viewF32[instanceBufferOffset++] = rotationAnimation[2];
var scaleXAnimation = image.scaleXAnimation.encoding;
viewF32[instanceBufferOffset++] = image.scaleX * frame.width;
viewF32[instanceBufferOffset++] = scaleXAnimation[0] * frame.width;
viewF32[instanceBufferOffset++] = scaleXAnimation[1];
viewF32[instanceBufferOffset++] = scaleXAnimation[2];
var scaleYAnimation = image.scaleYAnimation.encoding;
viewF32[instanceBufferOffset++] = image.scaleY * frame.height;
viewF32[instanceBufferOffset++] = scaleYAnimation[0] * frame.height;
viewF32[instanceBufferOffset++] = scaleYAnimation[1];
viewF32[instanceBufferOffset++] = scaleYAnimation[2];
viewF32[instanceBufferOffset++] = image.originX;
viewF32[instanceBufferOffset++] = image.originY;
viewF32[instanceBufferOffset++] = image.tintFill;
var scrollFactorXAnimation = image.scrollFactorXAnimation.encoding;
viewF32[instanceBufferOffset++] = image.scrollFactorX;
viewF32[instanceBufferOffset++] = scrollFactorXAnimation[0];
viewF32[instanceBufferOffset++] = scrollFactorXAnimation[1];
viewF32[instanceBufferOffset++] = scrollFactorXAnimation[2];
var scrollFactorYAnimation = image.scrollFactorYAnimation.encoding;
viewF32[instanceBufferOffset++] = image.scrollFactorY;
viewF32[instanceBufferOffset++] = scrollFactorYAnimation[0];
viewF32[instanceBufferOffset++] = scrollFactorYAnimation[1];
viewF32[instanceBufferOffset++] = scrollFactorYAnimation[2];
viewF32[instanceBufferOffset++] = frame.u0;
viewF32[instanceBufferOffset++] = frame.v0;
viewF32[instanceBufferOffset++] = frame.u1;
viewF32[instanceBufferOffset++] = frame.v1;
viewF32[instanceBufferOffset++] = image.tintFill;
var tintBlendAnimation = image.tintBlendAnimation.encoding;
viewF32[instanceBufferOffset++] = image.tintBlend;
viewF32[instanceBufferOffset++] = tintBlendAnimation[0];
viewF32[instanceBufferOffset++] = tintBlendAnimation[1];
viewF32[instanceBufferOffset++] = tintBlendAnimation[2];
viewU32[instanceBufferOffset++] =
getTint(image.tintBottomLeft, image.alphaBottomLeft);
@ -118,7 +157,11 @@ var BakeImageGPULayer = new Class({
viewU32[instanceBufferOffset++] =
getTint(image.tintTopRight, image.alphaTopRight);
var alphaAnimation = image.alphaAnimation.encoding;
viewF32[instanceBufferOffset++] = image.alpha;
viewF32[instanceBufferOffset++] = alphaAnimation[0];
viewF32[instanceBufferOffset++] = alphaAnimation[1];
viewF32[instanceBufferOffset++] = alphaAnimation[2];
}
instanceBuffer.update();

View file

@ -159,37 +159,44 @@ var SubmitterImageGPULayer = new Class({
instanceDivisor: 1,
layout: [
{
name: 'inPositionX'
name: 'inPositionX',
size: 4
},
{
name: 'inPositionY'
name: 'inPositionY',
size: 4
},
{
name: 'inRotation'
name: 'inRotation',
size: 4
},
{
name: 'inScaleX'
name: 'inScaleX',
size: 4
},
{
name: 'inScaleY'
name: 'inScaleY',
size: 4
},
{
name: 'inOrigin',
size: 2
name: 'inOriginAndTintFill',
size: 3
},
{
name: 'inScrollFactorX'
name: 'inScrollFactorX',
size: 4
},
{
name: 'inScrollFactorY'
name: 'inScrollFactorY',
size: 4
},
{
name: 'inFrameUVs',
size: 4
},
{
name: 'inTintFillAndBlend',
size: 2
name: 'inTintBlend',
size: 4
},
{
name: 'inTintBL',
@ -216,7 +223,8 @@ var SubmitterImageGPULayer = new Class({
normalized: true
},
{
name: 'inAlpha'
name: 'inAlpha',
size: 4
}
]
},
@ -389,6 +397,11 @@ var SubmitterImageGPULayer = new Class({
]
);
this.program.setUniform(
'uTime',
this.gameObject.timeElapsed
);
// Assemble textures.
var textures = [
this.gameObject.frame.source.glTexture

View file

@ -12,10 +12,11 @@ module.exports = [
'void main ()',
'{',
' vec4 texture = texture2D(uMainSampler, outTexCoord);',
' vec4 color = texture * outTint;',
' vec4 texel = vec4(outTint.bgr * outTint.a, outTint.a);',
' vec4 color = texture * texel;',
' if (outTintEffect == 1.0)',
' {',
' color.rgb = mix(texture.rgb, outTint.rgb, texture.a);',
' color.rgb = mix(texture.rgb, texel.rgb, texture.a);',
' }',
' gl_FragColor = color;',
'}',

View file

@ -6,73 +6,341 @@ module.exports = [
'uniform vec3 uCameraScrollAndAlpha;',
'uniform int uRoundPixels;',
'uniform vec2 uResolution;',
'uniform float uTime;',
'attribute float inVertex;',
'attribute float inPositionX;',
'attribute float inPositionY;',
'attribute float inRotation;',
'attribute float inScaleX;',
'attribute float inScaleY;',
'attribute vec2 inOrigin;',
'attribute float inScrollFactorX;',
'attribute float inScrollFactorY;',
'attribute vec4 inPositionX;',
'attribute vec4 inPositionY;',
'attribute vec4 inRotation;',
'attribute vec4 inScaleX;',
'attribute vec4 inScaleY;',
'attribute vec3 inOriginAndTintFill;',
'attribute vec4 inScrollFactorX;',
'attribute vec4 inScrollFactorY;',
'attribute vec4 inFrameUVs;',
'attribute vec2 inTintFillAndBlend;',
'attribute vec4 inTintBlend;',
'attribute vec4 inTintTL;',
'attribute vec4 inTintTR;',
'attribute vec4 inTintBL;',
'attribute vec4 inTintBR;',
'attribute float inAlpha;',
'attribute vec4 inAlpha;',
'varying vec2 outTexCoord;',
'varying float outTintEffect;',
'varying vec4 outTint;',
'const float PI = 3.14159265359;',
'const float HALF_PI = PI / 2.0;',
'const float BOUNCE_OVERSHOOT = 1.70158;',
'const float BOUNCE_OVERSHOOT_PLUS = BOUNCE_OVERSHOOT + 1.0;',
'const float BOUNCE_OVERSHOOT_IN_OUT = BOUNCE_OVERSHOOT * 1.525;',
'const float BOUNCE_OVERSHOOT_IN_OUT_PLUS = BOUNCE_OVERSHOOT_IN_OUT + 1.0;',
'float animate (vec4 anim)',
'{',
' float value = anim.x;',
' float a = anim.y;',
' float b = anim.z;',
' float c = anim.w;',
' int type = int(floor(c));',
' if (type == 0 || a == 0.0 || b == 0.0)',
' {',
' return value;',
' }',
' float duration = b;',
' float offset = mod(c, 1.0) * b;',
' bool yoyo = b < 0.0;',
' float time = mod(uTime / duration + offset, 1.0);',
' if (yoyo)',
' {',
' if (time < 0.5)',
' {',
' time = time * 2.0;',
' }',
' else',
' {',
' time = 1.0 - ((time - 0.5) * 2.0);',
' }',
' }',
' if (type == 1)',
' {',
' return value + a * time;',
' }',
' else if (type == 10)',
' {',
' return value + a * time * (2.0 - time);',
' }',
' else if (type == 11)',
' {',
' return value + a * time * time;',
' }',
' else if (type == 12)',
' {',
' time *= 2.0;',
' if (time < 1.0)',
' {',
' return value + a * time * time * 0.5;',
' }',
' time -= 1.0;',
' return value + a * -0.5 * (time * (time - 2.0) - 1.0);',
' }',
' else if (type == 20)',
' {',
' time -= 1.0;',
' return value + a * (time * time * time + 1.0);',
' }',
' else if (type == 21)',
' {',
' return value + a * time * time * time;',
' }',
' else if (type == 22)',
' {',
' time *= 2.0;',
' if (time < 1.0)',
' {',
' return value + a * time * time * time * 0.5;',
' }',
' time -= 2.0;',
' return value + a * 0.5 * (time * time * time + 2.0);',
' }',
' else if (type == 30)',
' {',
' time -= 1.0;',
' return value + a * (1.0 - time * time * time * time);',
' }',
' else if (type == 31)',
' {',
' return value + a * time * time * time * time;',
' }',
' else if (type == 32)',
' {',
' time *= 2.0;',
' if (time < 1.0)',
' {',
' return value + a * time * time * time * time * 0.5;',
' }',
' time -= 2.0;',
' return value + a * -0.5 * (time * time * time * time - 2.0);',
' }',
' else if (type == 40)',
' {',
' time -= 1.0;',
' return value + a * (time * time * time * time * time + 1.0);',
' }',
' else if (type == 41)',
' {',
' return value + a * time * time * time * time * time;',
' }',
' else if (type == 42)',
' {',
' time *= 2.0;',
' if (time < 1.0)',
' {',
' return value + a * time * time * time * time * time * 0.5;',
' }',
' time -= 2.0;',
' return value + a * 0.5 * (time * time * time * time * time + 2.0);',
' }',
' else if (type == 50)',
' {',
' return value + a * sin(time * HALF_PI);',
' }',
' else if (type == 51)',
' {',
' return value + a * (1.0 - cos(time * HALF_PI));',
' }',
' else if (type == 52)',
' {',
' return value + a * 0.5 * (1.0 - cos(PI * time));',
' }',
' else if (type == 60)',
' {',
' return value + a * (1.0 - pow(2.0, -10.0 * time));',
' }',
' else if (type == 61)',
' {',
' return value + a * pow(2.0, 10.0 * (time - 1.0) - 0.001);',
' }',
' else if (type == 62)',
' {',
' time *= 2.0;',
' if (time < 1.0)',
' {',
' return value + a * 0.5 * pow(2.0, 10.0 * (time - 1.0));',
' }',
' time -= 1.0;',
' return value + a * 0.5 * (2.0 - pow(2.0, -10.0 * (time - 1.0)));',
' }',
' else if (type == 70)',
' {',
' time -= 1.0;',
' return value + a * sqrt(1.0 - time * time);',
' }',
' else if (type == 71)',
' {',
' return value + a * (1.0 - sqrt(1.0 - time * time));',
' }',
' else if (type == 72)',
' {',
' time *= 2.0;',
' if (time < 1.0)',
' {',
' return value + a * -0.5 * (sqrt(1.0 - time * time) - 1.0);',
' }',
' time -= 2.0;',
' return value + a * 0.5 * (sqrt(1.0 - time * time) + 1.0);',
' }',
' else if (type == 90)',
' {',
' time -= 1.0;',
' return value + a * (time * time * (BOUNCE_OVERSHOOT_PLUS * time + BOUNCE_OVERSHOOT) + 1.0);',
' }',
' else if (type == 91)',
' {',
' return value + a * time * time * (BOUNCE_OVERSHOOT_PLUS * time - BOUNCE_OVERSHOOT);',
' }',
' else if (type == 92)',
' {',
' time *= 2.0;',
' if (time < 1.0)',
' {',
' return value + a * 0.5 * (time * time * (BOUNCE_OVERSHOOT_IN_OUT_PLUS * time - BOUNCE_OVERSHOOT_IN_OUT));',
' }',
' time -= 2.0;',
' return value + a * 0.5 * (time * time * (BOUNCE_OVERSHOOT_IN_OUT_PLUS * time + BOUNCE_OVERSHOOT_IN_OUT) + 2.0);',
' }',
' else if (type == 100)',
' {',
' if (time < 1.0 / 2.75)',
' {',
' return value + a * (7.5625 * time * time);',
' }',
' else if (time < 2.0 / 2.75)',
' {',
' time -= 1.5 / 2.75;',
' return value + a * (7.5625 * time * time + 0.75);',
' }',
' else if (time < 2.5 / 2.75)',
' {',
' time -= 2.25 / 2.75;',
' return value + a * (7.5625 * time * time + 0.9375);',
' }',
' else',
' {',
' time -= 2.625 / 2.75;',
' return value + a * (7.5625 * time * time + 0.984375);',
' }',
' }',
' else if (type == 101)',
' {',
' time = 1.0 - time;',
' if (time < 1.0 / 2.75)',
' {',
' return value + a * (1.0 - 7.5625 * time * time);',
' }',
' else if (time < 2.0 / 2.75)',
' {',
' time -= 1.5 / 2.75;',
' return value + a * (1.0 - (7.5625 * time * time + 0.75));',
' }',
' else if (time < 2.5 / 2.75)',
' {',
' time -= 2.25 / 2.75;',
' return value + a * (1.0 - (7.5625 * time * time + 0.9375));',
' }',
' else',
' {',
' time -= 2.625 / 2.75;',
' return value + a * (1.0 - (7.5625 * time * time + 0.984375));',
' }',
' }',
' else if (type == 102)',
' {',
' bool reverse = false;',
' if (time < 0.5)',
' {',
' time = 1.0 - time * 2.0;',
' reverse = true;',
' }',
' if (time < 1.0 / 2.75)',
' {',
' time = 7.5625 * time * time;',
' }',
' else if (time < 2.0 / 2.75)',
' {',
' time -= 1.5 / 2.75;',
' time = 7.5625 * time * time + 0.75;',
' }',
' else if (time < 2.5 / 2.75)',
' {',
' time -= 2.25 / 2.75;',
' time = 7.5625 * time * time + 0.9375;',
' }',
' else',
' {',
' time -= 2.625 / 2.75;',
' time = 7.5625 * time * time + 0.984375;',
' }',
' if (reverse)',
' {',
' return (1.0 - time) * 0.5;',
' }',
' return time * 0.5 + 0.5;',
' }',
' return value;',
'}',
'void main ()',
'{',
' float tintFill = inTintFillAndBlend.x;',
' float tintBlend = inTintFillAndBlend.y;',
' float x = -inOrigin.x;',
' float y = -inOrigin.y;',
' float positionX = animate(inPositionX);',
' float positionY = animate(inPositionY);',
' float rotation = animate(inRotation);',
' float scaleX = animate(inScaleX);',
' float scaleY = animate(inScaleY);',
' float scrollFactorX = animate(inScrollFactorX);',
' float scrollFactorY = animate(inScrollFactorY);',
' float tintBlend = animate(inTintBlend);',
' float alpha = animate(inAlpha);',
' vec2 origin = inOriginAndTintFill.xy;',
' float tintFill = inOriginAndTintFill.z;',
' float x = -origin.x;',
' float y = -origin.y;',
' float u = inFrameUVs.x;',
' float v = inFrameUVs.y;',
' vec4 tint = inTintTL;',
' if (inVertex == 0.0)',
' {',
' y = 1.0 - inOrigin.y;',
' y = 1.0 - origin.y;',
' v = inFrameUVs.w;',
' tint = inTintBL;',
' }',
' else if (inVertex == 2.0)',
' {',
' x = 1.0 - inOrigin.x;',
' y = 1.0 - inOrigin.y;',
' x = 1.0 - origin.x;',
' y = 1.0 - origin.y;',
' u = inFrameUVs.z;',
' v = inFrameUVs.w;',
' tint = inTintBR;',
' }',
' else if (inVertex == 3.0)',
' {',
' x = 1.0 - inOrigin.x;',
' x = 1.0 - origin.x;',
' u = inFrameUVs.z;',
' tint = inTintTR;',
' }',
' vec3 position = vec3(x * inScaleX, y * inScaleY, 1.0);',
' vec3 position = vec3(x * scaleX, y * scaleY, 1.0);',
' float sine = sin(rotation);',
' float cosine = cos(rotation);',
' mat3 transformMatrix = mat3(',
' cosine, sine, 0,',
' -sine, cosine, 0,',
' inPositionX - uCameraScrollAndAlpha.x * inScrollFactorX, inPositionY - uCameraScrollAndAlpha.y * inScrollFactorY, 1',
' cosine, sine, 0.0,',
' -sine, cosine, 0.0,',
' positionX - uCameraScrollAndAlpha.x * scrollFactorX, positionY - uCameraScrollAndAlpha.y * scrollFactorY, 1.0',
' );',
' position = uViewMatrix * transformMatrix * position;',
' tint.rgb *= tint.a;',
' float alpha = inAlpha * uCameraScrollAndAlpha.z;',
' tint *= alpha;',
' alpha *= uCameraScrollAndAlpha.z;',
' tint.a *= alpha;',
' gl_Position = uProjectionMatrix * vec4(position.xy, 1.0, 1.0);',
' if (uRoundPixels == 1)',
' {',
' gl_Position.xy = floor(((gl_Position.xy + 1.0) * 0.5 * uResolution) + 0.5) / uResolution * 2.0 - 1.0;',
' }',
' outTexCoord = vec2(u, v);',
' outTint = mix(vec4(1.0, 1.0, 1.0, alpha), tint, tintBlend).bgra;',
' outTint = mix(vec4(1.0, 1.0, 1.0, tint.a), tint, tintBlend);',
' outTintEffect = tintFill;',
'}',
].join('\n');

View file

@ -16,13 +16,15 @@ void main ()
{
vec4 texture = texture2D(uMainSampler, outTexCoord);
vec4 texel = vec4(outTint.bgr * outTint.a, outTint.a);
// Multiply texture tint
vec4 color = texture * outTint;
vec4 color = texture * texel;
if (outTintEffect == 1.0)
{
// Solid color + texture alpha
color.rgb = mix(texture.rgb, outTint.rgb, texture.a);
color.rgb = mix(texture.rgb, texel.rgb, texture.a);
}
gl_FragColor = color;

View file

@ -7,6 +7,7 @@ uniform mat3 uViewMatrix;
uniform vec3 uCameraScrollAndAlpha;
uniform int uRoundPixels;
uniform vec2 uResolution;
uniform float uTime;
// Vertex buffer attributes
@ -14,33 +15,342 @@ uniform vec2 uResolution;
attribute float inVertex;
// Instance buffer attributes
attribute float inPositionX;
attribute float inPositionY;
attribute float inRotation;
attribute float inScaleX;
attribute float inScaleY;
attribute vec2 inOrigin;
attribute float inScrollFactorX;
attribute float inScrollFactorY;
attribute vec4 inPositionX;
attribute vec4 inPositionY;
attribute vec4 inRotation;
attribute vec4 inScaleX;
attribute vec4 inScaleY;
attribute vec3 inOriginAndTintFill;
attribute vec4 inScrollFactorX;
attribute vec4 inScrollFactorY;
attribute vec4 inFrameUVs;
attribute vec2 inTintFillAndBlend;
attribute vec4 inTintBlend;
attribute vec4 inTintTL;
attribute vec4 inTintTR;
attribute vec4 inTintBL;
attribute vec4 inTintBR;
attribute float inAlpha;
attribute vec4 inAlpha;
varying vec2 outTexCoord;
varying float outTintEffect;
varying vec4 outTint;
const float PI = 3.14159265359;
const float HALF_PI = PI / 2.0;
const float BOUNCE_OVERSHOOT = 1.70158;
const float BOUNCE_OVERSHOOT_PLUS = BOUNCE_OVERSHOOT + 1.0;
const float BOUNCE_OVERSHOOT_IN_OUT = BOUNCE_OVERSHOOT * 1.525;
const float BOUNCE_OVERSHOOT_IN_OUT_PLUS = BOUNCE_OVERSHOOT_IN_OUT + 1.0;
float animate (vec4 anim)
{
float value = anim.x;
float a = anim.y;
float b = anim.z;
float c = anim.w;
int type = int(floor(c));
if (type == 0 || a == 0.0 || b == 0.0)
{
// None
return value;
}
float duration = b;
float offset = mod(c, 1.0) * b;
bool yoyo = b < 0.0;
float time = mod(uTime / duration + offset, 1.0);
if (yoyo)
{
if (time < 0.5)
{
time = time * 2.0;
}
else
{
time = 1.0 - ((time - 0.5) * 2.0);
}
}
if (type == 1)
{
// Linear
return value + a * time;
}
else if (type == 10)
{
// Quad.easeOut
return value + a * time * (2.0 - time);
}
else if (type == 11)
{
// Quad.easeIn
return value + a * time * time;
}
else if (type == 12)
{
// Quad.easeInOut
time *= 2.0;
if (time < 1.0)
{
return value + a * time * time * 0.5;
}
time -= 1.0;
return value + a * -0.5 * (time * (time - 2.0) - 1.0);
}
else if (type == 20)
{
// Cubic.easeOut
time -= 1.0;
return value + a * (time * time * time + 1.0);
}
else if (type == 21)
{
// Cubic.easeIn
return value + a * time * time * time;
}
else if (type == 22)
{
// Cubic.easeInOut
time *= 2.0;
if (time < 1.0)
{
return value + a * time * time * time * 0.5;
}
time -= 2.0;
return value + a * 0.5 * (time * time * time + 2.0);
}
else if (type == 30)
{
// Quart.easeOut
time -= 1.0;
return value + a * (1.0 - time * time * time * time);
}
else if (type == 31)
{
// Quart.easeIn
return value + a * time * time * time * time;
}
else if (type == 32)
{
// Quart.easeInOut
time *= 2.0;
if (time < 1.0)
{
return value + a * time * time * time * time * 0.5;
}
time -= 2.0;
return value + a * -0.5 * (time * time * time * time - 2.0);
}
else if (type == 40)
{
// Quint.easeOut
time -= 1.0;
return value + a * (time * time * time * time * time + 1.0);
}
else if (type == 41)
{
// Quint.easeIn
return value + a * time * time * time * time * time;
}
else if (type == 42)
{
// Quint.easeInOut
time *= 2.0;
if (time < 1.0)
{
return value + a * time * time * time * time * time * 0.5;
}
time -= 2.0;
return value + a * 0.5 * (time * time * time * time * time + 2.0);
}
else if (type == 50)
{
// Sine.easeOut
return value + a * sin(time * HALF_PI);
}
else if (type == 51)
{
// Sine.easeIn
return value + a * (1.0 - cos(time * HALF_PI));
}
else if (type == 52)
{
// Sine.easeInOut
return value + a * 0.5 * (1.0 - cos(PI * time));
}
else if (type == 60)
{
// Expo.easeOut
return value + a * (1.0 - pow(2.0, -10.0 * time));
}
else if (type == 61)
{
// Expo.easeIn
return value + a * pow(2.0, 10.0 * (time - 1.0) - 0.001);
}
else if (type == 62)
{
// Expo.easeInOut
time *= 2.0;
if (time < 1.0)
{
return value + a * 0.5 * pow(2.0, 10.0 * (time - 1.0));
}
time -= 1.0;
return value + a * 0.5 * (2.0 - pow(2.0, -10.0 * (time - 1.0)));
}
else if (type == 70)
{
// Circ.easeOut
time -= 1.0;
return value + a * sqrt(1.0 - time * time);
}
else if (type == 71)
{
// Circ.easeIn
return value + a * (1.0 - sqrt(1.0 - time * time));
}
else if (type == 72)
{
// Circ.easeInOut
time *= 2.0;
if (time < 1.0)
{
return value + a * -0.5 * (sqrt(1.0 - time * time) - 1.0);
}
time -= 2.0;
return value + a * 0.5 * (sqrt(1.0 - time * time) + 1.0);
}
else if (type == 90)
{
// Back.easeOut
time -= 1.0;
return value + a * (time * time * (BOUNCE_OVERSHOOT_PLUS * time + BOUNCE_OVERSHOOT) + 1.0);
}
else if (type == 91)
{
// Back.easeIn
return value + a * time * time * (BOUNCE_OVERSHOOT_PLUS * time - BOUNCE_OVERSHOOT);
}
else if (type == 92)
{
// Back.easeInOut
time *= 2.0;
if (time < 1.0)
{
return value + a * 0.5 * (time * time * (BOUNCE_OVERSHOOT_IN_OUT_PLUS * time - BOUNCE_OVERSHOOT_IN_OUT));
}
time -= 2.0;
return value + a * 0.5 * (time * time * (BOUNCE_OVERSHOOT_IN_OUT_PLUS * time + BOUNCE_OVERSHOOT_IN_OUT) + 2.0);
}
else if (type == 100)
{
// Bounce.easeOut
if (time < 1.0 / 2.75)
{
return value + a * (7.5625 * time * time);
}
else if (time < 2.0 / 2.75)
{
time -= 1.5 / 2.75;
return value + a * (7.5625 * time * time + 0.75);
}
else if (time < 2.5 / 2.75)
{
time -= 2.25 / 2.75;
return value + a * (7.5625 * time * time + 0.9375);
}
else
{
time -= 2.625 / 2.75;
return value + a * (7.5625 * time * time + 0.984375);
}
}
else if (type == 101)
{
// Bounce.easeIn
time = 1.0 - time;
if (time < 1.0 / 2.75)
{
return value + a * (1.0 - 7.5625 * time * time);
}
else if (time < 2.0 / 2.75)
{
time -= 1.5 / 2.75;
return value + a * (1.0 - (7.5625 * time * time + 0.75));
}
else if (time < 2.5 / 2.75)
{
time -= 2.25 / 2.75;
return value + a * (1.0 - (7.5625 * time * time + 0.9375));
}
else
{
time -= 2.625 / 2.75;
return value + a * (1.0 - (7.5625 * time * time + 0.984375));
}
}
else if (type == 102)
{
bool reverse = false;
// Bounce.easeInOut
if (time < 0.5)
{
time = 1.0 - time * 2.0;
reverse = true;
}
if (time < 1.0 / 2.75)
{
time = 7.5625 * time * time;
}
else if (time < 2.0 / 2.75)
{
time -= 1.5 / 2.75;
time = 7.5625 * time * time + 0.75;
}
else if (time < 2.5 / 2.75)
{
time -= 2.25 / 2.75;
time = 7.5625 * time * time + 0.9375;
}
else
{
time -= 2.625 / 2.75;
time = 7.5625 * time * time + 0.984375;
}
if (reverse)
{
return (1.0 - time) * 0.5;
}
return time * 0.5 + 0.5;
}
// Default (should not happen)
return value;
}
void main ()
{
float tintFill = inTintFillAndBlend.x;
float tintBlend = inTintFillAndBlend.y;
float positionX = animate(inPositionX);
float positionY = animate(inPositionY);
float rotation = animate(inRotation);
float scaleX = animate(inScaleX);
float scaleY = animate(inScaleY);
float scrollFactorX = animate(inScrollFactorX);
float scrollFactorY = animate(inScrollFactorY);
float tintBlend = animate(inTintBlend);
float alpha = animate(inAlpha);
float x = -inOrigin.x;
float y = -inOrigin.y;
vec2 origin = inOriginAndTintFill.xy;
float tintFill = inOriginAndTintFill.z;
float x = -origin.x;
float y = -origin.y;
float u = inFrameUVs.x;
float v = inFrameUVs.y;
vec4 tint = inTintTL;
@ -48,7 +358,7 @@ void main ()
if (inVertex == 0.0)
{
// Bottom-left
y = 1.0 - inOrigin.y;
y = 1.0 - origin.y;
v = inFrameUVs.w;
tint = inTintBL;
}
@ -56,8 +366,8 @@ void main ()
else if (inVertex == 2.0)
{
// Bottom-right
x = 1.0 - inOrigin.x;
y = 1.0 - inOrigin.y;
x = 1.0 - origin.x;
y = 1.0 - origin.y;
u = inFrameUVs.z;
v = inFrameUVs.w;
tint = inTintBR;
@ -65,29 +375,28 @@ void main ()
else if (inVertex == 3.0)
{
// Top-right
x = 1.0 - inOrigin.x;
x = 1.0 - origin.x;
u = inFrameUVs.z;
tint = inTintTR;
}
vec3 position = vec3(x * inScaleX, y * inScaleY, 1.0);
vec3 position = vec3(x * scaleX, y * scaleY, 1.0);
// Create and initialize the transform matrix.
float sine = sin(inRotation);
float cosine = cos(inRotation);
float sine = sin(rotation);
float cosine = cos(rotation);
mat3 transformMatrix = mat3(
cosine, sine, 0,
-sine, cosine, 0,
inPositionX - uCameraScrollAndAlpha.x * inScrollFactorX, inPositionY - uCameraScrollAndAlpha.y * inScrollFactorY, 1
cosine, sine, 0.0,
-sine, cosine, 0.0,
positionX - uCameraScrollAndAlpha.x * scrollFactorX, positionY - uCameraScrollAndAlpha.y * scrollFactorY, 1.0
);
// Transform the position.
position = uViewMatrix * transformMatrix * position;
// Tint handling.
tint.rgb *= tint.a;
float alpha = inAlpha * uCameraScrollAndAlpha.z;
tint *= alpha;
// Alpha handling.
alpha *= uCameraScrollAndAlpha.z;
tint.a *= alpha;
gl_Position = uProjectionMatrix * vec4(position.xy, 1.0, 1.0);
@ -97,6 +406,6 @@ void main ()
}
outTexCoord = vec2(u, v);
outTint = mix(vec4(1.0, 1.0, 1.0, alpha), tint, tintBlend).bgra;
outTint = mix(vec4(1.0, 1.0, 1.0, tint.a), tint, tintBlend);
outTintEffect = tintFill;
}