lib/gamnit depth: add stereoscopic view module
[nit.git] / lib / gamnit / depth / stereoscopic_view.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Refine `EulerCamera` and `App::frame_core_draw` to get a stereoscopic view
16 module stereoscopic_view
17
18 import depth
19 intrude import cameras
20
21 redef class EulerCamera
22 redef var mvp_matrix = new Matrix.identity(4)
23
24 # Half of the distance between the eyes
25 var eye_separation: Float = 0.03125
26
27 # MVP matrix for the left eye
28 fun mvp_matrix_left: Matrix do return mvp_matrix_eye(eye_separation)
29
30 # MVP matrix for the right eye
31 fun mvp_matrix_right: Matrix do return mvp_matrix_eye(-eye_separation)
32
33 # Get an MVP matrix for an eye at `diff` world unit from the center
34 private fun mvp_matrix_eye(diff: Float): Matrix
35 do
36 var view = new Matrix.identity(4)
37
38 # Translate the world away from the camera
39 view.translate(-position.x/2.0, -position.y/2.0, -position.z/2.0)
40
41 # Rotate the camera, first by looking left or right, then up or down
42 view = view * rotation_matrix
43
44 # Apply eye transformation
45 var translation = new Matrix.identity(4)
46 translation.translate(diff, 0.0, 0.0)
47 view = view * translation
48
49 # Use a projection matrix with a depth
50 var projection = new Matrix.perspective(pi*field_of_view_y/2.0,
51 display.aspect_ratio, near, far)
52
53 return view * projection
54 end
55 end
56
57 redef class GamnitDisplay
58
59 # With stereoscopic view, the aspect ratio (in each eye) is half of the screen
60 redef fun aspect_ratio do return super / 2.0
61 end
62
63 redef class App
64 redef fun frame_core_draw(display) do frame_core_stereoscopic display
65
66 # Split the screen in two, and call `frame_core_depth` for each eyes
67 protected fun frame_core_stereoscopic(display: GamnitDisplay)
68 do
69 var half_width = display.width / 2
70
71 # Left eye
72 glViewport(0, 0, half_width, display.height)
73 world_camera.mvp_matrix = world_camera.mvp_matrix_left
74 frame_core_depth display
75
76 # Right eye
77 glViewport(half_width, 0, half_width, display.height)
78 world_camera.mvp_matrix = world_camera.mvp_matrix_right
79 frame_core_depth display
80
81 # We reset the viewport for selection
82 glViewport(0, 0, display.width, display.height)
83
84 # Check for errors
85 var gl_error = glGetError
86 assert gl_error == gl_NO_ERROR else print gl_error
87 end
88 end