1 /**
  2  * Hilo
  3  * Copyright 2015 alibaba.com
  4  * Licensed under the MIT License
  5  */
  6 
  7 /**
  8  * @class Camera3d 伪3D虚拟摄像机。
  9  * @module hilo/game/Camera3d
 10  * @requires hilo/core/Class
 11  * @requires hilo/util/util
 12  * @property {Number} fv 镜头视点距离(屏幕视点相对眼睛距离,绝对了坐标缩放比例)。
 13  * @property {Number} fx 镜头视点X(屏幕视点相对屏幕左上角X距离)。
 14  * @property {Number} fy 镜头视点Y(屏幕视点相对屏幕左上角Y距离)。
 15  * @property {Object} stage 3D对象所在容器,可以是stage或container,结合ticker时是必须参数,用来Z深度排序。
 16  * @property {Number} x 镜头三维坐标x。
 17  * @property {Number} y 镜头三维坐标y。
 18  * @property {Number} z 镜头三维坐标z。
 19  * @property {Number} rotationX X轴旋转角度。
 20  * @property {Number} rotationY Y轴旋转角度。
 21  * @property {Number} rotationZ Z轴旋转角度。
 22  */
 23 var Camera3d = (function(){
 24 
 25 	var degtorad = Math.PI / 180;
 26 
 27 	//Rotate the axis.
 28 	function rotateX(x, y, z, ca, sa) {//rotate x
 29 		return {
 30 			x: x,
 31 			y: y * ca - z * sa,
 32 			z: y * sa + z * ca
 33 		};
 34 	}
 35 	function rotateY(x, y, z, ca, sa) {//rotate y
 36 		return {
 37 			x: x * ca - z * sa,
 38 			y: y,
 39 			z: x * sa + z * ca
 40 		};
 41 	}
 42 	function rotateZ(x, y, z, ca, sa) {//rotate z
 43 		return {
 44 			x: x * ca - y * sa,
 45 			y: x * sa + y * ca,
 46 			z: z
 47 		};
 48 	}
 49 
 50 	var Camera3d = Class.create(/** @lends Camera3d.prototype */{
 51 
 52 		constructor: function(properties){
 53 			properties.x = properties.x || 0;
 54 			properties.y = properties.y || 0;
 55 			properties.z = properties.z || 0;
 56 			properties.rotationX = properties.rotationX || 0;
 57 			properties.rotationY = properties.rotationY || 0;
 58 			properties.rotationZ = properties.rotationZ || 0;
 59 
 60         	util.copy(this, properties);
 61 		},
 62 
 63 	    /**
 64          * 仿射矩阵位移变换,不同于直接修改Camera3d.x/y/z. 是在Camera3d依次做坐标位移 - 旋转变换 后,再加上一个位移变换。主要功能可以做Zoomin/out 功能
 65 	     * @param {Number} x x坐标
 66 	     * @param {Number} y y坐标
 67 	     * @param {Number} z z坐标
 68 	     */
 69 		translate : function(x,y,z){
 70 			this.tx = x;
 71 			this.ty = y;
 72 			this.tz = z;
 73 		},
 74 
 75 	    /**
 76          * 旋转X轴方向角度,相当于欧拉角系统的 beta。
 77 	     * @param {Number} angle 旋转角度。
 78 	     */
 79 		rotateX : function(angle){
 80 			this.rotationX = angle;
 81 		},
 82 
 83 	    /**
 84          * 旋转Y轴方向角度,相当于欧拉角系统的 gamma。
 85 	     * @param {Number} angle 旋转角度。
 86 	     */
 87 		rotateY : function(angle){
 88 			this.rotationY = angle;
 89 		},
 90 
 91 	    /**
 92          * 旋转Z轴方向角度,相当于欧拉角系统的 alpha。
 93 	     * @param {Number} angle 旋转角度。
 94 	     */
 95 		rotateZ : function(angle){
 96 			this.rotationZ = angle;
 97 		},
 98 
 99 	    /**
100          * 将三维坐标转换投影为二维坐标。
101 	     * @param {object} vector3D 三维坐标对象,必须含有x, y, z属性。
102 	     * @param {View} view Hilo.View对象,用于自动转换坐标。
103          * @returns {Object} 二维坐标对象,包括缩放和z属性,例子:{x:x, y:y, z:z, scale}
104 	     */
105 		project : function(vector3D, view){
106 
107 			var rx = this.rotationX * degtorad,
108 				ry = this.rotationY * degtorad,
109 				rz = this.rotationZ * degtorad,
110 
111 				cosX = Math.cos(rx), sinX = Math.sin(rx),
112 				cosY = Math.cos(ry), sinY = Math.sin(ry),
113 				cosZ = Math.cos(rz), sinZ = Math.sin(rz),
114 
115 				// 旋转变换前的 仿射矩阵位移,
116 				dx = vector3D.x - this.x,
117 				dy = vector3D.y - this.y,
118 				dz = vector3D.z - this.z;
119 
120 			// 旋转矩阵变换
121 			var vector = rotateZ(dx, dy, dz, cosZ, sinZ);
122 			vector = rotateY(vector.x, vector.y, vector.z, cosY, sinY);
123 			vector = rotateX(vector.x, vector.y, vector.z, cosX, sinX);
124 
125 			// 最后的仿射矩阵变换
126 			if(this.tx) vector.x -= this.tx;
127 			if(this.ty) vector.y -= this.ty;
128 			if(this.tz) vector.z -= this.tz;
129 
130 			var	perspective = this.fv / (this.fv + vector.z),
131 				_x = vector.x * perspective,
132 				_y = -vector.y * perspective;
133 
134             var result = {
135                 x : _x + this.fx,
136                 y : _y + this.fy,
137                 z : -vector.z,
138                 scale : perspective
139             };
140 
141 			if(view){
142                 view.x = result.x;
143                 view.y = result.y;
144                 view.z = result.z;
145                 view.scaleX = result.scale;
146                 view.scaleY = result.scale;
147 			}
148 
149             return result;
150 		},
151 
152 	    /**
153          * Z深度排序。
154 	     */
155 		sortZ : function(){
156 			this.stage.children.sort(function(view_a, view_b){
157                 return view_a.z > view_b.z;
158             });
159 		},
160 
161 	    /**
162          * Ticker 轮询使用。
163 	     */
164 		tick : function(){
165 			this.sortZ();
166 		}
167 
168 	});
169 
170 	return Camera3d;
171 
172 })();