flash on 2010-4-8

by uwi
♥0 | Line 798 | Modified 2010-04-17 15:59:19 | MIT License
play

ActionScript3 source code

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

package {
    import org.papervision3d.view.BasicView;
    import flash.text.TextField;
    import flash.events.Event;
    import net.hires.debug.Stats;
    import org.papervision3d.materials.*;
    import org.papervision3d.objects.primitives.*;
    import org.papervision3d.core.proto.*;
    import flash.geom.*;
    import org.papervision3d.materials.utils.*;
    import org.papervision3d.objects.*;
    import org.papervision3d.core.math.*;
    
    [SWF(backgroundColor="0x000000", frameRate="60")]
    public class PV3D extends BasicView {
        private var _tf : TextField;
        private var m : MaterialObject3D;
        
        public function PV3D() {
            super(0, 0, true, false, "free");
            
            _tf = new TextField();
            addChild(_tf);
            _tf.textColor = 0xffffff;
            _tf.width = 200;
            _tf.height = 465;
                        
            m = new WireframeMaterial();
            m.doubleSided = true;
//            addChild(new Stats());
			var plane : Plane = new Plane(m, 1000, 1000, 5, 5);
			plane.localRotationY = 90;
			plane.x = 1000;
			scene.addChild(plane);
			
			camera.x = 1000;
			camera.y = 1000;
			camera.z = 1000;
			camera.lookAt(plane);
			
			var ml : MaterialsList = new MaterialsList();
			ml.addMaterial(m, "all");

			_l1 = new Link(new Vector3D(1000, 0, 0), 500, Vector3D.Y_AXIS, new Vector3D(-1, 0, 0), 0, 0, 0, null);
			_l2 = new Link(new Vector3D(1000, 0, 0), 500, Vector3D.Y_AXIS, new Vector3D(-1, 0, 0), 0, 0, 0, _l1);
			_b1 = new Cube(ml, 500, 80, 80);
			_b2 = new Cube(ml, 500, 80, 80);
			_l1.theta = 0.7;
			_l2.theta = 0.3;
			scene.addChild(_b1);
			scene.addChild(_b2);
			
			_camera = new OperableSphereCamera(new Number3D(0, 0, 0), 2000, new Number3D(0, 0, 1), new Number3D(0, 1, 0)		, stage, 0.005, 0.005);
            startRendering();
        }
        
        private var _l1 : Link;
        private var _l2 : Link;
        private var _b1 : Cube;
        private var _b2 : Cube;
        
        override protected function onRenderTick(e : Event = null) : void
        {
        		_l1.FK();
        		_tf.text = "";
        		renderBone(_b1, _l1);
        		renderBone(_b2, _l2);
        		tr(Link.FKFast(_l2));
            super.onRenderTick(e);
        }
        
        private function renderBone(b : DisplayObject3D, l : Link) : void
        {
        		var h : Vector3D = l.head();
        		tr(h);
        		b.x = (l.x.x + h.x) / 2;
        		b.y = (l.x.y + h.y) / 2;
        		b.z = (l.x.z + h.z) / 2; 
            
        		b.transform.n11 = -l.trans.rawData[0];
        		b.transform.n12 = -l.trans.rawData[1];
        		b.transform.n13 = -l.trans.rawData[2];
        		b.transform.n21 = l.trans.rawData[4];
        		b.transform.n22 = l.trans.rawData[5];
        		b.transform.n23 = l.trans.rawData[6];
        		b.transform.n31 = l.trans.rawData[8];
        		b.transform.n32 = l.trans.rawData[9];
        		b.transform.n33 = l.trans.rawData[10];
        }
        
        private function tr(...o : Array) : void
        {
            _tf.appendText(o + "\n");
            _tf.scrollV = _tf.maxScrollV;
        }
    }
}

import flash.geom.Matrix3D;
import flash.geom.Vector3D;

