r3-legacy/src/game-lib-system-animation.js

887 lines
26 KiB
JavaScript
Raw Normal View History

2017-09-05 05:22:52 +02:00
/**
* System takes care of updating all the entities (based on their component data)
* @param apiSystem GameLib.API.System
* @constructor
*/
GameLib.System.Animation = function(
apiSystem
) {
GameLib.System.call(
this,
apiSystem
);
2017-10-03 14:50:34 +02:00
this.animations = {};
this.latest = {};
2017-09-05 05:22:52 +02:00
};
GameLib.System.Animation.prototype = Object.create(GameLib.System.prototype);
GameLib.System.Animation.prototype.constructor = GameLib.System.Animation;
GameLib.System.Animation.prototype.start = function() {
GameLib.System.prototype.start.call(this);
2017-10-03 14:50:34 +02:00
this.beforeRenderSubscription = GameLib.Event.Subscribe(
GameLib.Event.BEFORE_RENDER,
2017-10-05 14:35:22 +02:00
this.beforeRender.bind(this)
);
var animations = GameLib.EntityManager.Instance.queryComponents(GameLib.D3.Animation);
animations.map(function(animation){
animation.meshes.map(
function(mesh) {
this.attachAnimation(animation, mesh);
}.bind(this)
)
}.bind(this));
this.animationMeshAddedSubscription = GameLib.Event.Subscribe(
GameLib.Event.ANIMATION_MESH_ADDED,
2017-10-03 14:50:34 +02:00
function(data) {
2017-10-05 14:35:22 +02:00
this.attachAnimation(data.animation, data.mesh);
}.bind(this)
);
2017-09-14 10:50:09 +02:00
2017-10-05 14:35:22 +02:00
this.animationMeshRemovedSubscription = GameLib.Event.Subscribe(
GameLib.Event.ANIMATION_MESH_REMOVED,
function(data) {
this.detachAnimation(data.mesh);
}.bind(this)
)
};
GameLib.System.Animation.prototype.beforeRender = function(data) {
if (this.paused) {
return;
}
2017-10-05 14:35:22 +02:00
var delta = data.delta;
2017-09-14 10:50:09 +02:00
2017-10-05 14:35:22 +02:00
for (var property in this.animations) {
if (this.animations.hasOwnProperty(property)) {
2017-09-14 10:50:09 +02:00
2017-10-05 14:35:22 +02:00
var processing = [];
if (this.animations[property].length > 0) {
var rotationDone = false;
var positionDone = false;
var scaleDone = false;
for (var i = 0; i < this.animations[property].length; i++) {
if (this.animations[property][i].type === 'rotation' && !rotationDone) {
rotationDone = true;
processing.push(this.animations[property][i]);
}
if (this.animations[property][i].type === 'position' && !positionDone) {
// if (this.animations[property][i].animation.blocking.position) {
// positionDone = true;
// }
processing.push(this.animations[property][i]);
}
if (this.animations[property][i].type === 'scale' && !scaleDone) {
//scaleDone = true;
processing.push(this.animations[property][i]);
}
if (scaleDone && positionDone && rotationDone) {
break;
}
}
}
processing.map(
function(__property) {
return function(animationObject) {
2017-09-14 10:50:09 +02:00
2017-10-03 14:50:34 +02:00
var done = false;
2017-09-14 10:50:09 +02:00
2017-10-05 14:35:22 +02:00
var increment = 0;
2017-10-03 14:50:34 +02:00
if (animationObject.type === 'rotation') {
2017-10-05 14:35:22 +02:00
increment = animationObject.animation.rotationSpeed * delta;
}
2017-10-05 14:35:22 +02:00
if (animationObject.type === 'position') {
increment = animationObject.animation.translationSpeed * delta;
}
2017-10-05 14:35:22 +02:00
if (animationObject.type === 'scale') {
increment = animationObject.animation.scaleSpeed * delta;
}
if (animationObject.from < animationObject.to) {
2017-10-03 14:50:34 +02:00
animationObject.from += increment;
2017-10-05 14:35:22 +02:00
} else {
animationObject.from -= increment;
}
2017-10-05 14:35:22 +02:00
if (Math.abs(animationObject.from - animationObject.to) < increment) {
animationObject.mesh.instance[animationObject.type][animationObject.axis] = animationObject.to;
done = true;
} else {
animationObject.mesh.instance[animationObject.type][animationObject.axis] = animationObject.from;
2017-10-03 14:50:34 +02:00
}
2017-10-03 14:50:34 +02:00
if (done) {
2017-10-05 14:35:22 +02:00
var index = this.animations[__property].indexOf(animationObject);
this.animations[__property].splice(index, 1);
2017-10-03 14:50:34 +02:00
}
2017-10-05 14:35:22 +02:00
}.bind(this);
}.bind(this)(property)
);
}
}
};
2017-10-05 14:35:22 +02:00
GameLib.System.Animation.prototype.detachAnimation = function(mesh) {
2017-10-05 14:35:22 +02:00
var detached = false;
2017-10-05 14:35:22 +02:00
if (mesh.backupQuaternionAngleDescriptor) {
Object.defineProperty(
mesh.quaternion,
'angle',
mesh.backupQuaternionAngleDescriptor
);
2017-10-03 14:50:34 +02:00
2017-10-05 14:35:22 +02:00
delete mesh.backupQuaternionAngleDescriptor;
2017-10-03 14:50:34 +02:00
2017-10-05 14:35:22 +02:00
detached = true;
}
2017-10-03 14:50:34 +02:00
2017-10-05 14:35:22 +02:00
if (mesh.backupQuaternionAxisXDescriptor) {
Object.defineProperty(
mesh.quaternion.axis,
'x',
mesh.backupQuaternionAxisXDescriptor
);
2017-10-03 14:50:34 +02:00
2017-10-05 14:35:22 +02:00
delete mesh.backupQuaternionAxisXDescriptor;
2017-10-03 14:50:34 +02:00
2017-10-05 14:35:22 +02:00
detached = true;
}
2017-10-03 14:50:34 +02:00
2017-10-05 14:35:22 +02:00
if (mesh.backupQuaternionAxisYDescriptor) {
Object.defineProperty(
mesh.quaternion.axis,
'y',
mesh.backupQuaternionAxisYDescriptor
);
2017-10-03 14:50:34 +02:00
2017-10-05 14:35:22 +02:00
delete mesh.backupQuaternionAxisYDescriptor;
2017-10-03 14:50:34 +02:00
2017-10-05 14:35:22 +02:00
detached = true;
}
2017-10-03 14:50:34 +02:00
2017-10-05 14:35:22 +02:00
if (mesh.backupQuaternionAxisZDescriptor) {
Object.defineProperty(
mesh.quaternion.axis,
'z',
mesh.backupQuaternionAxisZDescriptor
);
2017-10-03 14:50:34 +02:00
2017-10-05 14:35:22 +02:00
delete mesh.backupQuaternionAxisXDescriptor;
2017-10-03 14:50:34 +02:00
2017-10-05 14:35:22 +02:00
detached = true;
}
2017-10-03 14:50:34 +02:00
2017-10-05 14:35:22 +02:00
if (mesh.backupRotationXDescriptor) {
Object.defineProperty(
mesh.rotation,
'x',
mesh.backupRotationXDescriptor
);
delete mesh.backupRotationXDescriptor;
detached = true;
}
if (mesh.backupRotationYDescriptor) {
Object.defineProperty(
mesh.rotation,
'y',
mesh.backupRotationYDescriptor
);
delete mesh.backupRotationYDescriptor;
detached = true;
}
if (mesh.backupRotationZDescriptor) {
Object.defineProperty(
mesh.rotation,
'z',
mesh.backupRotationZDescriptor
);
delete mesh.backupRotationZDescriptor;
detached = true;
}
if (mesh.backupPositionXDescriptor) {
Object.defineProperty(
mesh.position,
'x',
mesh.backupPositionXDescriptor
);
delete mesh.backupPositionXDescriptor;
detached = true;
}
if (mesh.backupPositionYDescriptor) {
Object.defineProperty(
mesh.position,
'y',
mesh.backupPositionYDescriptor
);
delete mesh.backupPositionYDescriptor;
detached = true;
}
if (mesh.backupPositionZDescriptor) {
Object.defineProperty(
mesh.position,
'z',
mesh.backupPositionZDescriptor
);
delete mesh.backupPositionZDescriptor;
detached = true;
}
if (mesh.backupScaleXDescriptor) {
Object.defineProperty(
mesh.scale,
'x',
mesh.backupScaleXDescriptor
);
delete mesh.backupScaleXDescriptor;
detached = true;
}
if (mesh.backupScaleYDescriptor) {
Object.defineProperty(
mesh.scale,
'y',
mesh.backupScaleYDescriptor
);
delete mesh.backupScaleYDescriptor;
detached = true;
}
if (mesh.backupScaleZDescriptor) {
Object.defineProperty(
mesh.scale,
'z',
mesh.backupScaleZDescriptor
);
delete mesh.backupScaleZDescriptor;
detached = true;
}
if (this.latest[mesh.id]) {
mesh.rotation.x = this.latest[mesh.id].rotation.x;
mesh.rotation.y = this.latest[mesh.id].rotation.y;
mesh.rotation.z = this.latest[mesh.id].rotation.z;
mesh.position.x = this.latest[mesh.id].position.x;
mesh.position.y = this.latest[mesh.id].position.y;
mesh.position.z = this.latest[mesh.id].position.z;
mesh.scale.x = this.latest[mesh.id].scale.x;
mesh.scale.y = this.latest[mesh.id].scale.y;
mesh.scale.z = this.latest[mesh.id].scale.z;
mesh.quaternion.axis.x = this.latest[mesh.id].quaternion.axis.x;
mesh.quaternion.axis.y = this.latest[mesh.id].quaternion.axis.y;
mesh.quaternion.axis.z = this.latest[mesh.id].quaternion.axis.z;
mesh.quaternion.angle = this.latest[mesh.id].quaternion.angle;
delete this.latest[mesh.id];
detached = true;
}
if (this.animations[mesh.id]) {
delete this.animations[mesh.id];
detached = true;
}
if (detached) {
mesh.updateInstance();
}
};
GameLib.System.Animation.prototype.attachAnimation = function(animation, mesh) {
/**
* Initialize the property with the original mesh z value
*/
this.latest[mesh.id] = {
rotation : {
x : mesh.rotation.x,
y : mesh.rotation.y,
z : mesh.rotation.z
},
position : {
x : mesh.position.x,
y : mesh.position.y,
z : mesh.position.z
},
scale : {
x : mesh.scale.x,
y : mesh.scale.y,
z : mesh.scale.z
},
quaternion : {
axis : {
x : mesh.quaternion.axis.x,
y : mesh.quaternion.axis.y,
z : mesh.quaternion.axis.z
},
angle : mesh.quaternion.angle
}
};
this.animations[mesh.id] = [];
if (mesh.backupRotationXDescriptor) {
throw new Error('already a backed up x descriptor');
}
mesh.backupQuaternionAngleDescriptor = Object.getOwnPropertyDescriptor(mesh.quaternion, 'angle');
mesh.backupQuaternionAxisXDescriptor = Object.getOwnPropertyDescriptor(mesh.quaternion.axis, 'x');
mesh.backupQuaternionAxisYDescriptor = Object.getOwnPropertyDescriptor(mesh.quaternion.axis, 'y');
mesh.backupQuaternionAxisZDescriptor = Object.getOwnPropertyDescriptor(mesh.quaternion.axis, 'z');
mesh.backupRotationXDescriptor = Object.getOwnPropertyDescriptor(mesh.rotation, 'x');
mesh.backupRotationYDescriptor = Object.getOwnPropertyDescriptor(mesh.rotation, 'y');
mesh.backupRotationZDescriptor = Object.getOwnPropertyDescriptor(mesh.rotation, 'z');
mesh.backupPositionXDescriptor = Object.getOwnPropertyDescriptor(mesh.position, 'x');
mesh.backupPositionYDescriptor = Object.getOwnPropertyDescriptor(mesh.position, 'y');
mesh.backupPositionZDescriptor = Object.getOwnPropertyDescriptor(mesh.position, 'z');
mesh.backupScaleXDescriptor = Object.getOwnPropertyDescriptor(mesh.scale, 'x');
mesh.backupScaleYDescriptor = Object.getOwnPropertyDescriptor(mesh.scale, 'y');
mesh.backupScaleZDescriptor = Object.getOwnPropertyDescriptor(mesh.scale, 'z');
Object.defineProperty(
mesh.quaternion,
'angle',
{
'get': this.getQuaternionAngle(mesh, animation),
'set': this.setQuaternionAngle(mesh, animation),
'configurable': true
}
);
Object.defineProperty(
mesh.quaternion.axis,
'x',
{
'get': this.getQuaternionAxisX(mesh, animation),
'set': this.setQuaternionAxisX(mesh, animation),
'configurable': true
}
);
Object.defineProperty(
mesh.quaternion.axis,
'y',
{
'get': this.getQuaternionAxisY(mesh, animation),
'set': this.setQuaternionAxisY(mesh, animation),
'configurable': true
}
);
Object.defineProperty(
mesh.quaternion.axis,
'z',
{
'get': this.getQuaternionAxisZ(mesh, animation),
'set': this.setQuaternionAxisZ(mesh, animation),
'configurable': true
}
);
Object.defineProperty(
mesh.rotation,
'x',
{
'get': this.getProperty(mesh, 'x', 'rotation'),
'set': this.setProperty(mesh, animation, 'x', 'rotation'),
'configurable': true
}
);
Object.defineProperty(
mesh.rotation,
'y',
{
'get': this.getProperty(mesh, 'y', 'rotation'),
'set': this.setProperty(mesh, animation, 'y', 'rotation'),
'configurable': true
}
);
Object.defineProperty(
mesh.rotation,
'z',
{
'get': this.getProperty(mesh, 'z', 'rotation'),
'set': this.setProperty(mesh, animation, 'z', 'rotation'),
'configurable': true
}
);
Object.defineProperty(
mesh.scale,
'x',
{
'get': this.getProperty(mesh, 'x', 'scale'),
'set': this.setProperty(mesh, animation, 'x', 'scale'),
'configurable': true
}
);
Object.defineProperty(
mesh.scale,
'y',
{
'get': this.getProperty(mesh, 'y', 'scale'),
'set': this.setProperty(mesh, animation, 'y', 'scale'),
'configurable': true
}
);
Object.defineProperty(
mesh.scale,
'z',
{
'get': this.getProperty(mesh, 'z', 'scale'),
'set': this.setProperty(mesh, animation, 'z', 'scale'),
'configurable': true
}
);
Object.defineProperty(
mesh.position,
'x',
{
'get': this.getProperty(mesh, 'x', 'position'),
'set': this.setProperty(mesh, animation, 'x', 'position'),
'configurable': true
}
);
Object.defineProperty(
mesh.position,
'y',
{
'get': this.getProperty(mesh, 'y', 'position'),
'set': this.setProperty(mesh, animation, 'y', 'position'),
'configurable': true
}
);
Object.defineProperty(
mesh.position,
'z',
{
'get': this.getProperty(mesh, 'z', 'position'),
'set': this.setProperty(mesh, animation, 'z', 'position'),
'configurable': true
}
);
};
2017-09-05 05:22:52 +02:00
2017-10-03 14:50:34 +02:00
GameLib.System.Animation.prototype.getQuaternionAngle = function(mesh, animation) {
return function() {
return;
/**
* TODO: fix this shit..
* Back up the current property descriptor
*/
mesh.animationObject = {
backupAngleDescriptor: Object.getOwnPropertyDescriptor(mesh.quaternion, 'angle'),
targetAngle: mesh.quaternion.angle,
angleIncrement: true,
intermediateAngle: mesh.quaternion.angle,
subscription: null,
inProcess: false,
blocking: animation.blocking//,
// callbacks : [],
// storedValues : []
};
var getIntermediateAngle = function () {
return mesh.animationObject.intermediateAngle;
};
var getTargetAngle = function () {
// if (mesh.animationObject.storedValues.length > 0) {
// return mesh.animationObject.storedValues[mesh.animationObject.storedValues.length - 1];
// }
return mesh.animationObject.targetAngle;
};
var animateRotation = function (value) {
mesh.animationObject.intermediateAngle += value;
var done = false;
if (mesh.animationObject.angleIncrement) {
/**
* We are rotating upwards
*/
if (mesh.animationObject.intermediateAngle >= mesh.animationObject.targetAngle) {
/**
* We need to stop
*/
done = true;
}
} else {
/**
* We are rotating downwards
*/
if (mesh.animationObject.intermediateAngle <= mesh.animationObject.targetAngle) {
/**
* We need to stop
*/
done = true;
}
}
2017-10-03 14:50:34 +02:00
if (done) {
/**
* We clamp to our target angle
*/
mesh.animationObject.intermediateAngle = mesh.animationObject.targetAngle;
/**
* We limit our intermediate angle between values of -pi and pi
*/
while (mesh.animationObject.intermediateAngle > Math.PI) {
mesh.animationObject.intermediateAngle -= (Math.PI * 2);
}
while (mesh.animationObject.intermediateAngle < -(Math.PI)) {
mesh.animationObject.intermediateAngle += (Math.PI * 2);
}
2017-10-03 14:50:34 +02:00
/**
* We apply our new intermediate angle to our target
*/
mesh.animationObject.targetAngle = mesh.animationObject.intermediateAngle;
}
2017-10-03 14:50:34 +02:00
/**
* Apply the actual rotation to the mesh
*/
mesh.updateInstanceRotationFromAxisAngle(mesh.quaternion.axis, mesh.animationObject.intermediateAngle);
/**
2017-10-03 14:50:34 +02:00
* Check again if we are done, we need to do some additional work -
*/
2017-10-03 14:50:34 +02:00
if (done) {
if (!mesh.animationObject.subscription) {
var message = 'mesh animation object subscription went missing for ';
message += mesh.name + ': ';
message += animation.name;
console.warn(message);
throw new Error(message);
}
/**
* Stop subscribing to before render events
*/
mesh.animationObject.subscription.remove();
/**
* @type {null}
*/
mesh.animationObject.subscription = null;
/**
* For some meshes, when we are done with the animation, we want to apply
* the current state of the mesh to the object data itself (i.e. update
* its vertices etc)
*/
if (animation.applyToMeshWhenDone) {
/**
* Now we say that our intermediate angle is zero, because we will apply our rotation
* and this will prevent us from re-registering a new 'animationRender' event
*/
mesh.animationObject.targetAngle = 0;
mesh.animationObject.intermediateAngle = 0;
/**
* Apply our position, rotation and scale to the mesh
*/
mesh.applyPositionRotationScale();
}
/**
* Tell our animation component that it is no longer in process...
* @type {boolean}
*/
mesh.animationObject.inProcess = false;
// if (mesh.animationObject.callbacks.length > 0) {
// var callback = mesh.animationObject.callbacks[0];
// mesh.animationObject.callbacks.splice(0,1);
// callback();
// }
// mesh.animationObject.storedValues = [];
}
};
}
};
GameLib.System.Animation.prototype.setQuaternionAngle = function(mesh, animation) {
2017-10-03 14:50:34 +02:00
return function(value) {
return;
/**
* TODO: update this shit
*/
/**
* Check if we have work to do
*/
if (mesh.animationObject.intermediateAngle === value) {
2017-10-03 14:50:34 +02:00
mesh.animationObject.inProcess = false;
2017-10-03 14:50:34 +02:00
/**
* Nothing to do
*/
return;
}
2017-10-03 14:50:34 +02:00
/**
* Check if we have another animation in process
*/
if (mesh.animationObject.inProcess && mesh.animationObject.blocking) {
2017-10-03 14:50:34 +02:00
console.log('another animation is already in process');
2017-10-03 14:50:34 +02:00
// setTargetAngle(value);
2017-10-03 14:50:34 +02:00
// GameLib.Utils.PushUnique(mesh.animationObject.storedValues, value);
//
// mesh.animationObject.callbacks.push(
// function(__value) {
// return function(){
// mesh.quaternion.angle = __value;
// }
// }(value)
// );
2017-10-03 14:50:34 +02:00
/**
* Respond that our angle is actually our target angle (it will be that soon)
*/
return;
}
/**
* We indicate that we now have an animation in process
* @type {boolean}
*/
mesh.animationObject.inProcess = true;
/**
* Ok - all good - lets start the animation
*/
if (mesh.animationObject.intermediateAngle > value) {
/**
* We will rotate towards by decrementing
*/
mesh.animationObject.angleIncrement = false;
} else {
/**
* We will rotate towards by incrementing
*/
mesh.animationObject.angleIncrement = true;
}
/**
* We say what our target angle is - when we reach our target angle, we want
* to stop our animation
*/
mesh.animationObject.targetAngle = value;
/**
* Now we subscribe to 'before render' events, and slowly increment the value
* @type {{fn, remove}}
*/
mesh.animationObject.subscription = GameLib.Event.Subscribe(
GameLib.Event.BEFORE_RENDER,
function(data) {
var increment = Math.abs(animation.rotationSpeed);
if (!mesh.animationObject.angleIncrement) {
increment *= -1;
}
2017-10-03 14:50:34 +02:00
animateRotation(data.delta * increment);
}
);
}
};
GameLib.System.Animation.prototype.getQuaternionAxisX = function(mesh, animation) {
return function() {
return this.latest[mesh.id].quaternion.axis.x;
}.bind(this);
};
GameLib.System.Animation.prototype.setQuaternionAxisX = function(mesh, animation) {
return function(value) {
this.latest[mesh.id].quaternion.axis.x = value;
}.bind(this);
};
GameLib.System.Animation.prototype.getQuaternionAxisY = function(mesh, animation) {
return function() {
return this.latest[mesh.id].quaternion.axis.y;
}.bind(this);
};
GameLib.System.Animation.prototype.setQuaternionAxisY = function(mesh, animation) {
return function(value) {
this.latest[mesh.id].quaternion.axis.y = value;
}.bind(this);
};
GameLib.System.Animation.prototype.getQuaternionAxisZ = function(mesh, animation) {
return function() {
return this.latest[mesh.id].quaternion.axis.z;
}.bind(this);
};
GameLib.System.Animation.prototype.setQuaternionAxisZ = function(mesh, animation) {
return function(value) {
this.latest[mesh.id].quaternion.axis.z = value;
}.bind(this);
};
GameLib.System.Animation.prototype.getProperty = function(mesh, axis, property) {
return function() {
return this.latest[mesh.id][property][axis];
}.bind(this);
};
GameLib.System.Animation.prototype.setProperty = function(mesh, animation, axis, property) {
return function(value) {
var from = Number(this.latest[mesh.id][property][axis]);
this.latest[mesh.id][property][axis] = value;
2017-10-05 14:35:22 +02:00
if (property === 'position') {
/**
* Look for other position animations
* TODO:check when not super exausted
*/
// var positionAnimationObject = this.animations[mesh.id].reduce(
// function(result, animationObject) {
//
// if (animationObject.type === 'position') {
// result = animationObject;
// }
//
// return result;
// },
// null
// );
/**
* We found another position animation - just update the 'to' property
*/
// if (positionAnimationObject) {
// positionAnimationObject.to = value;
// return;
// }
}
2017-10-03 14:50:34 +02:00
this.animations[mesh.id].push(
{
type : property,
axis : axis,
from : from,
to : value,
animation : animation,
mesh : mesh
}
);
}.bind(this)
};
/**
* Stop Animation System
*/
GameLib.System.Animation.prototype.stop = function() {
GameLib.System.prototype.stop.call(this);
this.beforeRenderSubscription.remove();
this.animationMeshAddedSubscription.remove();
2017-10-05 14:35:22 +02:00
this.animationMeshRemovedSubscription.remove();
this.animations = {};
2017-10-03 14:50:34 +02:00
var meshes = GameLib.EntityManager.Instance.queryComponents(GameLib.D3.Mesh);
meshes.map(
function(mesh) {
2017-10-05 14:35:22 +02:00
for (var property in this.latest) {
if (
this.latest.hasOwnProperty(property) &&
property === mesh.id
) {
this.detachAnimation(mesh);
}
2017-10-03 14:50:34 +02:00
}
2017-10-05 14:35:22 +02:00
}.bind(this)
);
2017-09-05 05:22:52 +02:00
};