Merge: annotations: introduce `example` annotation
[nit.git] / lib / matrix / projection.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 # Services on `Matrix` to transform and project 3D coordinates
16 module projection
17
18 intrude import matrix
19
20 redef class Matrix
21
22 # Create an orthogonal projection matrix
23 #
24 # `left, right, bottom, top, near, far` defines the world clip planes.
25 new orthogonal(left, right, bottom, top, near, far: Float)
26 do
27 var dx = right - left
28 var dy = top - bottom
29 var dz = far - near
30
31 assert dx != 0.0 and dy != 0.0 and dz != 0.0
32
33 var mat = new Matrix.identity(4)
34 mat[0, 0] = 2.0 / dx
35 mat[3, 0] = -(right + left) / dx
36 mat[1, 1] = 2.0 / dy
37 mat[3, 1] = -(top + bottom) / dy
38 mat[2, 2] = 2.0 / dz
39 mat[3, 2] = -(near + far) / dz
40 return mat
41 end
42
43 # Create a perspective transformation matrix
44 #
45 # Using the given vertical `field_of_view_y` in radians, the `aspect_ratio`
46 # and the `near`/`far` world distances.
47 new perspective(field_of_view_y, aspect_ratio, near, far: Float)
48 do
49 var frustum_height = (field_of_view_y/2.0).tan * near
50 var frustum_width = frustum_height * aspect_ratio
51
52 return new Matrix.frustum(-frustum_width, frustum_width,
53 -frustum_height, frustum_height,
54 near, far)
55 end
56
57 # Create a frustum transformation matrix
58 #
59 # `left, right, bottom, top, near, far` defines the world clip planes.
60 new frustum(left, right, bottom, top, near, far: Float)
61 do
62 var dx = right - left
63 var dy = top - bottom
64 var dz = far - near
65
66 assert near > 0.0
67 assert far > 0.0
68 assert dx > 0.0
69 assert dy > 0.0
70 assert dz > 0.0
71
72 var mat = new Matrix(4, 4)
73
74 mat[0, 0] = 2.0 * near / dx
75 mat[0, 1] = 0.0
76 mat[0, 2] = 0.0
77 mat[0, 3] = 0.0
78
79 mat[1, 0] = 0.0
80 mat[1, 1] = 2.0 * near / dy
81 mat[1, 2] = 0.0
82 mat[1, 3] = 0.0
83
84 mat[2, 0] = (right + left) / dx
85 mat[2, 1] = (top + bottom) / dy
86 mat[2, 2] = -(near + far) / dz
87 mat[2, 3] = -1.0
88
89 mat[3, 0] = 0.0
90 mat[3, 1] = 0.0
91 mat[3, 2] = -2.0 * near * far / dz
92 mat[3, 3] = 0.0
93
94 return mat
95 end
96
97 # Apply a translation by `x, y, z` to this matrix
98 fun translate(x, y, z: Float)
99 do
100 for i in [0..3] do
101 self[3, i] = self[3,i] + self[0, i] * x + self[1, i] * y + self[2, i] * z
102 end
103 end
104
105 # Apply scaling on `x, y, z` to this matrix
106 fun scale(x, y, z: Float)
107 do
108 for i in [0..3] do
109 self[0, i] = self[0, i] * x
110 self[1, i] = self[1, i] * y
111 self[2, i] = self[2, i] * z
112 end
113 end
114
115 # Create a rotation matrix by `angle` around the vector defined by `x, y, z`
116 new rotation(angle, x, y, z: Float)
117 do
118 var mat = new Matrix.identity(4)
119
120 var mag = (x*x + y*y + z*z).sqrt
121 var sin = angle.sin
122 var cos = angle.cos
123
124 if mag > 0.0 then
125 x = x / mag
126 y = y / mag
127 z = z / mag
128
129 var inv_cos = 1.0 - cos
130
131 mat[0, 0] = inv_cos*x*x + cos
132 mat[0, 1] = inv_cos*x*y - z*sin
133 mat[0, 2] = inv_cos*z*x + y*sin
134
135 mat[1, 0] = inv_cos*x*y + z*sin
136 mat[1, 1] = inv_cos*y*y + cos
137 mat[1, 2] = inv_cos*y*z - x*sin
138
139 mat[2, 0] = inv_cos*z*x - y*sin
140 mat[2, 1] = inv_cos*y*z + x*sin
141 mat[2, 2] = inv_cos*z*z + cos
142 end
143 return mat
144 end
145
146 # Apply a rotation of `angle` radians around the vector `x, y, z`
147 fun rotate(angle, x, y, z: Float)
148 do
149 var rotation = new Matrix.rotation(angle, x, y, z)
150 var rotated = self * rotation
151 self.items = rotated.items
152 end
153
154 # Rotation matrix from Euler angles `pitch`, `yaw` and `roll` in radians
155 #
156 # Apply a composition of intrinsic rotations around the axes x-y'-z''.
157 # Or `pitch` around the X axis, `yaw` around Y and `roll` around Z,
158 # applied successively. All rotations follow the right hand rule.
159 #
160 # This service aims to respect the world axes and logic of `gamnit`,
161 # it may not correspond to all needs.
162 #
163 # The returned `Matrix` may be cached, it must not be modified.
164 new gamnit_euler_rotation(pitch, yaw, roll: Float)
165 do
166 if pitch == 0.0 and yaw == 0.0 and roll == 0.0 then
167 return once new Matrix.identity(4)
168 end
169
170 if rotation_pitch == pitch and rotation_yaw == yaw and rotation_roll == roll then
171 var rot = rotation_matrix_cache
172 if rot != null then return rot
173 end
174
175 var c1 = pitch.cos
176 var s1 = pitch.sin
177 var c2 = yaw.cos
178 var s2 = yaw.sin
179 var c3 = roll.cos
180 var s3 = roll.sin
181
182 var rot = new Matrix(4, 4)
183 rot.items.mat4_set(
184 c2*c3, -c2*s3, -s2, 0.0,
185 c1*s3+c3*s1*s2, c1*c3-s1*s2*s3, c2*s1, 0.0,
186 -s1*s3+c1*c3*s2, -c3*s1-c1*s2*s3, c1*c2, 0.0,
187 0.0, 0.0, 0.0, 1.0)
188
189 rotation_matrix_cache = rot
190 rotation_pitch = pitch
191 rotation_yaw = yaw
192 rotation_roll = roll
193 return rot
194 end
195 end
196
197 redef class NativeDoubleArray
198 fun mat4_set(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15: Float) `{
199 self[ 0] = f0;
200 self[ 1] = f1;
201 self[ 2] = f2;
202 self[ 3] = f3;
203
204 self[ 4] = f4;
205 self[ 5] = f5;
206 self[ 6] = f6;
207 self[ 7] = f7;
208
209 self[ 8] = f8;
210 self[ 9] = f9;
211 self[10] = f10;
212 self[11] = f11;
213
214 self[12] = f12;
215 self[13] = f13;
216 self[14] = f14;
217 self[15] = f15;
218 `}
219 end
220
221 redef class Sys
222
223 private var rotation_matrix_cache: nullable Matrix = null
224 private var rotation_pitch = 0.0
225 private var rotation_yaw = 0.0
226 private var rotation_roll = 0.0
227 end