// Joint - Bone
class Link
{
	public var x : Vector3D;
	public var len : Number;
	public var n : Vector3D;
	public var f : Vector3D;
	public var theta : Number;
	public var mintheta : Number;
	public var maxtheta : Number;
	public var trans : flash.geom.Matrix3D;
	public var transL : flash.geom.Matrix3D;
	
	public var parent : Link;
	public var child : Link;
	
	public function Link(
		x : Vector3D, len : Number, 
		n : Vector3D, f : Vector3D, 
		theta : Number, mintheta : Number, maxtheta : Number, 
		parent : Link)
	{
		this.x = x;
		this.len = len;
		this.n = n; this.f = f;
		this.theta = theta; this.mintheta = mintheta; this.maxtheta = maxtheta;
		this.parent = parent;
		if(parent != null)parent.child = this;
		this.child = null;
		this.transL = new flash.geom.Matrix3D();
		this.trans = new flash.geom.Matrix3D();
	}
	
	public function calcTransLocal() : flash.geom.Matrix3D
	{
		transL.identity();
		transL.appendTranslation(f.x * len, f.y * len, f.z * len);
		transL.appendRotation(theta * 180 / Math.PI, n);
		return transL;
	}
	
	private static const ZERO : Vector3D = new Vector3D(0, 0, 0);
	
	public function head() : Vector3D
	{
		return trans.transformVector(ZERO);
	}
	
	public function FK() : void
	{
		if(parent != null){
			x = parent.trans.transformVector(ZERO);
			trans = parent.trans.clone();
		}else{
			trans.identity();
			trans.appendTranslation(x.x, x.y, x.z);
		}
		
		calcTransLocal();
		trans.prepend(transL);
		
		if(child != null){
			child.FK();
		}
	}
	
	// 葉の座標だけ求める
	public static function FKFast(targ : Link) : Vector3D
	{
		var ret : Vector3D = ZERO.clone();
		var r : Link = null;
		for(var l : Link = targ;l != null;l = l.parent){
			//l.calcTransLocal();
			ret = l.transL.transformVector(ret);
			r = l;
		}
		ret.x += r.x.x;
		ret.y += r.x.y;
		ret.z += r.x.z;
		return ret;
	}
	
	public function root() : Link
	{
		return parent != null ? parent.root() : this;
	}
	
	// thisの位置をtargにするための逆運動学
	public function IK(targ : Vector3D) : Array
	{
		// FKの微分を計算してJacobi行列を構成
		var seq : Array = [];
		var l : Link;
		var r : Link = null;
		for(l = this;l != null;l = l.parent){
			seq.push(l);
			r = l;
		}
		
		var J : Array = [];
		for(var i : uint = 0;i < seq.length;i++){
			var ret : Vector3D = ZERO.clone();
			for(l = this;l != null;l = l.parent){
				if(l == seq[i]){
					// 微分項
					var dTransL : flash.geom.Matrix3D = new flash.geom.Matrix3D();
					dTransL.appendTranslation(f.x * len, f.y * len, f.z * len);
					dTransL.append(dRotateMatrix(l.n, theta));
					ret = dTransL.transformVector(ret);
				}else{
					// 非微分項
					ret = l.transL.transformVector(ret);
				}
			}
			ret.x += r.x.x;
			ret.y += r.x.y;
			ret.z += r.x.z;
		}
		J.push([ret.x, ret.y, ret.z]);
		
		// TODO JからJ'を構成
		var UVS : Array = MatrixUtils.doSVD(J);
		var IS : Array = new Array(S[0].length);
		for(i = 0;i < IS.length;i++){
			IS[i] = new Array(S.length);
			for(j = 0;j < S.length;j++){
				IS[i][j] = (i != j || S[i][j] == 0) ? 0 : 1.0 / S[i][j];
			}
		}
		var JSharp : Array = MatrixUtils.mul(V, Matrix(IS, MatrixUtils.transpose(U)));
		
		return null;
	}
    public static function dRotateMatrix(axis : Vector3D, angle : Number) : flash.geom.Matrix3D
    {
        var nCos:Number	= -Math.sin(angle);
        var nSin:Number	= Math.cos(angle);
        var scos:Number	= -nCos;

        var sxy	:Number = axis.x * axis.y * scos;
        var syz	:Number = axis.y * axis.z * scos;
        var sxz	:Number = axis.x * axis.z * scos;
        var sz	:Number = nSin * axis.z;
        var sy	:Number = nSin * axis.y;
        var sx	:Number = nSin * axis.x;
        
        return new flash.geom.Matrix3D(Vector.<Number>([
        		nCos + axis.x * axis.x * scos, -sz + sxy, sy + sxz, 
        		sz + sxy, nCos + axis.y * axis.y * scos, -sx + syz,
        		-sy + sxz, sx + syz, nCos + axis.z * axis.z * scos
        		]));
    }
}

