Decompose + TWEEN?

Is it possible to tween decompose instead of having to tween 3x vector3 for Pos, Rot, Scale? Or if not a way to tween between decompose is there a way to tween Pos, Rot, Scale at the same time? Ive tried but no success. Im running three tweens currently for the job. 1 per vector.

Are you using tween.js? Not clear what youā€™re asking.

Do you mean is it possible to tween a matrix? If so, then yes. You can tween the values in the matrix.elements array.

Thanks for the reply @looeee and @trusktr . I currently have an instancedMesh of elements that I tween. I tween their pos, rot, scale. But I do it with three different tweens. I would like to tween all three Vectors with one tween.

Something like:

new Tween( object.position, obj.rotation, obj.scale ).to( target.object.position, target.object.rotation, target.obj.scale )

Inside my tween I then convert the positions to a matrix and compose them and use the new composed matrix to update the matrix of the instances. So I know its obv possible to tween the matrices. I just want all three vectors in one tween.

@D13
Use .onUpdate() and lerp values for position, rotation and scale all at once there: Edit fiddle - JSFiddle - Code Playground

1 Like

@prisoner849 Boom! Thank you!

So, qq: I implemented this and indeed it works like a champ. A big tween performance enhancement. However, with this approach its similar to slerp. We turn 0 into 1 over series of steps. However, I need to preserve the current position of existing objects ā€œpanelCurrent.positionā€. It seems this solution starts at ā€˜val:0ā€™ every time. Is there a way it could start at the current objects position?

So the big diff here is going from a starting vector assumed at ā€˜current.object.positionā€™ vs a starting vector assumed from ā€˜0ā€™.

var obLen = targets.radial.length;
for( let i = 0; i < obLen; i++ ) {

     const panelCurrent = meshCache[ i ];
     const panelTarget = targets.radial[ i ];
     const panelMatrix = panelTarget.matrix;
     const orientation = panelCurrent.quaternion(); 
     const offset = new THREE.Vector3();
     const newRot = new THREE.Euler();   
     let valsCount = { val: 0 }
     let vals = {
          pos: new THREE.Vector3(),
          rot: new THREE.Vector3(),
          sca: new THREE.Vector3()
     }

     new TWEEN.Tween( valsCount ).to({ val: 1 }, Math.random() * duration * 2 ).onUpdate( function(){

       vals.pos.lerpVectors( panelCurrent.position, panelTarget.position, valsCount.val );
       vals.rot.lerpVectors( panelCurrent.rotation, panelTarget.rotation, valsCount.val );
       vals.sca.lerpVectors( panelCurrent.scale, panelCurrent.scale, valsCount.val );

       newRot.set( vals.rot.x, vals.rot.y, vals.rot.z );
       offset.set( vals.pos.x, vals.pos.y, vals.pos.z ).multiplyScalar( 0.075 );
       orientation.setFromEuler( newRot );
       panelMatrix.compose( offset, orientation, vals.sca );
       
       currentMesh.setMatrix( i, panelMatrix );
       currentMesh.instanceMatrix.needsUpdate = true;

     }).start();

}

What I miss? I need my positions to start from ā€˜panelCurrent.positionā€™. Thank you!

@D13
Use .onComplete() and save current values into initVals :thinking: Edit fiddle - JSFiddle - Code Playground

PS Reading ā€œUserā€™s guideā€ time to time is helpful :slight_smile: tween.js/user_guide.md at 8db070d25a82c19772352bbcbc33a326e6882b4e Ā· tweenjs/tween.js Ā· GitHub

1 Like

Thank You!

@D13 Youā€™re welcome :beers:

@D13 Besides lerping like @prisoner849 suggested, you can also animate any number if properties of an object. The following is closer to what you were thinking:

new Tween({ 
  position: object.position,
  rotation: obj.rotation,
  scale: obj.scale
}).to({
  position: target.object.position,
  rotation: target.object.rotation,
  scale: target.obj.scale
})

then in onUpdate you can read the values and use them on each update:

onUpdate(theValues => {
	console.log(theValues.position, theValues.rotation, theValues.scale)
})

where theValues is the same object you passed to new Tween, which gets modified on each update.

Iā€™ve seen that, that you can change nested objects, but seems I did something wrong so it didinā€™t work :thinking: :sweat_smile:

Ah! Made it working with nested objects: Edit fiddle - JSFiddle - Code Playground :partying_face:
Just had to care about box.rotation.order as itā€™s not a number, but a string, and it becomes NaN whilst tweening.

Very Nice, yes, I was able to use the onComplete solution just fine. I think as far as performance goes this is the best without going to a TWEEN Manager, which could be tuned to squeeze the last inth of performance when getting into those 10k+ instances. For now, Iā€™m happy with performance as it is. When I get into the big data Iā€™ll revisit this.

Solution for now:

new TWEEN.Tween({ val:0 }).to({ val: 1 }, Math.random() * duration * 2 )
.easing( TWEEN.Easing.Exponential.InOut )
.onUpdate( value => {

// lerp new positions
vals.pos.lerpVectors( panelCurrent.position, targets.radial[ i ].position, value.val );
vals.rot.lerpVectors( panelCurrent.rotation, targets.radial[ i ].rotation, value.val );
vals.sca.lerpVectors( panelCurrent.scale, targets.radial[ i ].scale, value.val );

// compose new matrix
newRot.set( vals.rot.x, vals.rot.y, vals.rot.z );
offset.set( vals.pos.x, vals.pos.y, vals.pos.z );
offset.multiplyScalar( 0.025 );
orientation.setFromEuler( newRot );
panelMatrix.compose( offset, orientation, vals.sca );

// update positions
currentMesh.setMatrixAt( i, panelMatrix );

// apply updates
currentMesh.instanceMatrix.needsUpdate = true;

}).onComplete(() => {

// reset positions
panelCurrent.position.set( vals.pos.x, vals.pos.y, vals.pos.z );
panelCurrent.rotation.set( vals.rot.x, vals.rot.y, vals.rot.z );
panelCurrent.scale.set( vals.sca.x, vals.sca.y, vals.sca.z );
offset.set( panelCurrent.position.x, panelCurrent.position.y, panelCurrent.position.z );
offset.multiplyScalar( 0.025 );
newRot.set( panelCurrent.rotation.x, panelCurrent.rotation.y, panelCurrent.rotation.z );
orientation.setFromEuler( newRot );
panelMatrix.compose( offset, orientation, panelCurrent.scale );

}).start();

Of course, there is the caveat that the ā€œonCompleteā€ has to finish its animation cycle to save the new position values. So, if a user clicks a new transition before the current one is finished then you get a snapping effect as the new positions are not saved and instead pull from the previous positions in the matrix. I can handle that thoughā€¦

For now, this was plenty good enough. A million thanks!

Oh I see. Ok, yes, this is nice too. Not having to use onComplete to save the new positions. I was going to play with a version of this soon.