diff --git a/functions.py b/functions.py index ee7029c..1523276 100644 --- a/functions.py +++ b/functions.py @@ -7,7 +7,7 @@ # functions # converts a coordinate from a coordinate with origin at the middle, to a coordinate with origin at the top left -from model import Point2D, Face, TexturedFace +from model import * def conv_coord(points2d, size, scale): @@ -18,88 +18,13 @@ return points2d -# projects a 3d point onto a 2d screen -def project(points3d, fp_distance, distance): - c_type = "perspective" - if c_type == "perspective": - points2d = [] - for i in range(len(points3d)): - points2d.append([0, 0]) - # calculate x pos - try: - points2d[i][0] = ((fp_distance*points3d[i][0]) / (points3d[i][1] - distance)) - - # calculate y pos - points2d[i][1] = ((fp_distance*points3d[i][2]) / (points3d[i][1] - distance)) - except ZeroDivisionError: - points2d[i] = [0, 0] - return points2d - elif c_type == "orthographic": - points2d = [] - for points in points3d: - points2d.append([points[0], points[2]]) - return points2d - - -# rotates a 3d point -def rotate(points3d, rx, ry, rz): - rx = math.radians(rx) - ry = math.radians(ry) - rz = math.radians(rz) - - new_point3d = [0, 0, 0] - # new_points3d.append([0, 0, 0]) - new_point3d[0] = ((math.cos(rz))*points3d[0] + - (-math.sin(rz))*points3d[1]) - - new_point3d[1] = ((math.sin(rz)*math.cos(rx))*points3d[0] + - (math.cos(rz)*math.cos(rx))*points3d[1] + - (-math.sin(rx)) * points3d[2]) - - new_point3d[2] = ((math.sin(rz)*math.sin(rx))*points3d[0] + - (math.cos(rz)*math.sin(rx))*points3d[1] + - (math.cos(rx))*points3d[2]) - return new_point3d - - -def reverse_rotate(points3d, ry, rx, rz): - rx = math.radians(rx) - ry = math.radians(ry) - rz = math.radians(rz) - - new_points3d = [] - # matrix z * y * x - for p in points3d: - new_points3d.append([((math.cos(rz)*math.cos(ry))*p[0] + - (math.cos(rz)*math.sin(ry)*math.sin(rx) - math.sin(rz)*math.cos(ry))*p[1]) + - (math.cos(rz)*math.sin(ry)*math.cos(rx) + math.sin(rz)+math.sin(ry))*p[2], - - (math.sin(rz)*math.cos(ry))*p[0] + - (math.sin(rz)*math.sin(rx)*math.sin(rx) + math.cos(rz)*math.cos(rx))*p[1] + - (math.sin(rz)*math.sin(ry)*math.sin(rx) - math.cos(rz)*math.cos(rx))*p[2], - - -(math.sin(ry)*p[0] + - (math.cos(ry)*math.sin(rx))*p[1] + - (math.cos(ry)*math.cos(rx))*p[2]) - ]) - return new_points3d - - -def translate(points3d, t_vec): - new_points3d = list(points3d) - for i in range(len(points3d)): - new_points3d[i][0] += t_vec[0] - new_points3d[i][1] += t_vec[1] - new_points3d[i][2] += t_vec[2] - return new_points3d - - # 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: @@ -135,43 +60,6 @@ return result -# returns a new list of points, without ones that are behind the camera -def fix_list_points_behind_camera(points3d, distance, face_list): - new_points3d = [] - invalid_points = [] - for i in range(len(points3d)): - if points3d[i][1] <= distance + .5: - invalid_points.append(points3d[i]) - for j in range(len(face_list)): - try: - face_list[j].remove(i) - except ValueError: - continue - else: - new_points3d.append(points3d[i]) - return points3d - - -# returns a new list of points, without ones that are behind the camera - for edges (only 2 points) -def fix_points_behind_camera_edge(p1, p2, distance): - if p2[1] <= distance + 0.01 and p1[1] <= distance + 0.01: - print("aaa") - return [] - elif p1[1] <= distance + 0.01: - diff = sub_l(p2, p1) - percentage = (p2[1]-1) / (p2[1]-p1[1]) - - new_p1 = [p2[0] - (diff[0]*percentage), 1, p2[2] - (diff[2] * percentage)] - return [new_p1, p2] - elif p2[1] <= distance + 0.01: - diff = sub_l(p1, p2) - percentage = (p1[1]-1) / (p1[1]-p2[1]) - new_p2 = [p1[0] - (diff[0]*percentage), 1, p1[2] - (diff[2] * percentage)] - return [p1, new_p2] - else: - return[p1, p2] - - # new functions def generate_cam_matrix(rx_d: int, ry_d: int, rz_d: int, player_pos): rx = math.radians(rx_d) @@ -234,9 +122,9 @@ def draw_face(face: Face, cam_matrix: numpy.array, fp_dis): if type(face) is TexturedFace: raise TypeError - # has face to draw and precalculated camera rotation matrix, along with player position as input + # 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 + # this function returns the face no matter what (face angle checking must be done elsewhere) _2dPoints: list[Point2D] = [] # apply camera position and rotation @@ -249,293 +137,4 @@ 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 - - -# old classes -# class Face: -# def __init__(self, points: list[list[float]], colour: Colour, has_edges, has_faces: bool): -# self.hasEdges = has_edges # whether the main face has edges -# self.colour = colour -# self.points = points -# self.hasFaces = has_faces -# -# def get_points_order(self): -# order = [] -# for i, _ in enumerate(self.points): -# order.append(i) -# return order -# -# points: list[list[float]] = [] # main edges of the face (used for distance and back face culling calculations) -# colour: Colour = [] # colour of the face -# hasEdges: bool = [] -# hasFaces: bool = [] -# -# -# # contains n faces that are all in the same plane -# class TexturedFace: -# def __init__(self, face: Face): # init from existing face object (probably an easier way of doing this) -# self.hasEdges = face.hasEdges # whether the main face has edges -# self.colour = face.colour -# self.points = face.points -# self.hasFaces = face.hasFaces -# -# def get_points_order(self): -# order = [] -# for i, _ in enumerate(self.points): -# order.append(i) -# return order -# -# def set_sub_faces(self, sub_faces: list[Face]): -# self.sub_faces = sub_faces -# -# def get_sub_faces(self): -# return self.sub_faces -# -# points: list[list[float]] = [] # main edges of the face (used for distance and back face culling calculations) -# colour: Colour = [] # colour of the main face (should be unused 99% of the time) -# hasEdges: bool = [] -# hasFaces: bool = [] # whether the main face renders (should always be false) -# sub_faces: list[Face] -# -# -# class Texture: -# # Y -> X -> Colour -# pixels: list[list[Colour]] = [] -# -# def __init__(self, path: str): -# self.pixels = [] -# -# image = imageio.imread(path) -# for i, image_y in enumerate(image): -# self.pixels.append([]) -# for image_x in image_y: -# self.pixels[i].append(Colour(image_x)) -# -# def get(self): -# return self.pixels -# -# def get_pixel(self, x, y): -# return self.pixels[y][x] -# -# def get_pixel_alpha(self, x, y): -# return self.pixels[y][x].get_alpha() -# -# -# class Cube: -# position: list[int] = [] -# colours: list[Colour] = [] # list of 6 colours [legacy, will be removed] -# textures: list[Texture] = [] # list of 6 textures for each face of the cube -# has_edges: bool = [] -# has_faces: bool = [] -# textured_face_list: list[TexturedFace] = None -# # ordered top - north - east - south - west - bottom -# cubePoints = [[.5, .5, .5], [.5, .5, -.5], [.5, -.5, .5], [.5, -.5, -.5], -# [-.5, .5, .5], [-.5, .5, -.5], [-.5, -.5, .5], [-.5, -.5, -.5]] -# cubeSides = [[3, 7, 5, 1], [2, 6, 7, 3], [4, 5, 7, 6], [0, 1, 5, 4], [0, 2, 3, 1], [6, 2, 0, 4]] -# -# # generate the points for a subdivided 16*16 face -# sub_div_points = [] -# y = -.5 -# for i in range(17): -# x = -.5 -# sub_div_points.append([]) -# for j in range(17): -# sub_div_points[i].append([x, y]) -# x += 1 / 16 -# y += 1 / 16 -# -# # generate the face_list for a subdivided 16*16 face -# sub_div_faces = [] -# y = 0 -# for i in range(16): -# x = 0 -# for j in range(16): -# sub_div_faces.append([[x, y], [1 + x, y], [1 + x, 1 + y], [x, y + 1]]) -# x += 1 -# y += 1 -# -# def __init__(self, position: list[int], colour, has_edges, has_faces): -# self.has_edges = has_edges -# self.has_faces = has_faces -# -# self.colours = [[], [], [], [], [], []] -# self.textures = [[], [], [], [], [], []] -# self.position = position -# -# if type(colour) is list: -# if type(colour[0]) is Texture: -# for i in range(6): -# self.textures[i] = colour[i] -# else: -# for i in range(6): -# self.colours[i] = colour[i] -# else: -# for i in range(6): -# self.colours[i] = colour -# -# # legacy function, will be removed -# def get_geometry_points(self, player_pos): -# points_list = translate(translate(self.cubePoints, player_pos), self.position) -# face_list = copy.deepcopy(self.cubeSides) -# return [points_list, face_list] -# -# def has_texture(self): -# if not self.textures[0]: -# return False -# else: -# return True -# -# def get_faces(self, player_pos): -# face_list: list[Face] = [] -# for x in range(6): -# points_list = translate(translate(self.cubePoints, player_pos), self.position) -# if not self.colours[0]: -# face_list.append(Face([points_list[face] -# for face in self.cubeSides[x]], -# Colour([0, 255, 0]), self.has_edges, False)) -# else: -# face_list.append(Face([points_list[face] -# for face in self.cubeSides[x]], -# self.colours[x], self.has_edges, self.has_faces)) -# return face_list -# -# def get_textured_faces(self, player_pos): # returns a list of 6 textured face objects -# -# if self.textured_face_list is None: -# textured_face_list = [TexturedFace(i) for i in self.get_faces([0, 0, 0])] -# # top face (translate up by .5) -# new_sub_faces = [] -# i = 0 -# for j, face in enumerate(self.sub_div_faces): -# points = [copy.deepcopy(self.sub_div_points[point[0]][point[1]]) for point in face] -# for point in points: -# -# # set the z coordinate to -.5 for top face positioning -# point.insert(2, -.5) -# -# y = j % 16 -# x = j // 16 -# if True: # not self.textures[i].get_pixel_alpha(x, y): -# new_sub_faces.append(Face(points, self.textures[i].get_pixel(x, y), False, True)) -# textured_face_list[i].set_sub_faces(new_sub_faces) -# -# # bottom face (5) (translate down by .5, mirror in x) -# new_sub_faces = [] -# i = 5 -# for j, face in enumerate(self.sub_div_faces): -# points = [copy.deepcopy(self.sub_div_points[point[0]][point[1]]) for point in face] -# for point in points: -# -# # set the z coordinate to .5 for bottom face positioning -# point.insert(2, .5) -# # mirror the face in x -# point[0] = -point[0] -# -# y = j % 16 -# x = j // 16 -# if True: # not self.textures[i].get_pixel_alpha(x, y): -# new_sub_faces.append(Face(points, self.textures[i].get_pixel(x, y), False, True)) -# textured_face_list[i].set_sub_faces(new_sub_faces) -# -# # north (swap y and z) -# new_sub_faces = [] -# i = 1 -# for j, face in enumerate(self.sub_div_faces): -# points = [copy.deepcopy(self.sub_div_points[point[0]][point[1]]) for point in face] -# for point in points: -# -# # set the z coordinate to .5 -# point.insert(2, .5) -# # swap x and z -# temp = point[2] -# point[2] = point[1] -# point[1] = temp -# # mirror the face in x -# # point[0] = -point[0] -# -# y = j % 16 -# x = j // 16 -# if True: # not self.textures[i].get_pixel_alpha(x, y): -# new_sub_faces.append(Face(points, self.textures[i].get_pixel(x, y), False, True)) -# textured_face_list[i].set_sub_faces(new_sub_faces) -# -# # south = swap y and z, mirror in x -# new_sub_faces = [] -# i = 3 -# for j, face in enumerate(self.sub_div_faces): -# points = [copy.deepcopy(self.sub_div_points[point[0]][point[1]]) for point in face] -# for point in points: -# -# # set the z coordinate to -.5 -# point.insert(2, -.5) -# # swap x and z -# temp = point[2] -# point[2] = point[1] -# point[1] = temp -# # mirror the face in x -# point[0] = -point[0] -# -# y = j % 16 -# x = j // 16 -# if True: # not self.textures[i].get_pixel_alpha(x, y): -# new_sub_faces.append(Face(points, self.textures[i].get_pixel(x, y), False, True)) -# textured_face_list[i].set_sub_faces(new_sub_faces) -# -# # east face = swap x and z -# new_sub_faces = [] -# i = 2 -# for j, face in enumerate(self.sub_div_faces): -# points = [copy.deepcopy(self.sub_div_points[point[0]][point[1]]) for point in face] -# for point in points: -# -# # set the z coordinate to .5 -# point.insert(2, .5) -# # swap y and z -# temp = point[2] -# point[2] = point[1] -# point[1] = temp -# # swap x and y -# temp = point[1] -# point[1] = point[0] -# point[0] = temp -# # mirror the face in y -# point[1] = -point[1] -# y = j % 16 -# x = j // 16 -# if True: # not self.textures[i].get_pixel_alpha(x, y): -# new_sub_faces.append(Face(points, self.textures[i].get_pixel(x, y), False, True)) -# textured_face_list[i].set_sub_faces(new_sub_faces) -# # west face = swap y and z, and reverse sub lists -# new_sub_faces = [] -# i = 4 -# for j, face in enumerate(self.sub_div_faces): -# points = [copy.deepcopy(self.sub_div_points[point[0]][point[1]]) for point in face] -# for point in points: -# -# # set the z coordinate to .5 -# point.insert(2, .5) -# # swap y and z -# temp = point[2] -# point[2] = point[1] -# point[1] = temp -# # swap x and y -# temp = point[1] -# point[1] = point[0] -# point[0] = temp -# # mirror the face in x -# point[0] = -point[0] -# y = j % 16 -# x = j // 16 -# if True: # not self.textures[i].get_pixel_alpha(x, y): -# new_sub_faces.append(Face(points, self.textures[i].get_pixel(x, y), False, True)) -# textured_face_list[i].set_sub_faces(new_sub_faces) -# # return a face_list with the colours of the texture applied -# self.textured_face_list = textured_face_list -# textured_face_list = copy.deepcopy(self.textured_face_list) -# for textured_face in textured_face_list: -# for face in textured_face.sub_faces: -# for point in face.points: -# for i in range(3): -# point[i] += player_pos[i] -# return textured_face_list + return None \ No newline at end of file diff --git a/main.py b/main.py index 5cbe72d..93593fe 100644 --- a/main.py +++ b/main.py @@ -25,7 +25,6 @@ maxSpeed = 4 # units per second sensitivity = 0.05 - frameRate = 60 # measures the frame-rate (if this decreases then other things are sped up) # program variables @@ -109,7 +108,7 @@ while running: # every tick: - if lastMillis[1] + (16.6 * 2) <= pygame.time.get_ticks() or triggerDraw: + if lastMillis[1] + (16.6) <= pygame.time.get_ticks() or triggerDraw: frameRate = 1 / ((pygame.time.get_ticks() - lastMillis[1]) / 1000) # change colours and apply rotation from mouse if grabbed: @@ -251,7 +250,8 @@ pass allFaces.clear() # _____end face drawing_____ - # player movement + + # _____player movement_____ # speed increase with key press if keys[pygame.K_LCTRL] == 1: sprint = 5 @@ -260,28 +260,36 @@ # calculate the key presses, figure out a force direction, then rotate it, then apply a magnitude, # then apply that to the player speed, subtract the drag (value proportional to the speed) from the vector. - pressedKeys = [(keys[pygame.K_a] - keys[pygame.K_d]), - (keys[pygame.K_w] - keys[pygame.K_s]), - (keys[pygame.K_SPACE] - keys[pygame.K_LSHIFT])] + pressedKeys = Point3D((keys[pygame.K_a] - keys[pygame.K_d]), + (keys[pygame.K_w] - keys[pygame.K_s]), + (keys[pygame.K_SPACE] - keys[pygame.K_LSHIFT])) try: - magnitude = 1 / math.sqrt(pressedKeys[0] ** 2 + pressedKeys[1] ** 2 + pressedKeys[2] ** 2) + magnitude = 1 / math.sqrt(pressedKeys.x ** 2 + pressedKeys.y ** 2 + pressedKeys.z ** 2) except ZeroDivisionError: magnitude = 0 - vectoredDirection = [x * magnitude for x in pressedKeys] - rotatedVector = rotate(vectoredDirection, 0, 0, rotZ) + vectoredDirection = Point3D._make([x * magnitude for x in pressedKeys.xyz()]) + + radZ = math.radians(-rotZ) + rotatedVector = vectoredDirection.apply_matrix(numpy.array([[math.cos(radZ), math.sin(radZ), 0, 0], + [-math.sin(radZ), math.cos(radZ), 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1]])) playerSpeed = Point3D._make( - [(speed + ((acc / frameRate) * kPress)) * (1 - (((acc-1) / maxSpeed)/frameRate)) - for speed, kPress in zip(playerSpeed.xyz(), rotatedVector)]) + [(speed + ((acc / frameRate) * kPress)) * (1 - (((acc - 1) / maxSpeed) / frameRate)) + for speed, kPress in zip(playerSpeed.xyz(), rotatedVector.xyz())]) # handle speed and movement in that direction - move = Point3D._make([x / frameRate for x in playerSpeed.xyz()]) + if frameRate > 1: + move = Point3D._make([x / frameRate for x in playerSpeed.xyz()]) + else: + move = playerSpeed playerPos = playerPos.translate(move) # handle Q and E rotation - #rotY += keys[pygame.K_q] - keys[pygame.K_e] - + # rotY += keys[pygame.K_q] - keys[pygame.K_e] + # _____end player movement_____ screen.blit(mainSurface, (0, 0)) pygame.display.update() # END EVERY FRAME diff --git a/model.py b/model.py index 95231d8..ff60032 100644 --- a/model.py +++ b/model.py @@ -3,6 +3,7 @@ from typing import NamedTuple import imageio +# todo fix this import numpy @@ -195,9 +196,8 @@ class TexturedFace(Face): - # todo: - # write the ability to get sub faces - # figure out a way of storing the 'index' of the face (probably in the cube class) + # todo write the ability to get sub faces + # todo figure out a way of storing the 'index' of the face (probably in the cube class) texture: Texture