class MatrixUtils
{
	
	public static function transpose(mat : Array) : Array
	{
		var r : uint = mat.length;
		var c : uint = mat[0].length;
		var ret : Array = new Array(c);
		for(var i : uint = 0;i < c;i++){
			var row : Array = new Array(r);
			for(var j : uint = 0;j < r;j++){
				row[j] = mat[j][i];
			}
			ret[i] = row;
		}
		return ret;
	}
	
	public static function mul(a : Array, b : Array) : Array
	{
		var r : uint = a.length;
		var c : uint = b[0].length;
		var u : uint = a[0].length;
		if(u != b.length)return null;
		
		var ret : Array = new Array(r);
		for(var i : uint = 0;i < r;i++){
			var row : Array = new Array(c);
			for(var j : uint = 0;j < c;j++){
				var val : Number = 0;
				for(var k : uint = 0;k < u;k++){
					val += a[i][k] * b[k][j];
				}
				row[j] = val;
			}
			ret[i] = row;
		}
		return ret;
	}
	
    
	public static function doSVD(arg:Array) : Array
	{
		var m:uint = arg.length;
		var n:uint = arg[0].length;
		var a:Array = arg.concat();

		var nu:int = Math.min(m,n);
		var s : Array = new Array (Math.min(m+1,n));
		var i:int;

		var Ui : Array = new Array(m);
		for (i = 0; i < Ui.length; i++){
			Ui[i] = new Array(nu);
			for(j = 0;j < nu;j++){
				Ui[i][j] = 0;
			}
		}
			
		var Vi : Array = new Array(n);
		for (i = 0; i < Vi.length; i++){
			Vi[i] = new Array(m);
			for(j = 0;j < m;j++){
				Vi[i][j] = 0;
			}
		}
			
		var e:Array = new Array(n);
		var work:Array = new Array(m);
		var wantu:Boolean = true;
		var wantv:Boolean = true;

		// Reduce A to bidiagonal form, storing the diagonal elements
		// in s and the super-diagonal elements in e.

		var nct:int = Math.min(m-1,n);
		var nrt:int = Math.max(0,Math.min(n-2,m));
		var j:int;
		var t:Number;
		
		for (var k:int = 0; k < Math.max(nct,nrt); k++) {
			if (k < nct) {

				// Compute the transformation for the k-th column and
				// place the k-th diagonal in s[k].
				// Compute 2-norm of k-th column without under/overflow.
				s[k] = 0;
				for (i = k; i < m; i++) {
					s[k] = hypot(s[k],a[i][k]);
				}
				if (s[k] != 0.0) {
					if (a[k][k] < 0) {
						s[k] = -s[k];
					}
					for (i = k; i < m; i++) {
						a[i][k] /= s[k];
					}
					a[k][k] += 1;
				}
				s[k] = -s[k];
			}
			for (j = k+1; j < n; j++) {
				if ((k < nct) && (s[k] != 0.0))  {

					// Apply the transformation.

					t = 0;
					for (i = k; i < m; i++) {
						t += a[i][k] * a[i][j];
					}
					t = -t/a[k][k];
					for (i = k; i < m; i++) {
						a[i][j] += t*a[i][k];
					}
				}

				// Place the k-th row of A into e for the
				// subsequent calculation of the row transformation.

				e[j] = a[k][j];
				/*trace ('e[' + j + ']: ' + e[j]);*/
			}
			
			if (wantu && (k < nct)) {

				// Place the transformation in U for subsequent back
				// multiplication.

				for (i = k; i < m; i++) {
					Ui[i][k] = a[i][k];
				}
			} 

			if (k < nrt) {

				// Compute the k-th row transformation and place the
				// k-th super-diagonal in e[k].
				// Compute 2-norm without under/overflow.
				e[k] = 0;
				for (i = k+1; i < n; i++) {
					e[k] = hypot(e[k],e[i]);
				}
				if (e[k] != 0.0) {
					if (e[k+1] < 0.0) {
						e[k] = -e[k];
					}
					for (i = k+1; i < n; i++) {
						e[i] /= e[k];
					}
					e[k+1] += 1.0;
				}
				e[k] = -e[k];
				if ((k+1 < m) && (e[k] != 0.0)) {

					// Apply the transformation.

					for (i = k+1; i < m; i++) {
						work[i] = 0.0;
					}
					for (j = k+1; j < n; j++) {
						for (i = k+1; i < m; i++) {
							work[i] += e[j] * a[i][j];
						}
					}
					for (j = k+1; j < n; j++) {
						t = -e[j]/e[k+1];
						for (i = k+1; i < m; i++) {
							a[i][j] += t*work[i];
						}
					}
				}
				if (wantv) {

					// Place the transformation in V for subsequent
					// back multiplication.

					for (i = k+1; i < n; i++) {
						Vi[i][k] = e[i];
					}
				}
			}
		}

		// Set up the final bidiagonal matrix or order p.

		var p:int = Math.min(n,m+1);
		if (nct < n) {
			s[nct] = a[nct][nct];
		}
		if (m < p) {
			s[p-1] = 0.0;
		}
		if (nrt+1 < p) {
			e[nrt] = a[nrt][p-1];
		}
		e[p-1] = 0.0;

		// If required, generate U.

		if (wantu) {
			for (j = nct; j < nu; j++) {
				for (i = 0; i < m; i++) {
					Ui[i][j] = 0.0;
				}
				Ui[j][j] = 1.0;
			}
			
			for (k = nct-1; k >= 0; k--) {
				if (s[k] != 0.0) {
					for (j = k+1; j < nu; j++) {
						t = 0;
						for (i = k; i < m; i++) {
							t += Ui[i][k]*Ui[i][j];
						}
						
						t = -t/Ui[k][k];
					
						for (i = k; i < m; i++) {
							Ui[i][j] += t*Ui[i][k];
						}
					}
					for (i = k; i < m; i++ ) {
						Ui[i][k] = -Ui[i][k];
					}
					Ui[k][k] = 1.0 + Ui[k][k];
					for (i = 0; i < k-1; i++) {
						Ui[i][k] = 0.0;
					}
				} else {
					for (i = 0; i < m; i++) {
						Ui[i][k] = 0.0;
					}
					Ui[k][k] = 1.0;
				}
			}
		}

		// If required, generate V.

		if (wantv) {
			for (k = n-1; k >= 0; k--) {
				if ((k < nrt) && (e[k] != 0.0)) {
					for (j = k+1; j < nu; j++) {
						t = 0;
						for (i = k+1; i < n; i++) {
							t += Vi[i][k]*Vi[i][j];
						}
						t = -t/Vi[k+1][k];
						for (i = k+1; i < n; i++) {
							Vi[i][j] += t*Vi[i][k];
						}
					}
				}
				for (i = 0; i < n; i++) {
					Vi[i][k] = 0.0;
				}
				Vi[k][k] = 1.0;
			}
		}

		// Main iteration loop for the singular values.

		var pp:int = p-1;
		var iter:int = 0;
		var eps:Number = 1e-10;
		var tiny:Number = 1e-10;
		var ks:int;
		var ttt:Number;
		var f:Number;
		var kase:int;
		var iteration:int = 0;
		var debug:Boolean = false;
		while (p > 0) {
			/*if (iteration++ % 100 == 0) {
				trace('iteration: ' + iteration + ' p: ' + p);
				debug = true;
			} else debug = false;*/
			// Here is where a test for too many iterations would go.

			// This section of the program inspects for
			// negligible elements in the s and e arrays.  On
			// completion the variables kase and k are set as follows.

			// kase = 1     if s(p) and e[k-1] are negligible and k<p
			// kase = 2     if s(k) is negligible and k<p
			// kase = 3     if e[k-1] is negligible, k<p, and
			//              s(k), ..., s(p) are not negligible (qr step).
			// kase = 4     if e(p-1) is negligible (convergence).

			for (k = p-2; k >= -1; k--) {
				if (debug) {
					trace('k: ' + k);
				}
				if (k == -1) {
					break;
				}
				if (Math.abs(e[k]) <= tiny + eps*(Math.abs(s[k]) + Math.abs(s[k+1]))) {
					e[k] = 0;
					break;
				} else if (debug) {
					trace('e[k]: ' + Math.abs(e[k]) + ' sth: ' + (tiny + eps*(Math.abs(s[k]) + Math.abs(s[k+1]))));
				}
			}
			if (k == p-2) {
				kase = 4;
			} else {
				for (ks = p-1; ks >= k; ks--) {
					if (ks == k) {
						break;
					}
					ttt = (ks != p ? Math.abs(e[ks]) : 0.) + 
						(ks != k+1 ? Math.abs(e[ks-1]) : 0.);
					if (Math.abs(s[ks]) <= tiny + eps*ttt)  {
						s[ks] = 0.0;
						break;
					}
				}
				if (ks == k) {
					kase = 3;
				} else if (ks == p-1) {
					kase = 1;
				} else {
					kase = 2;
					k = ks;
				}
			}
			k++;

			// Perform the task indicated by kase.

			switch (kase) {

				// Deflate negligible s(p).

				case 1: {
					f = e[p-2];
					e[p-2] = 0.0;
					for (j = p-2; j >= k; j--) {
						t = hypot(s[j],f);
						cs = s[j]/t;
						sn = f/t;
						s[j] = t;
						if (j != k) {
							f = -sn*e[j-1];
							e[j-1] = cs*e[j-1];
						}
						if (wantv) {
							for (i = 0; i < n; i++) {
								t = cs*Vi[i][j] + sn*Vi[i][p-1];
								Vi[i][p-1] = -sn*Vi[i][j] + cs*Vi[i][p-1];
								Vi[i][j] = t;
							}
						}
					}
				}
				break;

				// Split at negligible s(k).

				case 2: {
					f = e[k-1];
					e[k-1] = 0.0;
					for (j = k; j < p; j++) {
						ttt = hypot(s[j],f);
						var cs:Number = s[j]/ttt;
						var sn:Number = f/ttt;
						s[j] = ttt;
						f = -sn*e[j];
						e[j] = cs*e[j];
						if (wantu) {
							for (i = 0; i < m; i++) {
								ttt = cs*Ui[i][j] + sn*Ui[i][k-1];
								Ui[i][k-1] = -sn*Ui[i][j] + cs*Ui[i][k-1];
								Ui[i][j] = ttt;
							}
						}
					}
				}
				break;

				// Perform one qr step.

				case 3: {

					// Calculate the shift.

					var scale:Number = Math.max(Math.max(Math.max(Math.max(
						Math.abs(s[p-1]),Math.abs(s[p-2])),Math.abs(e[p-2])), 
						Math.abs(s[k])),Math.abs(e[k]));
					var sp:Number = s[p-1]/scale;
					var spm1:Number = s[p-2]/scale;
					var epm1:Number = e[p-2]/scale;
					var sk:Number = s[k]/scale;
					var ek:Number = e[k]/scale;
					var b:Number = ((spm1 + sp)*(spm1 - sp) + epm1*epm1)/2.0;
					var c:Number = (sp*epm1)*(sp*epm1);
					var shift:Number = 0.0;
					if ((b != 0.0) || (c != 0.0)) {
						shift = Math.sqrt(b*b + c);
						if (b < 0.0) {
							shift = -shift;
						}
						shift = c/(b + shift);
					}
					f = (sk + sp)*(sk - sp) + shift;
					var g:Number = sk*ek;
					// Chase zeros.
					
					for (j = k; j < p-1; j++) {
						ttt = hypot(f,g);
						cs = f/ttt;
						sn = g/ttt;
						if (j != k) {
							e[j-1] = ttt;
						}
						f = cs*s[j] + sn*e[j];
						e[j] = cs*e[j] - sn*s[j];
						g = sn*s[j+1];
						s[j+1] = cs*s[j+1];
						if (wantv) {
							for (i = 0; i < n; i++) {
								ttt = cs*Vi[i][j] + sn*Vi[i][j+1];
								Vi[i][j+1] = -sn*Vi[i][j] + cs*Vi[i][j+1];
								Vi[i][j] = ttt;
							}
						}
						ttt = hypot(f,g);
						cs = f/ttt;
						sn = g/ttt;
						s[j] = ttt;
						f = cs*e[j] + sn*s[j+1];
						s[j+1] = -sn*e[j] + cs*s[j+1];
						g = sn*e[j+1];
						e[j+1] = cs*e[j+1];
						if (wantu && (j < m-1)) {
							for (i = 0; i < m; i++) {
								ttt = cs*Ui[i][j] + sn*Ui[i][j+1];
								Ui[i][j+1] = -sn*Ui[i][j] + cs*Ui[i][j+1];
								Ui[i][j] = ttt;
							}
						}
					}
					e[p-2] = f;
					iter = iter + 1;
				}
				break;

				// Convergence.

				case 4: {

					// Make the singular values positive.

					if (s[k] <= 0.0) {
						s[k] = (s[k] < 0.0 ? -s[k] : 0.0);
						if (wantv) {
							for (i = 0; i <= pp; i++) {
								Vi[i][k] = -Vi[i][k];
							}
						}
					}

					// Order the singular values.

					while (k < pp) {
						if (s[k] >= s[k+1]) {
							break;
						}
						ttt = s[k];
						s[k] = s[k+1];
						s[k+1] = ttt;
						if (wantv && (k < n-1)) {
							for (i = 0; i < n; i++) {
								ttt = Vi[i][k+1]; Vi[i][k+1] = Vi[i][k]; Vi[i][k] = ttt;
							}
						}
						if (wantu && (k < m-1)) {
							for (i = 0; i < m; i++) {
								ttt = Ui[i][k+1]; Ui[i][k+1] = Ui[i][k]; Ui[i][k] = ttt;
							}
						}
						k++;
					}
					iter = 0;
					p--;
				}
				break;
			}
		}
		return [Ui, Vi, s];
	}
	
