In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

[BetweenAS3] Papervision Matrix3D Tween

/**
 * Copyright yonatan ( http://wonderfl.net/user/yonatan )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/1iHb
 */

// forked from nitoyon's [BetweenAS3] Random Text Tween
package {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.ui.Mouse;
    import flash.ui.MouseCursor;
    import org.libspark.betweenas3.easing.*;
    import org.papervision3d.lights.PointLight3D;
    import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
    import org.papervision3d.objects.DisplayObject3D;
    import org.papervision3d.objects.primitives.PaperPlane;
    import org.papervision3d.view.BasicView;

    [SWF (backgroundColor="0x000000")]

    public class FlashTest extends BasicView {
        public function FlashTest() {
            _camera.zoom = 100;
            Mouse.cursor = MouseCursor.BUTTON;

            var light:PointLight3D;
            light = new PointLight3D(false);
            light.x = 0;
            light.y = 100;
            light.z = -1000;

            var mat:FlatShadeMaterial = new FlatShadeMaterial(light, 0xFFCC44);
            mat.doubleSided = true;
            var p:PaperPlane = new PaperPlane(mat);
            scene.addChild(p);

            var dst:DisplayObject3D = new DisplayObject3D;
            dst.x = 100;
            dst.copyTransform(dst);

            var tween:PVMatrixTween = new PVMatrixTween();
            tween.target = p;
            tween.easing = Sine.easeOut;
            tween.matrix = dst.transform;
            tween.time = 1;

            function onClick(e:Event):void {
                dst.x = -100 + 200*Math.random();
                dst.y = -100 + 200*Math.random();
                dst.z = -100 + 200*Math.random();
                dst.scaleX = 1+Math.random();
                dst.scaleY = 1+Math.random();
                dst.scaleZ = 1+Math.random();
                dst.rotationX = 360*Math.random();
                dst.rotationY = 360*Math.random();

                dst.copyTransform(dst);

                tween.target = p; // reset initial matrix to the current one.
                tween.play();
            }

            stage.addEventListener("click", onClick);
            onClick(null);

            startRendering();
            
        }
    }
}

import org.libspark.betweenas3.BetweenAS3;
import org.libspark.betweenas3.core.easing.IEasing;
import org.libspark.betweenas3.core.ticker.ITicker;
import org.libspark.betweenas3.core.tweens.AbstractTween;
import org.libspark.betweenas3.core.tweens.IITween;
import org.papervision3d.core.math.Matrix3D;
import org.papervision3d.core.math.Number3D;
import org.papervision3d.core.math.Quaternion;
import org.papervision3d.objects.DisplayObject3D;

class PVMatrixTween extends AbstractTween
{
    /**
    * static ITicker cache.
    */
    private static var _ticker:ITicker;

    /**
    * initial Matrix3D storage.
    */
    private var _start:Matrix3D;

    /**
    * DisplayObject3D.
    */
    public function get target():DisplayObject3D { return _target; }
    public function set target(value:DisplayObject3D):void {
        value.copyTransform(value); // hack -- make sure the do3d's transform is up to date.
        _start = Matrix3D.clone(value.transform);
        _target = value;
    }
    private var _target:DisplayObject3D;

    /**
    * Tween easing.
    */
    public function get easing():IEasing { return _easing; }
    public function set easing(value:IEasing):void { _easing = value; }
    private var _easing:IEasing;

    /**
    * Destination transform matrix.
    */
    public function get matrix():Matrix3D { return _matrix; }
    public function set matrix(value:Matrix3D):void { _matrix = value; }
    private var _matrix:Matrix3D;

    /**
    * Set or get duration.
    */
    public function get time():Number { return _duration; }
    public function set time(value:Number):void { _duration = value; }

    /**
    * Constructor.
    *
    * @param duration sleep duration.
    * @param ticker   ticker object.
    * @param position initial position.
    */
    public function PVMatrixTween(ticker:ITicker = null, position:Number = 0) {
        if (!_ticker) {
            // create tmp tween to get the BetweenAS3's static ticker.
            var tmpTween:IITween = BetweenAS3.parallel() as IITween;
            _ticker = tmpTween.ticker;
        }

        super(ticker || _ticker, position);
    }

