Source code for vulk.math.matrix

'''Matrix module

This module contains all class relative to Matrices.
In graphic computing, matrices is a key to manipulate object in
space. I recommand you to read this article to understand what is
the main goal of matrices:
http://www.codinglabs.net/article_world_view_projection_matrix.aspx
'''
import numpy as np

from vulk.math.vector import Vector3


[docs]class Matrix(): '''Base class for Matrix''' def __init__(self, values): ''' *Parameters:* - `values`: `list` of float **Note: In Vulk, Matrix is in column-major order** ''' self._values = np.array(values, dtype=np.float32) def __len__(self): '''Return the matrix size''' return len(self._values) @property def values(self): return self._values
[docs] def set(self, matrix): '''Set this matrix to `matrix` *Parameters:* - `matrix`: `Matrix` to set ''' return self.set2(matrix.values)
[docs] def set2(self, values, offset=0): '''Set this matrix to `values` from `offset` *Parameters:* - `values`: `list` of `float` - `offset`: Start of the update ''' self._values[offset:] = values return self
[docs]class Matrix3(Matrix): '''Matrix3 class''' def __init__(self, values=None): ''' *Parameters:* - `values`: `list` of 9 `float` ''' if not values: super().__init__([1, 0, 0, 0, 1, 0, 0, 0, 1]) elif np.shape(values) == (9,): super().__init__(values) else: raise ValueError("Matrix3 needs 9 components")
[docs]class Matrix4(Matrix): '''Matrix4 class''' def __init__(self, values=None): ''' *Parameters:* - `values`: `list` of 16 `float` ''' if not values: super().__init__([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) elif np.shape(values) == (16,): super().__init__(values) else: raise ValueError("Matrix4 needs 16 components") # Tmp property used during computation self.tmp = np.zeros(16, dtype=np.float32) def __str__(self): '''Return a beautiful readable matrix''' v = self._values return ('{} {} {} {}\n' '{} {} {} {}\n' '{} {} {} {}\n' '{} {} {} {}\n'.format( v[0], v[4], v[8], v[12], v[1], v[5], v[9], v[13], v[2], v[6], v[10], v[14], v[3], v[7], v[11], v[15]))
[docs] def mul(self, matrix): '''Multiply this matrix by `matrix` The order of operation is: `this @ matrix`. *Parameters:* - `matrix`: `Matrix4` ''' # Make a matrix4 shape to matmul function view1 = np.reshape(self._values, (4, 4)) view2 = np.reshape(matrix.values, (4, 4)) self.tmp.shape = (4, 4) # np.matmul(view2, view1, out=out) np.matmul(view2, view1, out=self.tmp) self.tmp.shape = (16,) self._values[:] = self.tmp return self
[docs] def inv(self): '''Inverse this matrix''' view = self._values.view() view.shape = (4, 4) tmp_mat = np.matrix(view, copy=False) self._values[:] = tmp_mat.I.flatten()
[docs] def to_identity(self): '''Set this matrix to identity matrix''' self._values[:] = 0. self._values[0] = 1. self._values[5] = 1. self._values[10] = 1. self._values[15] = 1. return self
[docs]class ViewMatrix(Matrix4): '''This class represents a view Matrix. View Matrix convert vertex from World space to View space. '''
[docs] def to_look_at_direction(self, direction, up): ''' Set this matrix to a *look at* matrix with a `direction` and a `up` vector. *Parameters:* - `direction`: Direction `Vector3` - `up`: Up `Vector3` ''' vec_z = Vector3(direction).nor() vec_x = Vector3(direction).nor() vec_x.crs(up.nor()).nor() vec_y = Vector3(vec_x).crs(vec_z).nor() self.to_identity() self.values[0] = vec_x.x self.values[4] = vec_x.y self.values[8] = vec_x.z self.values[1] = vec_y.x self.values[5] = vec_y.y self.values[9] = vec_y.z self.values[2] = -vec_z.x self.values[6] = -vec_z.y self.values[10] = -vec_z.z return self
[docs] def to_look_at(self, position, target, up): ''' Set this matrix to a *look at* matrix with a `position`, `target` and `up` vector. *Parameters:* - `position`: Position `Vector3` - `direction`: Direction `Vector3` - `up`: Up `Vector3` ''' # http://www.cs.virginia.edu/~gfx/Courses/1999/intro.fall99.html/lookat.html vec = Vector3(target).sub(position) self.to_look_at_direction(vec, up) translation = TransformationMatrix().to_translation( -position.x, -position.y, -position.z) self.mul(translation) return self
[docs]class ProjectionMatrix(Matrix4): '''This class represents a projection Matrix. Projection is the last step to compute Vertex position. It must be applied after the view matrix. '''
[docs] def to_orthographic_2d(self, x, y, width, height, near=0, far=1): '''Set this matrix to an orthographic matrix used in 2D rendering *Parameters:* - `x`: X coordinate of the origin - `y`: Y coordinate of the origin - `width`: Width - `hight`: Height - `near`: Near plane - `Far`: Far plane ''' return self.to_orthographic(x, x + width, y + height, y, near, far)
[docs] def to_orthographic(self, left, right, bottom, top, near, far): ''' Set this matrix to an orthographic projection matrix *Parameters:* - `left`: Left clipping plane - `right`: Right clipping plane - `bottom`: Bottom clipping plane - `top`: Top clipping plane - `near`: Near clipping plane - `far`: Far clipping plane **Note: Vulkan coordinate system is not the same as OpenGL, thus the matrix is adapted to Vulkan** ''' # https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/ # OpenGL Orthographic matrix # x_orth = 2 / (right - left) # y_orth = 2 / (top - bottom) # z_orth = -2 / (far - near) # tx = -(right + left) / (right - left) # ty = -(top + bottom) / (top - bottom) # tz = -(far + near) / (far - near) # Vulkan Orthographic matrix x_orth = 2 / (right - left) y_orth = -2 / (top - bottom) z_orth = -1 / (far - near) tx = -(right + left) / (right - left) ty = (top + bottom) / (top - bottom) tz = -0.5 * (far + near) / (far - near) + 0.5 self.to_identity() self.values[0] = x_orth self.values[5] = y_orth self.values[10] = z_orth self.values[12] = tx self.values[13] = ty self.values[14] = tz return self
[docs]class TransformationMatrix(Matrix4): '''This class represents a transformation Matrix. It's a Matrix4 with added capabilities used to transform model space to world space. '''
[docs] def to_translation(self, x, y, z): ''' Set this matrix to a translation matrix. First set it to identity and then set the 4th column to the translation vector. *Parameters:* - `x`: x-component of translation vector - `y`: y-component of translation vector - `z`: z-component of translation vector ''' self.to_identity() self._values[12] = x self._values[13] = y self._values[14] = z return self