	public static function hypot(a:Number, b:Number):Number {
      var r:Number;
      if (Math.abs(a) > Math.abs(b)) {
         r = b/a;
         r = Math.abs(a)*Math.sqrt(1+r*r);
      } else if (b != 0) {
         r = a/b;
         r = Math.abs(b)*Math.sqrt(1+r*r);
      } else {
         r = 0.0;
      }
      return r;
   }
}

import org.papervision3d.objects.*;
import org.papervision3d.core.math.*;

class PV3DUtils
{
    public static function rotate(x : Number3D, axis : Number3D, angle : Number) : Number3D
    {
        var nCos:Number	= Math.cos(angle);
        var nSin:Number	= Math.sin(angle);
        var scos:Number	= 1 - nCos;

        var sxy	:Number = axis.x * axis.y * scos;
        var syz	:Number = axis.y * axis.z * scos;
        var sxz	:Number = axis.x * axis.z * scos;
        var sz	:Number = nSin * axis.z;
        var sy	:Number = nSin * axis.y;
        var sx	:Number = nSin * axis.x;

        var nx : Number = (nCos + axis.x * axis.x * scos) * x.x + (-sz + sxy) * x.y + (sy + sxz) * x.z;
        var ny : Number = (sz + sxy) * x.x + (nCos + axis.y * axis.y * scos) * x.y + (-sx + syz) * x.z;
        var nz : Number = (-sy + sxz) * x.x + (sx + syz) * x.y + (nCos + axis.z * axis.z * scos) * x.z;

        x.x = nx; x.y = ny; x.z = nz;
        return x;
    }
    