    /**
    * 更新処理を行う。
    */
    protected override function internalUpdate(time:Number):void
    {
        // check
        if (!_target) { throw new Error("target is not set"); }
        if (!_easing) { throw new Error("easing is not set"); }
        if (!_matrix) { throw new Error("matrix is not set"); }

        // get the factor (0.0~1.0)
        var factor:Number = 0.0;
        if (time > 0.0) {
            if (time < _duration) {
                factor = _easing.calculate(time, 0.0, 1.0, _duration);
            }
            else {
                factor = 1.0;
            }
        }

        // update the matrix
        _target.copyTransform(interpolateMatrix3D(_start, _matrix, factor));
    }

    /**
    * 3D utils 
    */

    private function interpolateMatrix3D(thisMat:Matrix3D,toMat:Matrix3D,percent:Number):Matrix3D{
        // decomposition:
        
        // we need to give Quaternion.createFromMatrix rotation-only matrices (with normalized axii), 
        // otherwise we choke on scaling matrices
        
        var thisQuat:Quaternion = Quaternion.createFromMatrix( getRotationMatrix( thisMat ) );
        var toQuat:Quaternion = Quaternion.createFromMatrix( getRotationMatrix( toMat ) );
        
        var thisScale:Number3D = getScaleFromMatrix3D( thisMat );
        var toScale:Number3D = getScaleFromMatrix3D( toMat );
        
        var thisTranslation:Number3D = new Number3D( thisMat.n14, thisMat.n24, thisMat.n34 );
        var toTranslation:Number3D = new Number3D( toMat.n14, toMat.n24, toMat.n34 );
        
        // interpolation:
        
        var scale_x:Number = thisScale.x*(1-percent) + toScale.x*percent;
        var scale_y:Number = thisScale.y*(1-percent) + toScale.y*percent;
        var scale_z:Number = thisScale.z*(1-percent) + toScale.z*percent;
        
        var tx:Number = thisTranslation.x*(1-percent) + toTranslation.x*percent;
        var ty:Number = thisTranslation.y*(1-percent) + toTranslation.y*percent;
        var tz:Number = thisTranslation.z*(1-percent) + toTranslation.z*percent;

        var q:Quaternion = Quaternion.slerp( thisQuat, toQuat, percent );

        // recomposition:

        var x:Number = q.x;
        var y:Number = q.y;
        var z:Number = q.z;
        var w:Number = q.w;
        
        var ret:Matrix3D = new Matrix3D;

        // apparently mxmlc optimizes away the duplicate multiplications, 
        // so there's no need to do it manually
        ret.n11 = (1-2*y*y-2*z*z)*scale_x;
        ret.n21 = (2*x*y+2*w*z)*scale_x;
        ret.n31 = (2*x*z-2*w*y)*scale_x;
        ret.n41 = 0;
        ret.n12 = (2*x*y-2*w*z)*scale_y;
        ret.n22 = (1-2*x*x-2*z*z)*scale_y;
        ret.n32 = (2*y*z+2*w*x)*scale_y;
        ret.n42 = 0;
        ret.n13 = (2*x*z+2*w*y)*scale_z;
        ret.n23 = (2*y*z-2*w*x)*scale_z;
        ret.n33 = (1-2*x*x-2*y*y)*scale_z;
        ret.n43 = 0;
        ret.n14 = tx;
        ret.n24 = ty;
        ret.n34 = tz;
        ret.n44 = 1;
        
        return ret;
    }
    
    // scale values can't be negative!
    private function getScaleFromMatrix3D( m:Matrix3D ):Number3D {
        var x:Number = Math.sqrt( m.n11*m.n11 + m.n21*m.n21 + m.n31*m.n31 );
        var y:Number = Math.sqrt( m.n12*m.n12 + m.n22*m.n22 + m.n32*m.n32 );
        var z:Number = Math.sqrt( m.n13*m.n13 + m.n23*m.n23 + m.n33*m.n33 );
        return( new Number3D( x, y, z ) );
    }

    // returns a rotation-only matrix (no scale/translate)
    private function getRotationMatrix( m:Matrix3D ):Matrix3D {
        var scale:Number3D = getScaleFromMatrix3D( m );
        return( new Matrix3D ( [
                    m.n11 / scale.x, m.n12 / scale.y, m.n13 / scale.z, 0,
                    m.n21 / scale.x, m.n22 / scale.y, m.n23 / scale.z, 0,
                    m.n31 / scale.x, m.n32 / scale.y, m.n33 / scale.z, 0,
                    0, 0, 0, 1 
                ] ) );
    }
}