Files
GDevelop/Extensions/SkeletonObject/Ask.js
T
2018-07-04 22:26:02 +01:00

399 lines
11 KiB
JavaScript

/**
GDevelop - Skeleton Object Extension
Copyright (c) 2017-2018 Franco Maciel (francomaciel10@gmail.com)
This project is released under the MIT License.
*/
/**
* @memberof gdjs.sk
*/
gdjs.sk = gdjs.sk || {
// Some useful constants
SLOT_UNDEFINED: -1,
SLOT_IMAGE: 0,
SLOT_MESH: 1,
SLOT_POLYGON: 2,
SLOT_ARMATURE: 3,
EASING_CONST: 0,
EASING_LINEAR: 1,
EASING_CURVE: 2,
EVENT_STOP: 0,
EVENT_PLAY: 1,
EVENT_PLAYSINGLE: 2
};
/**
* The Matrix holds the basic transformation data in a matrix form.
*
* @memberof gdjs.sk
* @class Matrix
*/
gdjs.sk.Matrix = function(a=1, b=0, tx=0, c=0, d=1, ty=0){
this.a = a; this.b = b; this.tx = tx;
this.c = c; this.d = d; this.ty = ty;
this.u = 0; this.v = 0; this.w = 1;
};
gdjs.sk.Matrix.prototype.translation = function(x, y){
this.tx = x;
this.ty = y;
return this;
};
gdjs.sk.Matrix.prototype.rotation = function(angle){
this.a = Math.cos(angle); this.b = -Math.sin(angle);
this.c = Math.sin(angle); this.d = Math.cos(angle);
return this;
};
gdjs.sk.Matrix.prototype.scale = function(sx, sy){
this.a = sx;
this.d = sy;
return this;
};
gdjs.sk.Matrix.prototype.clone = function(){
return new gdjs.sk.Matrix(this.a, this.b, this.tx,
this.c, this.d, this.ty,
this.u, this.v, this.w );
};
gdjs.sk.Matrix.prototype.mul = function(m){
return new gdjs.sk.Matrix(this.a*m.a + this.b*m.c,
this.a*m.b + this.b*m.d,
this.a*m.tx + this.b*m.ty + this.tx,
this.c*m.a + this.d*m.c,
this.c*m.b + this.d*m.d,
this.c*m.tx + this.d*m.ty + this.ty);
};
gdjs.sk.Matrix.prototype.mulVec = function(v){
return [this.a*v[0] + this.b*v[1] + this.tx,
this.c*v[0] + this.d*v[1] + this.ty];
};
gdjs.sk.Matrix.prototype.invert = function(){
var det_inv = 1.0 / (this.a*this.d - this.b*this.c);
var a = this.a;
var tx = this.tx;
this.tx = (this.b*this.ty - this.d*tx)*det_inv;
this.ty = (this.c*tx - this.a*this.ty)*det_inv;
this.a = this.d*det_inv;
this.b =-this.b*det_inv;
this.c =-this.c*det_inv;
this.d = a*det_inv;
return this;
};
gdjs.sk.Matrix.prototype.inverse = function(){
var det_inv = 1.0 / (this.a*this.d - this.b*this.c);
return new gdjs.sk.Matrix( this.d*det_inv,
-this.b*det_inv,
(this.b*this.ty - this.d*this.tx)*det_inv,
-this.c*det_inv,
this.a*det_inv,
(this.c*this.tx - this.a*this.ty)*det_inv);
};
gdjs.sk.Matrix.prototype.str = function(){
return "|" + this.a.toFixed(2) + ", " + this.b.toFixed(2) + ", " + this.tx.toFixed(2) + "|\n" +
"|" + this.c.toFixed(2) + ", " + this.d.toFixed(2) + ", " + this.ty.toFixed(2) + "|\n" +
"|" + this.u.toFixed(2) + ", " + this.v.toFixed(2) + ", " + this.w.toFixed(2) + "|\n";
};
/**
* The Transform is the basic class for transformable objects as bones, slots and armatures.
*
* @memberof gdjs.sk
* @class Transform
*/
gdjs.sk.Transform = function(x=0, y=0, rot=0, sx=1, sy=1){
this.parent = null;
this.children = [];
this.x = x;
this.y = y;
this.rot = rot * Math.PI / 180.0;
this.sx = sx;
this.sy = sy;
this.matrix = new gdjs.sk.Matrix();
this.worldMatrix = new gdjs.sk.Matrix();
this._updateMatrix = true;
this._updateWorldMatrix = false;
this.inheritTranslation = true;
this.inheritRotation = true;
this.inheritScale = true;
};
gdjs.sk.Transform.prototype.addChild = function(child){
this.children.push(child);
child.reparent(this);
};
gdjs.sk.Transform.prototype.reparent = function(parent){
if(this.parent){
this.parent.removeChild(this);
}
this.parent = parent;
this._updateWorldMatrix = true;
};
gdjs.sk.Transform.prototype.removeChild = function(child){
var index = this.children.indexOf(child);
if(index > -1){
this.children.splice(index, 1);
}
};
gdjs.sk.Transform.prototype.getX = function(){
return this.x;
};
gdjs.sk.Transform.prototype.setX = function(x){
if(this.x !== x){
this.x = x;
this._updateMatrix = true;
}
};
gdjs.sk.Transform.prototype.getGlobalX = function(){
this.updateParentsTransform();
return this.worldMatrix.tx;
};
gdjs.sk.Transform.prototype.setGlobalX = function(x){
var globalY = this.getGlobalY(); // Also updates parent transforms
var localPos = this.parent.worldMatrix.inverse().mulVec([x, globalY, 1.0]);
this.x = localPos[0];
this.y = localPos[1];
this._updateMatrix = true;
};
gdjs.sk.Transform.prototype.getY = function(){
return this.y;
};
gdjs.sk.Transform.prototype.setY = function(y){
if(this.y !== y){
this.y = y;
this._updateMatrix = true;
}
};
gdjs.sk.Transform.prototype.getGlobalY = function(){
this.updateParentsTransform();
return this.worldMatrix.ty;
};
gdjs.sk.Transform.prototype.setGlobalY = function(y){
var globalX = this.getGlobalX(); // Also updates parent transforms
var localPos = this.parent.worldMatrix.inverse().mulVec([globalX, y, 1.0]);
this.x = localPos[0];
this.y = localPos[1];
this._updateMatrix = true;
};
gdjs.sk.Transform.prototype.setPos = function(x, y){
if(this.x !== x || this.y !== y){
this.x = x;
this.y = y;
this._updateMatrix = true;
}
};
gdjs.sk.Transform.prototype.getRot = function(){
return this.rot * 180.0 / Math.PI;
};
gdjs.sk.Transform.prototype.setRot = function(angle){
angle *= Math.PI / 180.0;
if(this.rot !== angle){
this.rot = angle;
this._updateMatrix = true;
}
};
gdjs.sk.Transform.prototype.getGlobalRot = function(){
this.updateParentsTransform();
var sx = Math.sqrt(this.worldMatrix.a * this.worldMatrix.a +
this.worldMatrix.c * this.worldMatrix.c );
var sy = Math.sqrt(this.worldMatrix.b * this.worldMatrix.b +
this.worldMatrix.d * this.worldMatrix.d );
return Math.atan2(-this.worldMatrix.b/sy, this.worldMatrix.a/sx) * 180.0 / Math.PI;
};
gdjs.sk.Transform.prototype.setGlobalRot = function(rot){
var parentGlobalRot = this.parent ? this.parent.getGlobalRot() : 0;
this.rot = (rot - parentGlobalRot)*Math.PI/180.0;
this._updateMatrix = true;
};
gdjs.sk.Transform.prototype.getSx = function(){
return this.sx;
};
gdjs.sk.Transform.prototype.getSy = function(){
return this.sy;
};
gdjs.sk.Transform.prototype.setSx = function(sx){
if(this.sx !== sx){
this.sx = sx;
this._updateMatrix = true;
}
};
gdjs.sk.Transform.prototype.setSy = function(sy){
if(this.sy !== sy){
this.sy = sy;
this._updateMatrix = true;
}
};
gdjs.sk.Transform.prototype.setScale = function(sx, sy){
if(this.sx !== sx || this.sy !== sy){
this.sx = sx;
this.sy = sy;
this._updateMatrix = true;
}
};
gdjs.sk.Transform.prototype.move = function(x, y){
this.x += x;
this.y += y;
this._updateMatrix = true;
};
gdjs.sk.Transform.prototype.rotate = function(angle){
this.rot += angle * Math.PI / 180.0;
this._updateMatrix = true;
};
gdjs.sk.Transform.prototype.scale = function(sx, sy){
this.sx *= sx;
this.sy *= sy;
this._updateMatrix = true;
};
gdjs.sk.Transform.prototype.update = function(){
this.updateTransform();
for(var i=0; i<this.children.length; i++){
this.children[i].update();
}
};
gdjs.sk.Transform.prototype.updateTransform = function(){
var sin_rot, cos_rot;
if(this._updateMatrix || this._updateWorldMatrix){
sin_rot = Math.sin(this.rot);
cos_rot = Math.cos(this.rot);
}
if(this._updateMatrix){
this.matrix = new gdjs.sk.Matrix(this.sx*cos_rot,-this.sy*sin_rot, this.x,
this.sx*sin_rot, this.sy*cos_rot, this.y,
0, 0, 1);
}
if(this._updateMatrix || this._updateWorldMatrix){
if(!this.parent){
this.worldMatrix = this.matrix.clone();
}
else{
this.worldMatrix = this.parent.worldMatrix.mul(this.matrix);
if(!this.inheritRotation || !this.inheritScale){
if(this.inheritScale){ // Non iherited rotation
var worldSx = Math.sqrt(this.worldMatrix.a*this.worldMatrix.a +
this.worldMatrix.c*this.worldMatrix.c);
var worldSy = Math.sqrt(this.worldMatrix.b*this.worldMatrix.b +
this.worldMatrix.d*this.worldMatrix.d);
this.worldMatrix.a = worldSx*cos_rot;
this.worldMatrix.b = -worldSy*sin_rot;
this.worldMatrix.c = worldSx*sin_rot;
this.worldMatrix.d = worldSy*cos_rot;
}
else if(this.inheritRotation){ // Non inherited scale
var worldSx = Math.sqrt(this.worldMatrix.a*this.worldMatrix.a +
this.worldMatrix.c*this.worldMatrix.c);
var worldSy = Math.sqrt(this.worldMatrix.b*this.worldMatrix.b +
this.worldMatrix.d*this.worldMatrix.d);
this.worldMatrix.a *= this.sx / worldSx;
this.worldMatrix.b *= this.sy / worldSy;
this.worldMatrix.c *= this.sx / worldSx;
this.worldMatrix.d *= this.sy / worldSy;
}
else{ // Non inherited rotation nor scale
this.worldMatrix.a = this.sx*cos_rot;
this.worldMatrix.b = -this.sy*sin_rot;
this.worldMatrix.c = this.sx*sin_rot;
this.worldMatrix.d = this.sy*cos_rot;
}
}
if(!this.inheritTranslation){
this.worldMatrix.tx = this.x;
this.worldMatrix.ty = this.y;
}
}
for(var i=0; i<this.children.length; i++){
this.children[i]._updateWorldMatrix = true;
}
}
this._updateMatrix = false;
this._updateWorldMatrix = false;
};
gdjs.sk.Transform.prototype.updateParentsTransform = function(){
if(this.parent){
this.parent.updateParentsTransform();
}
this.updateTransform();
};
gdjs.sk.Transform.prototype.transformPolygon = function(vertices){
this.updateParentsTransform();
var worldPoly = new gdjs.Polygon();
for(var i=0; i<vertices.length; i++){
worldPoly.vertices.push(this.worldMatrix.mulVec(vertices[i]));
}
return worldPoly;
};
gdjs.sk.Transform._statics = {
transform: {
x: 0,
y: 0,
sx: 1,
sy: 1,
skx: 0,
sky: 0,
rot: 0
}
};
gdjs.sk.Transform.decomposeMatrix = function(matrix){
var transform = gdjs.sk.Transform._statics.transform;
transform.x = matrix.tx
transform.y = matrix.ty;
var sx = Math.sqrt(matrix.a*matrix.a + matrix.c*matrix.c);
var sy = Math.sqrt(matrix.b*matrix.b + matrix.d*matrix.d);
transform.sx = sx;
transform.sy = sy;
transform.skx = -Math.atan2(matrix.d, matrix.b) + Math.PI/2.0;
transform.sky = Math.atan2(matrix.c, matrix.a);
transform.rot = Math.atan2(-matrix.b/sy, matrix.a/sx);
return transform;
};