908761a5bd65f44821e627f8f92ecfe262df45b9
[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 end