tests: add some runtime error in nitin.input
[nit.git] / lib / gamnit / cameras.nit
index 2dfc706..401e1ce 100644 (file)
@@ -98,6 +98,17 @@ class EulerCamera
                position.y += dy
        end
 
+       # Aim the camera at `x, y, z`
+       fun look_at(x, y, z: Float)
+       do
+               var dx = position.x
+               var dy = position.y
+               var dz = position.z
+
+               yaw = atan2(dx, dz)
+               pitch = atan2(-dy, dz)
+       end
+
        # Rotation matrix produced by the current rotation of the camera
        protected fun rotation_matrix: Matrix
        do
@@ -116,13 +127,13 @@ class EulerCamera
                var view = new Matrix.identity(4)
 
                # Translate the world away from the camera
-               view.translate(-position.x/2.0, -position.y/2.0, -position.z/2.0)
+               view.translate(-position.x, -position.y, -position.z)
 
                # Rotate the camera, first by looking left or right, then up or down
                view = view * rotation_matrix
 
                # Use a projection matrix with a depth
-               var projection = new Matrix.perspective(pi*field_of_view_y/2.0,
+               var projection = new Matrix.perspective(field_of_view_y,
                        display.aspect_ratio, near, far)
 
                return view * projection
@@ -151,6 +162,35 @@ class EulerCamera
                yaw = 0.0
                roll = 0.0
        end
+
+       # Convert the position `x, y` on screen, to world coordinates on the plane at `target_z`
+       #
+       # `target_z` defaults to `0.0` and specifies the Z coordinates of the plane
+       # on which to project the screen position `x, y`.
+       #
+       # This method assumes that the camera is looking along the Z axis towards higher values.
+       # Using it in a different orientation can be useful, but won't result in valid
+       # world coordinates.
+       fun camera_to_world(x, y: Numeric, target_z: nullable Float): Point[Float]
+       do
+               # TODO, this method could be tweaked to support projecting the 2D point,
+               # on the near plane (x,y) onto a given distance no matter to orientation
+               # of the camera.
+
+               target_z = target_z or else 0.0
+
+               # Convert from pixel units / window resolution to
+               # units on the near clipping wall to
+               # units on the target wall at Z = 0
+               var near_height = (field_of_view_y/2.0).tan * near
+               var cross_screen_to_near = near_height / (display.height.to_f/2.0)
+               var cross_near_to_target = (position.z - target_z) / near
+               var mod = cross_screen_to_near * cross_near_to_target
+
+               var wx = position.x + (x.to_f-display.width.to_f/2.0) * mod
+               var wy = position.y - (y.to_f-display.height.to_f/2.0) * mod
+               return new Point[Float](wx, wy)
+       end
 end
 
 # Orthogonal camera to draw UI objects with services to work with screens of different sizes
@@ -194,20 +234,35 @@ class UICamera
 
                var wx = x.to_f * width / display.width.to_f - position.x
                var wy = y.to_f * height / display.height.to_f - position.y
-               return new Point[Float](wx, wy)
+               return new Point[Float](wx, -wy)
        end
 
-       # Anchor in the top left corner of the screen, at z = 0
+       # Center of the screen, from the point of view of the camera, at z = 0
+       fun center: Point3d[Float] do return new Point3d[Float](position.x + width / 2.0, position.y - height / 2.0, 0.0)
+
+       # Center of the top of the screen, at z = 0
+       fun top: Point3d[Float] do return new Point3d[Float](position.x + width / 2.0, position.y, 0.0)
+
+       # Center of the bottom of the screen, at z = 0
+       fun bottom: Point3d[Float] do return new Point3d[Float](position.x + width / 2.0, position.y - height, 0.0)
+
+       # Center of the left border of the screen, at z = 0
+       fun left: Point3d[Float] do return new Point3d[Float](position.x, position.y - height / 2.0, 0.0)
+
+       # Center of the right border of the screen, at z = 0
+       fun right: Point3d[Float] do return new Point3d[Float](position.x + width, position.y - height / 2.0, 0.0)
+
+       # Top left corner of the screen, at z = 0
        fun top_left: Point3d[Float] do return new Point3d[Float](position.x, position.y, 0.0)
 
-       # Anchor in the top right corner of the screen, at z = 0
+       # Top right corner of the screen, at z = 0
        fun top_right: Point3d[Float] do return new Point3d[Float](position.x + width, position.y, 0.0)
 
-       # Anchor in the bottom left corner of the screen, at z = 0
-       fun bottom_left: Point3d[Float] do return new Point3d[Float](position.x, position.y + height, 0.0)
+       # Bottom left corner of the screen, at z = 0
+       fun bottom_left: Point3d[Float] do return new Point3d[Float](position.x, position.y - height, 0.0)
 
-       # Anchor in the bottom right corner of the screen, at z = 0
-       fun bottom_right: Point3d[Float] do return new Point3d[Float](position.x + width, position.y + height, 0.0)
+       # Bottom right corner of the screen, at z = 0
+       fun bottom_right: Point3d[Float] do return new Point3d[Float](position.x + width, position.y - height, 0.0)
 
        # TODO cache the anchors and the matrix
 
@@ -216,7 +271,7 @@ class UICamera
                var view = new Matrix.identity(4)
 
                # Translate the world away from the camera
-               view.translate(-position.x/2.0, -position.y/2.0, -position.z/2.0)
+               view.translate(-position.x, -position.y, -position.z)
 
                # Use a projection matrix with a depth
                var projection = new Matrix.orthogonal(0.0, width, -height, 0.0, near, far)