import * as ECS from '../../../packages/ecs-js/index';
import {AffineType} from '../../xmath/vec';
import {Affine} from '../../xmath/affine';
/**Combine affine transformation animated by XTweener.
*
* For details, see <a href='https://odys-z.github.io/x-visual/design-memo/affine.html'>doc</a>
* @class AffineCombiner
*/
export default class AffineCombiner3 extends ECS.System {
constructor(ecs, x) {
super(ecs);
this.ecs = ecs;
console.log('AffineCombiner v3 ...');
var ents = ecs.queryEntities({iffall: ['Obj3', 'ModelSeqs', 'CmpTweens']});
if (ents || ents.length > 0)
this.initCombined(ents);
}
initCombined (entities) {
for (const e of entities) {
for (var seqx = 0; seqx < e.CmpTweens.twindx.length; seqx++) {
for (const twn of e.CmpTweens.tweens[seqx]) {
if (twn.affineCombine) { // flag set by animizer
if (!e.Obj3.m0) {
e.Obj3.m0 = new Affine();
if (e.Obj3.mesh)
e.Obj3.m0.decompose(e.Obj3.mesh.matrix);
e.Obj3.mi = new Affine();
e.CmpTweens.idle = true;
e.CmpTweens.idleRising = false;
e.CmpTweens.playRising = false;
e.CmpTweens._mfs = new Array(e.CmpTweens.twindx.length);
e.CmpTweens.mf_buff = new Array(e.CmpTweens.twindx.length);
}
twn.mf = new Affine();
}
}
if (e.CmpTweens && e.CmpTweens._mfs) {
e.CmpTweens._mfs[seqx] = new Affine();
e.CmpTweens.mf_buff[seqx] = new Affine();
}
}
}
}
update(tick, entities) {
for (const e of entities) {
// Only Obj3 with mi initialized by animizer is affine combinable,
// but we still handle ending events
if (e.Obj3.mi) {
e.Obj3.mi.i();
if (!e.CmpTweens.idle) {
var dirty = false;
dirty = this.combineUpdate(e.Obj3, e.CmpTweens);
if (dirty) {
this.combineEnd(e.Obj3, e.Obj3.mesh);
}
}
if (e.CmpTweens.idleRising) {
// see also XTweener.update of (twnx + 1 >= e.CmpTweens.tweens[seqx].length)
for (var fx = 0; fx < e.CmpTweens.endingFiring.length; fx++) {
if (e.CmpTweens._mfs[fx] && e.CmpTweens.endingFiring[fx]) {
e.CmpTweens._mfs[fx].copy(e.CmpTweens.mf_buff[fx]);
// if (e.CmpTweens.eFinished
// && typeof e.CmpTweens.eFinished === 'function') {
// // eFinished is a copy of ModelSeqs.fFinished
// e.CmpTweens.eFinished(e.CmpTweens, fx);
// }
}
}
}
}
if (e.CmpTweens.idleRising) {
for (let fx = 0; fx < e.CmpTweens.endingFiring.length; fx++)
if (typeof e.CmpTweens.eFinished === 'function' && e.CmpTweens.endingFiring[fx] === true){
e.CmpTweens.eFinished(e.CmpTweens, fx);
e.CmpTweens.endingFiring[fx] = false;
}
}
}
}
// pinned reference 96-131
/**
* Update affine combination for the object.
*
* @param {Obj3} obj3 combined target object with mi
* @param {CmpTweens} cmpTweens e.g. [{mi, translate: [x, y, z]}]
* @return {bool} dirty
* @member AffineCombiner#combineUpdate
*
*/
combineUpdate(obj3, cmpTweens) {
var dirty = false;
for (var seqx = 0; seqx < cmpTweens.twindx.length; seqx++) {
var twindx = cmpTweens.twindx[seqx];
if (twindx < cmpTweens.tweens[seqx].length
&& cmpTweens.mf_buff[seqx]) {
cmpTweens.mf_buff[seqx].i();
var tw = cmpTweens.tweens[seqx][twindx];
if ( tw.affineCombine ) {
dirty = true;
// combine the mf even it's stopped
if ( tw.affines && tw.affines.length > 0) {
if ( tw.isPlaying ) {
tw.mf.i();
for (var ax = 0; ax < tw.affines.length; ax++) {
tw.mf.appAffine(tw.affines[ax]);
}
}
cmpTweens.mf_buff[seqx].mul(tw.mf);
}
}
}
if (cmpTweens.mf_buff[seqx])
obj3.mi.mul(cmpTweens.mf_buff[seqx]);
}
return dirty;
}
// pinned end
/**Update combined object transform to Obj3.mesh.matrix.
*
* Must been called when z-transform is really increased - tweens updated in one loop.
* - needing first updating mesh matrix, then apply affine transformation to it.
*
* Three.js docs about the order:
*
* Convenience properties and matrixAutoUpdate,
*
* https://threejs.org/docs/#manual/en/introduction/Matrix-transformations
* @param {Obj3} obj3 combined
* @param {THREE.Mesh} mesh
* @member AffineCombiner#combineEnd
*/
combineEnd(obj3, mesh) {
mesh.matrixAutoUpdate = false;
if (obj3.mi) {
obj3.mi.mulpost(obj3.m0);
obj3.mi.composeTo( mesh.matrix );
mesh.matrix.decompose( mesh.position, mesh.quaternion, mesh.scale );
}
}
}
AffineCombiner3.query = {iffall: ['Obj3', 'ModelSeqs', 'CmpTweens']};