lib/matrix: add services for projection Matrix and 3D coord manipulation
authorAlexis Laferrière <alexis.laf@xymus.net>
Tue, 15 Sep 2015 14:40:12 +0000 (10:40 -0400)
committerAlexis Laferrière <alexis.laf@xymus.net>
Tue, 15 Sep 2015 17:17:28 +0000 (13:17 -0400)
Signed-off-by: Alexis Laferrière <alexis.laf@xymus.net>

lib/matrix/projection.nit [new file with mode: 0644]

diff --git a/lib/matrix/projection.nit b/lib/matrix/projection.nit
new file mode 100644 (file)
index 0000000..908761a
--- /dev/null
@@ -0,0 +1,153 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services on `Matrix` to transform and project 3D coordinates
+module projection
+
+intrude import matrix
+
+redef class Matrix
+
+       # Create an orthogonal projection matrix
+       #
+       # `left, right, bottom, top, near, far` defines the world clip planes.
+       new orthogonal(left, right, bottom, top, near, far: Float)
+       do
+               var dx = right - left
+               var dy = top - bottom
+               var dz = far - near
+
+               assert dx != 0.0 and dy != 0.0 and dz != 0.0
+
+               var mat = new Matrix.identity(4)
+               mat[0, 0] = 2.0 / dx
+               mat[3, 0] = -(right + left) / dx
+               mat[1, 1] = 2.0 / dy
+               mat[3, 1] = -(top + bottom) / dy
+               mat[2, 2] = 2.0 / dz
+               mat[3, 2] = -(near + far) / dz
+               return mat
+       end
+
+       # Create a perspective transformation matrix
+       #
+       # Using the given vertical `field_of_view_y` in radians, the `aspect_ratio`
+       # and the `near`/`far` world distances.
+       new perspective(field_of_view_y, aspect_ratio, near, far: Float)
+       do
+               var frustum_height = (field_of_view_y/2.0).tan * near
+               var frustum_width = frustum_height * aspect_ratio
+
+               return new Matrix.frustum(-frustum_width, frustum_width,
+                                         -frustum_height, frustum_height,
+                                         near, far)
+       end
+
+       # Create a frustum transformation matrix
+       #
+       # `left, right, bottom, top, near, far` defines the world clip planes.
+       new frustum(left, right, bottom, top, near, far: Float)
+       do
+               var dx = right - left
+               var dy = top - bottom
+               var dz = far - near
+
+               assert near > 0.0
+               assert far > 0.0
+               assert dx > 0.0
+               assert dy > 0.0
+               assert dz > 0.0
+
+               var mat = new Matrix(4, 4)
+
+               mat[0, 0] = 2.0 * near / dx
+               mat[0, 1] = 0.0
+               mat[0, 2] = 0.0
+               mat[0, 3] = 0.0
+
+               mat[1, 0] = 0.0
+               mat[1, 1] = 2.0 * near / dy
+               mat[1, 2] = 0.0
+               mat[1, 3] = 0.0
+
+               mat[2, 0] = (right + left) / dx
+               mat[2, 1] = (top + bottom) / dy
+               mat[2, 2] = -(near + far) / dz
+               mat[2, 3] = -1.0
+
+               mat[3, 0] = 0.0
+               mat[3, 1] = 0.0
+               mat[3, 2] = -2.0 * near * far / dz
+               mat[3, 3] = 0.0
+
+               return mat
+       end
+
+       # Apply a translation by `x, y, z` to this matrix
+       fun translate(x, y, z: Float)
+       do
+               for i in [0..3] do
+                       self[3, i] = self[3,i] + self[0, i] * x + self[1, i] * y + self[2, i] * z
+               end
+       end
+
+       # Apply scaling on `x, y, z` to this matrix
+       fun scale(x, y, z: Float)
+       do
+               for i in [0..3] do
+                       self[0, i] = self[0, i] * x
+                       self[1, i] = self[1, i] * y
+                       self[2, i] = self[2, i] * z
+               end
+       end
+
+       # Create a rotation matrix by `angle` around the vector defined by `x, y, z`
+       new rotation(angle, x, y, z: Float)
+       do
+               var mat = new Matrix.identity(4)
+
+               var mag = (x*x + y*y + z*z).sqrt
+               var sin = angle.sin
+               var cos = angle.cos
+
+               if mag > 0.0 then
+                       x = x / mag
+                       y = y / mag
+                       z = z / mag
+
+                       var inv_cos = 1.0 - cos
+
+                       mat[0, 0] = inv_cos*x*x + cos
+                       mat[0, 1] = inv_cos*x*y - z*sin
+                       mat[0, 2] = inv_cos*z*x + y*sin
+
+                       mat[1, 0] = inv_cos*x*y + z*sin
+                       mat[1, 1] = inv_cos*y*y + cos
+                       mat[1, 2] = inv_cos*y*z - x*sin
+
+                       mat[2, 0] = inv_cos*z*x - y*sin
+                       mat[2, 1] = inv_cos*y*z + x*sin
+                       mat[2, 2] = inv_cos*z*z + cos
+               end
+               return mat
+       end
+
+       # Apply a rotation of `angle` radians around the vector `x, y, z`
+       fun rotate(angle, x, y, z: Float)
+       do
+               var rotation = new Matrix.rotation(angle, x, y, z)
+               var rotated = self * rotation
+               self.items = rotated.items
+       end
+end