Newer
Older
3d_renderer / functions.py
import math
import copy
import numpy

# todo (once everything is mildly sorted) - find out which of these are unused


# functions
# converts a coordinate from a coordinate with origin at the middle, to a coordinate with origin at the top left
from model import *


def conv_coord(points2d, size, scale):
    for i in range(len(points2d)):
        points2d[i][0] = (points2d[i][0] * scale) + size[0] / 2
        points2d[i][1] = (points2d[i][1] * scale) + size[1] / 2

    return points2d


# calculates the focal point distance given an FOV and screen size in units
def fov_calc(fov, size):
    fp_distance = (size / (2 * (math.tan(math.radians(fov) / 2))))
    return fp_distance


# todo use numpy magic to optimise + collapse these
# function that returns the angle of a face to the camera
def face_angle(points3d, fp_distance, distance):
    # calculate cross product:
    vec1 = points3d[0][0], points3d[0][1] - (distance), points3d[0][2]
    vec2 = cross_product(sub_l(points3d[0], points3d[1]),
                         sub_l(points3d[1], points3d[2]))
    return math.degrees(_2vec_angle(vec1, vec2))


# returns the cross product of two vectors
def cross_product(vector1, vector2):
    product = [vector1[1]*vector2[2] - vector1[2]*vector2[1],
               vector1[2]*vector2[0] - vector1[0]*vector2[2],
               vector1[0]*vector2[1] - vector1[1]*vector2[0]]
    return product


# returns the angle of 2 vectors in radians
def _2vec_angle(vector1, vector2):
    result = math.acos(
        (vector1[0]*vector2[0] + vector1[1]*vector2[1] + vector1[2]*vector2[2])
        /  # -----------------------------------------------------------
        (math.sqrt((vector1[0] ** 2) + (vector1[1] ** 2) + (vector1[2] ** 2)) *
         math.sqrt((vector2[0] ** 2) + (vector2[1] ** 2) + (vector2[2] ** 2)))
        )
    return result


def sub_l(list1, list2):
    result = list()
    for i1, i2 in zip(list1, list2):
        result.append(i1 - i2)
    return result


# new functions
def generate_cam_matrix(rx_d: int, ry_d: int, rz_d: int, player_pos):
    rx = math.radians(rx_d)
    ry = math.radians(ry_d)
    rz = math.radians(rz_d)

    x_rot = numpy.array([[1, 0, 0, 0],
                         [0, math.cos(rx), math.sin(rx), 0],
                         [0, -math.sin(rx), math.cos(rx), 0],
                         [0, 0, 0, 1]])

    y_rot = numpy.array([[math.cos(ry), 0, -math.sin(ry), 0],
                         [0, 1, 0, 0],
                         [math.sin(ry), 0, math.cos(ry), 0],
                         [0, 0, 0, 1]])

    z_rot = numpy.array([[math.cos(rz), math.sin(rz), 0, 0],
                         [-math.sin(rz), math.cos(rz), 0, 0],
                         [0, 0, 1, 0],
                         [0, 0, 0, 1]])
    rotation_matrix = x_rot.dot(y_rot).dot(z_rot)

    translation_matrix = numpy.array(
        [[1, 0, 0, player_pos.x],
         [0, 1, 0, player_pos.y],
         [0, 0, 1, player_pos.z],
         [0, 0, 0, 1]]
    )
    # todo implement mapping matrix
    # map_matrix = numpy.array([[1, 0, 0 / fp_dis, 0],
    #                           [0, 1, 0 / fp_dis, 0],
    #                           [0, 0, 1 / fp_dis, 0],
    #                           [0, 0, 0, 1]])

    return rotation_matrix.dot(translation_matrix)


def gen_inv_rot_matrix(rx_d, ry_d, rz_d):
    rx = math.radians(rx_d)
    ry = math.radians(- rx_d + 90)
    rz = math.radians(- rz_d + 90)

    x_rot = numpy.array([[1, 0, 0, 0],
                         [0, math.cos(rx), math.sin(rx), 0],
                         [0, -math.sin(rx), math.cos(rx), 0],
                         [0, 0, 0, 1]])

    y_rot = numpy.array([[math.cos(ry), 0, -math.sin(ry), 0],
                         [0, 1, 0, 0],
                         [math.sin(ry), 0, math.cos(ry), 0],
                         [0, 0, 0, 1]])

    z_rot = numpy.array([[math.cos(rz), math.sin(rz), 0, 0],
                         [-math.sin(rz), math.cos(rz), 0, 0],
                         [0, 0, 1, 0],
                         [0, 0, 0, 1]])
    return z_rot.dot(y_rot).dot(x_rot)


def draw_face(face: Face, cam_matrix: numpy.array, fp_dis):
    if type(face) is TexturedFace:
        raise TypeError
    # has a face to draw and precalculated camera rotation matrix, along with player position as input
    # the face has the global coordinates here
    # this function returns the face no matter what (face angle checking must be done elsewhere)

    _2dPoints: list[Point2D] = []
    # apply camera position and rotation
    for point in face.points:
        point.apply_matrix(cam_matrix)
        _2dPoints.append(point.get_2d_point(fp_dis))
    return _2dPoints


def draw_textured_face(face: Face, transform_matrix: numpy.array):
    # has face and a camera rotation + position matrix as input, draws the textured face onto the screen

    return None