Camera.hx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. package h3d;
  2. // use left-handed coordinate system, more suitable for 2D games X=0,Y=0 at screen top-left and Z towards user
  3. class Camera {
  4. public var zoom : Float;
  5. /**
  6. The screenRatio represents the W/H screen ratio.
  7. **/
  8. public var screenRatio : Float;
  9. /**
  10. The horizontal FieldOfView, in degrees.
  11. **/
  12. public var fovX : Float;
  13. public var zNear : Float;
  14. public var zFar : Float;
  15. /**
  16. Set orthographic bounds.
  17. **/
  18. public var orthoBounds : h3d.col.Bounds;
  19. public var rightHanded : Bool;
  20. public var mproj : Matrix;
  21. public var mcam : Matrix;
  22. public var m : Matrix;
  23. public var pos : Vector;
  24. public var up : Vector;
  25. public var target : Vector;
  26. public var viewX : Float = 0.;
  27. public var viewY : Float = 0.;
  28. var minv : Matrix;
  29. var needInv : Bool;
  30. public function new( fovX = 60., zoom = 1., screenRatio = 1.333333, zNear = 0.02, zFar = 4000., rightHanded = false ) {
  31. this.fovX = fovX;
  32. this.zoom = zoom;
  33. this.screenRatio = screenRatio;
  34. this.zNear = zNear;
  35. this.zFar = zFar;
  36. this.rightHanded = rightHanded;
  37. pos = new Vector(2, 3, 4);
  38. up = new Vector(0, 0, 1);
  39. target = new Vector(0, 0, 0);
  40. m = new Matrix();
  41. mcam = new Matrix();
  42. mproj = new Matrix();
  43. update();
  44. }
  45. /**
  46. Update the fovX value based on the requested fovY value (in degrees) and current screenRatio.
  47. **/
  48. public function setFovY( value : Float ) {
  49. fovX = Math.atan( Math.tan(value * Math.PI / 180) / screenRatio ) * 180 / Math.PI;
  50. }
  51. public function clone() {
  52. var c = new Camera(fovX, zoom, screenRatio, zNear, zFar, rightHanded);
  53. c.pos = pos.clone();
  54. c.up = up.clone();
  55. c.target = target.clone();
  56. c.update();
  57. return c;
  58. }
  59. /**
  60. Returns the inverse of the camera matrix view and projection. Cache the result until the next update().
  61. **/
  62. public function getInverseViewProj() {
  63. if( minv == null ) minv = new h3d.Matrix();
  64. if( needInv ) {
  65. minv.inverse(m);
  66. needInv = false;
  67. }
  68. return minv;
  69. }
  70. /**
  71. Transforms a 2D screen position into the 3D one according to the current camera.
  72. The screenX and screenY values must be in the [-1,1] range.
  73. The camZ value represents the normalized z in the frustum in the [0,1] range.
  74. [unproject] can be used to get the ray from the camera position to a given screen position by using two different camZ values.
  75. For instance the 3D ray between unproject(0,0,0) and unproject(0,0,1) is the center axis of the 3D frustum.
  76. **/
  77. public function unproject( screenX : Float, screenY : Float, camZ ) {
  78. var p = new h3d.Vector(screenX, screenY, camZ);
  79. p.project(getInverseViewProj());
  80. return p;
  81. }
  82. public function update() {
  83. makeCameraMatrix(mcam);
  84. makeFrustumMatrix(mproj);
  85. m.multiply(mcam, mproj);
  86. needInv = true;
  87. }
  88. public function lostUp() {
  89. var p2 = pos.clone();
  90. p2.normalize();
  91. return Math.abs(p2.dot3(up)) > 0.999;
  92. }
  93. public function movePosAxis( dx : Float, dy : Float, dz = 0. ) {
  94. var p = new Vector(dx, dy, dz);
  95. p.project(mcam);
  96. pos.x += p.x;
  97. pos.y += p.y;
  98. pos.z += p.z;
  99. }
  100. public function moveTargetAxis( dx : Float, dy : Float, dz = 0. ) {
  101. var p = new Vector(dx, dy, dz);
  102. p.project(mcam);
  103. target.x += p.x;
  104. target.y += p.y;
  105. target.z += p.z;
  106. }
  107. function makeCameraMatrix( m : Matrix ) {
  108. // in leftHanded the z axis is positive else it's negative
  109. // this way we make sure that our [ax,ay,-az] matrix follow the same handness as our world
  110. // We build a transposed version of Matrix.lookAt
  111. var az = rightHanded ? pos.sub(target) : target.sub(pos);
  112. az.normalize();
  113. var ax = up.cross(az);
  114. ax.normalize();
  115. if( ax.length() == 0 ) {
  116. ax.x = az.y;
  117. ax.y = az.z;
  118. ax.z = az.x;
  119. }
  120. var ay = az.cross(ax);
  121. m._11 = ax.x;
  122. m._12 = ay.x;
  123. m._13 = az.x;
  124. m._14 = 0;
  125. m._21 = ax.y;
  126. m._22 = ay.y;
  127. m._23 = az.y;
  128. m._24 = 0;
  129. m._31 = ax.z;
  130. m._32 = ay.z;
  131. m._33 = az.z;
  132. m._34 = 0;
  133. m._41 = -ax.dot3(pos);
  134. m._42 = -ay.dot3(pos);
  135. m._43 = -az.dot3(pos);
  136. m._44 = 1;
  137. }
  138. function makeFrustumMatrix( m : Matrix ) {
  139. m.zero();
  140. // this will take into account the aspect ratio and normalize the z value into [0,1] once it's been divided by w
  141. // Matrixes have to solve the following formulaes :
  142. //
  143. // transform P by Mproj and divide everything by
  144. // [x,y,-zNear,1] => [sx/zNear, sy/zNear, 0, 1]
  145. // [x,y,-zFar,1] => [sx/zFar, sy/zFar, 1, 1]
  146. // we apply the screen ratio to the height in order to have the fov being a horizontal FOV. This way we don't have to change the FOV when the screen is enlarged
  147. var bounds = orthoBounds;
  148. if( bounds != null ) {
  149. var w = 1 / (bounds.xMax - bounds.xMin);
  150. var h = 1 / (bounds.yMax - bounds.yMin);
  151. var d = 1 / (bounds.zMax - bounds.zMin);
  152. m._11 = 2 * w;
  153. m._22 = 2 * h;
  154. m._33 = d;
  155. m._41 = -(bounds.xMin + bounds.xMax) * w;
  156. m._42 = -(bounds.yMin + bounds.yMax) * h;
  157. m._43 = -bounds.zMin * d;
  158. m._44 = 1;
  159. } else {
  160. var scale = zoom / Math.tan(fovX * Math.PI / 360.0);
  161. m._11 = scale;
  162. m._22 = scale * screenRatio;
  163. m._33 = zFar / (zFar - zNear);
  164. m._34 = 1;
  165. m._43 = -(zNear * zFar) / (zFar - zNear);
  166. }
  167. m._11 += viewX * m._14;
  168. m._21 += viewX * m._24;
  169. m._31 += viewX * m._34;
  170. m._41 += viewX * m._44;
  171. m._12 += viewY * m._14;
  172. m._22 += viewY * m._24;
  173. m._32 += viewY * m._34;
  174. m._42 += viewY * m._44;
  175. // our z is negative in that case
  176. if( rightHanded ) {
  177. m._33 *= -1;
  178. m._34 *= -1;
  179. }
  180. }
  181. }