    public static function rotateMulti(xs : Array, axis : Number3D, angle : Number) : void
    {
        var nCos:Number	= Math.cos(angle);
        var nSin:Number	= Math.sin(angle);
        var scos:Number	= 1 - nCos;

        var sxy	:Number = axis.x * axis.y * scos;
        var syz	:Number = axis.y * axis.z * scos;
        var sxz	:Number = axis.x * axis.z * scos;
        var sz	:Number = nSin * axis.z;
        var sy	:Number = nSin * axis.y;
        var sx	:Number = nSin * axis.x;

        for each(var x : Number3D in xs){
            var nx : Number = (nCos + axis.x * axis.x * scos) * x.x + (-sz + sxy) * x.y + (sy + sxz) * x.z;
            var ny : Number = (sz + sxy) * x.x + (nCos + axis.y * axis.y * scos) * x.y + (-sx + syz) * x.z;
            var nz : Number = (-sy + sxz) * x.x + (sx + syz) * x.y + (nCos + axis.z * axis.z * scos) * x.z;
	        x.x = nx; x.y = ny; x.z = nz;
        }
    }
    
    public static function setLookAt(d : DisplayObject3D, front : Number3D, up : Number3D) : void
    {
        var X : Number3D = Number3D.cross(front, up);
        var look : org.papervision3d.core.math.Matrix3D = d.transform;
        look.n11 = X.x; look.n21 = X.y; look.n31 = X.z;
        look.n12 = -up.x; look.n22 = -up.y; look.n32 = -up.z;
        look.n13 = front.x; look.n23 = front.y; look.n33 = front.z;
    }    
}

