import math from vectors import Vector3d, Vector4d class Matrix3x3: def __init__(self, row1, row2, row3): self.rows = [row1, row2, row3] @property def row1(self): return self.rows[0] @row1.setter def row1(self, value): self.rows[0] = value @property def row2(self): return self.rows[1] @row2.setter def row2(self, value): self.rows[1] = value @property def row3(self): return self.rows[2] @row3.setter def row3(self, value): self.rows[2] = value def __getitem__(self, key): return self.rows[key] def __setitem__(self, key, value): self.rows[key] = value class Matrix4x4(Matrix3x3): def __init__(self, row1, row2, row3, row4): if not isinstance(row1, Vector4d): raise ValueError("Row must be a Vector4d object.", row1) if not isinstance(row2, Vector4d): raise ValueError("Row must be a Vector4d object.", row2) if not isinstance(row3, Vector4d): raise ValueError("Row must be a Vector4d object.", row3) if not isinstance(row4, Vector4d): raise ValueError("Row must be a Vector4d object.", row4) self.rows = [row1, row2, row3, row4] @property def row4(self): return self.rows[3] @row4.setter def row4(self, value): self.rows[3] = value def multiply_3d(self, vector3d): x = vector3d.x * self.row1.x + vector3d.y * self.row2.x + vector3d.z * self.row3.x + self.row4.x y = vector3d.x * self.row1.y + vector3d.y * self.row2.y + vector3d.z * self.row3.y + self.row4.y z = vector3d.x * self.row1.z + vector3d.y * self.row2.z + vector3d.z * self.row3.z + self.row4.z w = vector3d.x * self.row1.w + vector3d.y * self.row2.w + vector3d.z * self.row3.w + self.row4.w if w != 0: x /= w y /= w z /= w return Vector3d(x, y, z) def multiply_matrix(self, matrix4x4): out = Matrix4x4.get_identity_matrix() for column in range(4): for row in range(4): out[row][column] = self.rows[row][0] * matrix4x4.rows[0][column] + self.rows[row][1] * matrix4x4.rows[1][column] + self.rows[row][2] * matrix4x4.rows[2][column] + self.rows[row][3] * matrix4x4.rows[3][column] return out def get_projection_matrix(fov_degrees, aspect_ratio, near_plane, far_plane): fov_rad = 1 / math.tan(fov_degrees * 0.5 / 180.0 * math.pi) row1 = Vector4d(aspect_ratio * fov_rad, 0, 0, 0) row2 = Vector4d(0, fov_rad, 0, 0) row3 = Vector4d(0, 0, far_plane / (far_plane - near_plane), 1.0) row4 = Vector4d(0, 0, -far_plane * near_plane / (far_plane - near_plane), 0) return Matrix4x4(row1, row2, row3, row4) def get_identity_matrix(): row1 = Vector4d(1, 0, 0, 0) row2 = Vector4d(0, 1, 0, 0) row3 = Vector4d(0, 0, 1, 0) row4 = Vector4d(0, 0, 0, 1) return Matrix4x4(row1, row2, row3, row4) def get_y_rotation_matrix(angle_rad): row1 = Vector4d(math.cos(angle_rad), 0, math.sin(angle_rad), 0) row2 = Vector4d(0, 1, 0, 0) row3 = Vector4d(-math.sin(angle_rad), 0, math.cos(angle_rad), 0) row4 = Vector4d(0, 0, 0, 1) return Matrix4x4(row1, row2, row3, row4) def get_z_rotation_matrix(angle_rad): row1 = Vector4d(math.cos(angle_rad), math.sin(angle_rad), 0, 0) row2 = Vector4d(-math.sin(angle_rad), math.cos(angle_rad), 0, 0) row3 = Vector4d(0, 0, 0, 0) row4 = Vector4d(0, 0, 1, 0) return Matrix4x4(row1, row2, row3, row4) def get_x_rotation_matrix(angle_rad): row1 = Vector4d(1, 0, 0, 0) row2 = Vector4d(0, math.cos(angle_rad), math.sin(angle_rad), 0) row3 = Vector4d(0, -math.sin(angle_rad), math.cos(angle_rad), 0) row4 = Vector4d(0, 0, 0, 1) return Matrix4x4(row1, row2, row3, row4) def get_translation_matrix(vector3d): row1 = Vector4d(1, 0, 0, 0) row2 = Vector4d(0, 1, 0, 0) row3 = Vector4d(0, 0, 1, 0) row4 = Vector4d(vector3d.x, vector3d.y, vector3d.z, 1) return Matrix4x4(row1, row2, row3, row4)