gamnit: 3 Euler rotation angles for actors
[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 new gamnit_euler_rotation(pitch, yaw, roll: Float)
163 do
164 var c1 = pitch.cos
165 var s1 = pitch.sin
166 var c2 = yaw.cos
167 var s2 = yaw.sin
168 var c3 = roll.cos
169 var s3 = roll.sin
170 return new Matrix.from(
171 [[ c2*c3, -c2*s3, -s2, 0.0],
172 [ c1*s3+c3*s1*s2, c1*c3-s1*s2*s3, c2*s1, 0.0],
173 [-s1*s3+c1*c3*s2, -c3*s1-c1*s2*s3, c1*c2, 0.0],
174 [ 0.0, 0.0, 0.0, 1.0]])
175 end
176 end