import org.papervision3d.cameras.*;

class QCamera3D extends Camera3D
{
    public var _up : Number3D; // カメラの上の向きの単位ベクトル
    protected var _front : Number3D;
    private var _prevDir : Number3D;
    
    public function QCamera3D(up : Number3D, front : Number3D= null)
    {
        super();
        _up = null;
        init(up, front);
    }
    
    // prevDirからcurDirへ向ける回転を_upにかけるだけ。カメラ自体に操作はしない
    public function rotate(curDir : Number3D) : void
    {
        if(_prevDir != null){
            var n : Number3D = Number3D.cross(curDir, _prevDir);
//            if(n.moduloSquared > 0.00000001){
            if(n.moduloSquared != 0){
                n.normalize();
                var angle : Number = Math.acos(Number3D.dot(_prevDir, curDir));
                if(_front != null){
                		PV3DUtils.rotate(_front, n, angle);
                }
           		PV3DUtils.rotate(_up, n, angle);
            }
        }
        _prevDir = curDir.clone();
    }
    
    // カメラを_frontのほうへ向ける
    public function head() : void
    {
        if(_front != null){
        		var Z : Number3D = _front.clone();
            Z.normalize();
            var X : Number3D = Number3D.cross(Z, _up);
            X.normalize();
            var Y : Number3D = Number3D.cross(Z, X);
            Y.normalize();

            var look : org.papervision3d.core.math.Matrix3D = this.transform;
            look.n11 = X.x*this.scaleX; look.n21 = X.y*this.scaleY; look.n31 = X.z*this.scaleZ;
            look.n12 = -Y.x*this.scaleX; look.n22 = -Y.y*this.scaleY; look.n32 = -Y.z*this.scaleZ;
            look.n13 = Z.x*this.scaleX; look.n23 = Z.y*this.scaleY; look.n33 = Z.z*this.scaleZ;
        }
    }
    
