1 /**
  2  * Hilo
  3  * Copyright 2015 alibaba.com
  4  * Licensed under the MIT License
  5  */
  6 
  7 /**
  8  * @language=en
  9  * @class Camera3d is a pseudo-3d camera.
 10  * @module hilo/game/Camera3d
 11  * @requires hilo/core/Hilo
 12  * @requires hilo/core/Class
 13  * @property {Number} fv The distance of the fov(The distance between eyes and the Z plane,it determines the scale ratio of the 3d object).
 14  * @property {Number} fx The x position of the screen viewpoint(The distance between the screen viewpoint and the screen left top corner on the x axis).
 15  * @property {Number} fy The y position of the screen viewpoint(The distance between the screen viewpoint and the screen left top corner on the y axis).
 16  * @property {Object} stage The 3d object's container, it can be stage or container.It is required if you need to sort the 3d object by z axis.
 17  * @property {Number} x The x position.
 18  * @property {Number} y The y position.
 19  * @property {Number} z The z position.
 20  * @property {Number} rotationX The x rotation.
 21  * @property {Number} rotationY The y rotation.
 22  * @property {Number} rotationZ The z rotation.
 23  */
 24 var Camera3d = (function(){
 25 
 26 	var degtorad = Math.PI / 180;
 27 
 28 	//Rotate the axis.
 29 	function rotateX(x, y, z, ca, sa) {//rotate x
 30 		return {
 31 			x: x,
 32 			y: y * ca - z * sa,
 33 			z: y * sa + z * ca
 34 		};
 35 	}
 36 	function rotateY(x, y, z, ca, sa) {//rotate y
 37 		return {
 38 			x: x * ca - z * sa,
 39 			y: y,
 40 			z: x * sa + z * ca
 41 		};
 42 	}
 43 	function rotateZ(x, y, z, ca, sa) {//rotate z
 44 		return {
 45 			x: x * ca - y * sa,
 46 			y: x * sa + y * ca,
 47 			z: z
 48 		};
 49 	}
 50 
 51 	var Camera3d = Class.create(/** @lends Camera3d.prototype */{
 52 
 53 		constructor: function(properties){
 54 			properties.x = properties.x || 0;
 55 			properties.y = properties.y || 0;
 56 			properties.z = properties.z || 0;
 57 			properties.rotationX = properties.rotationX || 0;
 58 			properties.rotationY = properties.rotationY || 0;
 59 			properties.rotationZ = properties.rotationZ || 0;
 60 
 61         	Hilo.copy(this, properties);
 62 		},
 63 
 64 	    /**
 65          * @language=en
 66          * Translate the camera,used for Zoomin/out feature.
 67 	     * @param {Number} x The x position.
 68 	     * @param {Number} y The y position.
 69 	     * @param {Number} z The z position.
 70 	     */
 71 		translate : function(x,y,z){
 72 			this.tx = x;
 73 			this.ty = y;
 74 			this.tz = z;
 75 		},
 76 
 77 	    /**
 78          * @language=en
 79          * Rotate by the x axis.
 80 	     * @param {Number} angle The rotate degree.
 81 	     */
 82 		rotateX : function(angle){
 83 			this.rotationX = angle;
 84 		},
 85 
 86 	    /**
 87          * @language=en
 88          * Rotate by the y axis.
 89 	     * @param {Number} angle The rotate degree.
 90 	     */
 91 		rotateY : function(angle){
 92 			this.rotationY = angle;
 93 		},
 94 
 95 	    /**
 96          * @language=en
 97          * Rotate by the z axis.
 98 	     * @param {Number} angle The rotate degree.
 99 	     */
100 		rotateZ : function(angle){
101 			this.rotationZ = angle;
102 		},
103 
104 	    /**
105          * @language=en
106          * Project the 3d point to 2d point.
107 	     * @param {object} vector3D The 3d position, it must have x, y and z properties.
108 	     * @param {View} view The view related to the 3d position.It'll be auto translated by the 3d position.
109          * @returns {Object} The 2d object include z and scale properties, e.g.:{x:x, y:y, z:z, scale}
110 	     */
111 		project : function(vector3D, view){
112 
113 			var rx = this.rotationX * degtorad,
114 				ry = this.rotationY * degtorad,
115 				rz = this.rotationZ * degtorad,
116 
117 				cosX = Math.cos(rx), sinX = Math.sin(rx),
118 				cosY = Math.cos(ry), sinY = Math.sin(ry),
119 				cosZ = Math.cos(rz), sinZ = Math.sin(rz),
120 
121 				// 旋转变换前的 仿射矩阵位移,
122 				dx = vector3D.x - this.x,
123 				dy = vector3D.y - this.y,
124 				dz = vector3D.z - this.z;
125 
126 			// 旋转矩阵变换
127 			var vector = rotateZ(dx, dy, dz, cosZ, sinZ);
128 			vector = rotateY(vector.x, vector.y, vector.z, cosY, sinY);
129 			vector = rotateX(vector.x, vector.y, vector.z, cosX, sinX);
130 
131 			// 最后的仿射矩阵变换
132 			if(this.tx) vector.x -= this.tx;
133 			if(this.ty) vector.y -= this.ty;
134 			if(this.tz) vector.z -= this.tz;
135 
136 			var	perspective = this.fv / (this.fv + vector.z),
137 				_x = vector.x * perspective,
138 				_y = -vector.y * perspective;
139 
140             var result = {
141                 x : _x + this.fx,
142                 y : _y + this.fy,
143                 z : -vector.z,
144                 scale : perspective
145             };
146 
147 			if(view){
148                 view.x = result.x;
149                 view.y = result.y;
150                 view.z = result.z;
151                 view.scaleX = result.scale;
152                 view.scaleY = result.scale;
153 			}
154 
155             return result;
156 		},
157 
158 	    /**
159          * @language=en
160          * Sort by z axis.
161 	     */
162 		sortZ : function(){
163 			this.stage.children.sort(function(view_a, view_b){
164                 return view_a.z > view_b.z;
165             });
166 		},
167 
168 	    /**
169          * @language=en
170          * Used for the ticker.
171 	     */
172 		tick : function(){
173 			this.sortZ();
174 		}
175 
176 	});
177 
178 	return Camera3d;
179 
180 })();