    public function init(up : Number3D = null, front : Number3D = null) : void
    {
        if(up != null){
            _up = up.clone();
            _up.normalize();
        }
        if(front != null){
            _front = front.clone();
            _front.normalize();
        }else{
            _front = null;
        }
        _prevDir = null;
    }
}

// 球面上を動き、球の中心を見るカメラ
class SphereCamera extends QCamera3D
{
    private var _O : Number3D; // 球の中心
    private var _R : Number; // 球の半径
    
    // O : 中心座標
    // R : 半径
    // front : カメラ正面の向きを表す単位ベクトル
    // up : カメラ上の向きを表す単位ベクトル
    public function SphereCamera(O : Number3D, R : Number, front : Number3D, up : Number3D) : void
    {
        super(up, front);
        _O = O;
        _R = R;
        move();
    }
    
    public function move(x : Number = 0, y : Number = 0) : void
    {
        if(x != 0 || y != 0){
		var X : Number3D = Number3D.cross(_up, _front);
		var axis : Number3D = new Number3D(X.x * -y + _up.x * x, X.y * -y + _up.y * x, X.z * -y + _up.z * x);
		var angle : Number = axis.modulo;
		axis.normalize();
		PV3DUtils.rotate(_front, axis, angle);
		PV3DUtils.rotate(_up, axis, angle);
		this.x = _front.x * -_R + _O.x;
		this.y = _front.y * -_R + _O.y;
		this.z = _front.z * -_R + _O.z;
		head();
        }
    }

}

import flash.display.*;
import flash.events.*;

class OperableSphereCamera extends SphereCamera
{
    private var _dx : Number;
    private var _dy : Number;
    private var _stage : DisplayObject;
    private var _prevX : Number = -1;
    private var _prevY : Number = -1;
    private var _down : Boolean = false;

    // O : 中心座標
    // R : 半径
    // front : カメラ正面の向きを表す単位ベクトル
    // up : カメラ上の向きを表す単位ベクトル
    // stage : マウスイベントをlistenするDisplayObject
    // dx : カメラ横への移動角度単位
    // dy : カメラ上への移動角度単位
    public function OperableSphereCamera(O : Number3D, R : Number, front : Number3D, up : Number3D, stage : DisplayObject, dx : Number, dy : Number) : void
    {
      super(O, R, front, up);
      _dx = dx;
      _dy = dy;
      _stage = stage;
      addCallback();
      move(0, 0.001);
    }
    
    public function addCallback() : void
    {
      _stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
      _stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
      _stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
    }
    
    public function removeCallback() : void
    {
      _stage.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
      _stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
      _stage.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
    }
    
    private function onMouseDown(e : MouseEvent) : void { _down = true; }
    private function onMouseUp(e : MouseEvent) : void { _down = false; }
    
    private function onEnterFrame(e : Event) : void
    {
      if(_down){
        if(_prevX != -1){
          this.move((_stage.mouseX - _prevX) * _dx, (_stage.mouseY - _prevY) * _dy);
        }
        _prevX = _stage.mouseX;
        _prevY = _stage.mouseY;
      }else{
        _prevX = -1;
        _prevY = -1;
      }
    }
}