diff --git a/functions.py b/functions.py new file mode 100644 index 0000000..68e7015 --- /dev/null +++ b/functions.py @@ -0,0 +1,510 @@ +import math +import copy +import colorsys +import random +import imageio + + +# functions +# converts a coordinate from a coordinate with origin at the middle, to a coordinate with origin at the top left +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 + + +# 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_points3d = [] + for i in range(len(points3d)): + new_points3d.append([0, 0, 0]) + new_points3d[i][0] = ((math.cos(rz))*points3d[i][0] + + (-math.sin(rz))*points3d[i][1]) + + new_points3d[i][1] = ((math.sin(rz)*math.cos(rx))*points3d[i][0] + + (math.cos(rz)*math.cos(rx))*points3d[i][1] + + (-math.sin(rx)) * points3d[i][2]) + + new_points3d[i][2] = ((math.sin(rz)*math.sin(rx))*points3d[i][0] + + (math.cos(rz)*math.sin(rx))*points3d[i][1] + + (math.cos(rx))*points3d[i][2]) + return new_points3d + + +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 = copy.deepcopy(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 + + +# 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 + + +# 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]) + # print(points3d[i][1], distance) + for j in range(len(face_list)): + try: + face_list[j].remove(i) + except ValueError: + continue + else: + new_points3d.append(points3d[i]) + return points3d + + +def fix_points_behind_camera(points3d, distance): + new_points3d = [] + for _3point in points3d: + if _3point[1] > distance + .5: + new_points3d.append(_3point) + return new_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] + + +# classes +class Colour: + r = 0 + g = 0 + b = 0 + a = 0 + + def __init__(self, rgb): + if len(rgb) == 3: + self.r = rgb[0] + self.g = rgb[1] + self.b = rgb[2] + elif len(rgb) == 4: + self.r = rgb[0] + self.g = rgb[1] + self.b = rgb[2] + self.a = rgb[3] + + def set_rgb(self, rgb: list[int]): + self.r = rgb[0] + self.g = rgb[1] + self.b = rgb[2] + + def get_rgb(self): + return [self.r, self.g, self.b] + + def set_hsv(self, hsv): + rgb = [x * 256 for x in colorsys.hsv_to_rgb(hsv[0] / 256, hsv[1] / 256, hsv[2] / 256)] + self.r = rgb[0] + self.g = rgb[1] + self.b = rgb[2] + + def get_hsv(self): + hsv = [x * 256 for x in colorsys.hsv_to_rgb(self.r / 256, self.g / 256, self.b / 256)] + return [self.r, self.g, self.b] + + def get_alpha(self): + if self.a > 128: + return True + else: + return False + + +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 diff --git a/grass.png b/grass.png new file mode 100644 index 0000000..90b580f --- /dev/null +++ b/grass.png Binary files differ diff --git a/main.py b/main.py new file mode 100644 index 0000000..ff0fa1c --- /dev/null +++ b/main.py @@ -0,0 +1,322 @@ +# TODO +# make todo list + +# import +import copy + +import pygame +from functions import * + +# variables +# screen size in pixels +size = (1000, 1000) +# screen with in units +scrWidth = 3.2 +playerPos = [0, 10, 0] +playerSpeed = [0, 0.01, 0] + +acc = 20 # units per second per second +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 +running = True +triggerDraw = True +placing = True +blockDistance = 3 + +lastMillis = [0, 0, 0] +scale = size[1] / scrWidth +allFaces: list[Face] = [] + +# colours +bgColour = (0, 0, 0) +fgColour = (255, 255, 255) +polyColour = [(255, 120, 120), (255, 120, 120), (255, 120, 120), (255, 120, 120), (255, 120, 120), (255, 120, 120)] +hue = 0 + +# placing colours is to be replaced with placingTextures +placingColours: list[list[Colour]] = [[Colour([100, 100, 100]), Colour([100, 100, 100]), Colour([100, 100, 100]), Colour([100, 100, 100]), Colour([100, 100, 100]), Colour([100, 100, 100])], + [Colour([162, 84, 57]), Colour([162, 84, 57]), Colour([162, 84, 57]), Colour([162, 84, 57]), Colour([162, 84, 57]), Colour([162, 84, 57])], + [Colour([50, 255, 50]), Colour([100, 255, 100]), Colour([100, 255, 100]), Colour([100, 255, 100]), Colour([100, 255, 100]), Colour([162, 84, 57])]] + +print(placingColours) +currentTexture: int = 0 + +cubes: list[Cube] = [] +for x in [[-12, 3, 4], [2, 2, 2], [2, 0, 0], [0, 2, 0], [0, 0, 0], [6, -8, 4]]: + cubes.append(Cube(x, Colour([255, 0, 0]), True, True)) + + +axesPoints = [[1000, 0, 0], [-1000, 0, 0], [0, 1000, 0], [0, -1000, 0], [0, 0, 1000], [0, 0, -1000]] +axesEdges = [[0, 1], [2, 3], [4, 5]] +axesColours = [(0, .8, .8), (.2, .8, 1), (.8, .8, .8)] + + +i = 0 +for c in axesColours: + axesColours[i] = colorsys.hsv_to_rgb(c[0], c[1], c[2]) + axesColours[i] = tuple(255 * x for x in axesColours[i]) + i += 1 + +# rendering variables +# rotation +rotY = 0 +rotX = 0 +rotZ = 0 + +# distance +camDistance = 0 +# fov +camFOV = 120 + +FPDis = fov_calc(camFOV, scrWidth) + +# start +pygame.init() +lastMillis[1] = pygame.time.get_ticks() + 1000 +lastMillis[2] = pygame.time.get_ticks() + 1000 +timerOffset = 0 +screen = pygame.display.set_mode(size) +screen.fill(bgColour) + +# initial values for variables so pycharm doesn't get annoyed +keys = pygame.key.get_pressed() +newCubePos = [0, 0, 0] + +# settings +pygame.mouse.set_visible(True) +grabbed = False +pygame.event.set_grab(False) + +# create surfaces +mainSurface = pygame.Surface(size) + +grass = [Texture("grass.png") for x in range(6)] +cubes.append(Cube([0, 2, 3], grass, True, True)) + +for i in range(len(polyColour)): + polyColour[i] = tuple(255 * x for x in colorsys.hsv_to_rgb(hue, .5, 1)) + hue += 1 / 6 + +while running: + # every tick: + 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: + # rotate camera if mouse moves + dZ, dX = pygame.mouse.get_rel() + rotX -= dX * sensitivity + rotZ += dZ * sensitivity + + # fix camera within movement bounds + rotZ = rotZ % 360 + if rotX > 90: + rotX = 90 + elif rotX < -90: + rotX = -90 + + if not triggerDraw: + lastMillis[1] = pygame.time.get_ticks() + triggerDraw = False + # reset surface area + mainSurface.fill(fgColour) + + # save currently pressed keys + keys = pygame.key.get_pressed() + + # __draw cubes__ + # calculate 3d and project 3d point locations + col = 0 + for cube in cubes: + if cube.has_texture(): + texturedFaces = cube.get_textured_faces(playerPos) + for texturedFace in texturedFaces: + newSubFaces = [] + try: + faces = texturedFace.get_sub_faces() + for face in faces: + newPoints = fix_points_behind_camera(rotate(face.points, rotX, rotY, rotZ), camDistance) + if len(newPoints) > 2: + face.points = newPoints + newSubFaces.append(face) + except AttributeError: + pass + texturedFace.set_sub_faces(newSubFaces) + allFaces.append(texturedFace) + faces = cube.get_faces(playerPos) + # fix points behind camera and remove faces with two or fewer remaining points + for face in faces: + newPoints = fix_points_behind_camera(rotate(face.points, rotX, rotY, rotZ), camDistance) + if len(newPoints) > 2: + face.points = newPoints + allFaces.append(face) + + # add faces for placeable cube + if placing: + # generate the position for the new cube ('ray cast' out of camera and do weird things) + newCubePos = reverse_rotate([[blockDistance, 0, 0]], rotX, rotY, -rotZ + 90) + newCubePos = translate(newCubePos, [-x for x in playerPos]) + newCubePos = [[int(round(x)) for x in newCubePos[0]]] + # generate cube object with position of newCubePos + newCube = Cube(newCubePos[0], placingColours[currentTexture], True, keys[pygame.K_LCTRL]) + # take faces out of cube object and never speak of it again + faces = newCube.get_faces(playerPos) + # fix points behind camera and remove faces with two or fewer remaining points + for face in faces: + newPoints = fix_points_behind_camera(rotate(face.points, rotX, rotY, rotZ), camDistance) + if len(newPoints) > 2: + face.points = newPoints + allFaces.append(face) + # ______ TESTING ____ + # generate the points for a subdivided 16*16 face + grass = Texture("grass.png") + + 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 + + for i, face in enumerate(sub_div_faces): + points = [copy.deepcopy(sub_div_points[point[0]][point[1]]) for point in sub_div_faces[i]] + for point in points: + point.insert(2, 1) + y = i % 16 + x = i // 16 + newPoints = fix_points_behind_camera(rotate(translate(points, playerPos), rotX, rotY, rotZ), camDistance) + if len(newPoints) > 2: + allFaces.append(Face(newPoints, grass.get_pixel(x, y), False, True)) + + # ______draw all faces______ + # sort the list of allFaces + # iterates through all faces - iterates through each point axis - iterates through points within faces + # (strange order) + allFaces_sort = [] + for face in allFaces: + average = [0, 0, 0] + for index in range(3): + for point in face.points: + average[index] += point[index] + average[index] = average[index] / 3 + + # 3d pythagoras to find the distance to the camera + allFaces_sort.append(-(average[0] ** 2 + average[1] ** 2 + average[2] ** 2)) + allFaces = [x for _, x in sorted(zip(allFaces_sort, allFaces), key=lambda item: item[0])] + + # draw allFaces + i = 0 + for i, face in enumerate(allFaces): + _2dPoints = conv_coord(project(face.points, FPDis, camDistance), size, scale) + try: + if True: # face_angle([face.points[0], face.points[1], face.points[2]], FPDis, camDistance) > 90: + if face.hasFaces: + pygame.draw.polygon(mainSurface, tuple(face.colour.get_rgb()), _2dPoints, 0) + if face.hasEdges: + for i2 in range(len(_2dPoints)): + pygame.draw.aaline(mainSurface, (0, 0, 0), _2dPoints[i2], + _2dPoints[(i2 + 1) % len(_2dPoints)]) + except IndexError: + print("found face with only 2 points (This error occurring here indicates a bug in the code somewhere)") + allFaces.clear() + # _____end face drawing_____ + + # draw axes + # _3dPoints = rotate(translate(axesPoints, playerPos), rotX, rotY, rotZ) + # for i in range(len(axesEdges)): + # if not (_3dPoints[axesEdges[i][0]][1] <= camDistance and _3dPoints[axesEdges[i][1]][1] <= camDistance): + # _new3dPoints = fix_points_behind_camera_edge(_3dPoints[axesEdges[i][0]], _3dPoints[axesEdges[i][1]], + # camDistance) + # _2dPoints = conv_coord(project(_new3dPoints, FPDis, camDistance), size, scale) + + # player movement + # speed increase with key press + if keys[pygame.K_LCTRL] == 1: + sprint = 5 + else: + sprint = 1 + + # 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_s] - keys[pygame.K_w]), + (keys[pygame.K_SPACE] - keys[pygame.K_LSHIFT])] + try: + magnitude = 1 / math.sqrt(pressedKeys[0] ** 2 + pressedKeys[1] ** 2 + pressedKeys[2] ** 2) + except ZeroDivisionError: + magnitude = 0 + + vectoredDirection = [x * magnitude for x in pressedKeys] + rotatedVector = rotate([vectoredDirection], 0, 0, -rotZ)[0] + + playerSpeed = [(speed + ((acc / frameRate) * kPress)) * (1 - (((acc-1) / maxSpeed)/frameRate)) + for speed, kPress in zip(playerSpeed, rotatedVector)] + + # handle speed and movement in that direction + move = [x / frameRate for x in playerSpeed] + for j in range(3): + playerPos[j] += move[j] + + screen.blit(mainSurface, (0, 0)) + pygame.display.update() + # END EVERY FRAME + + # handle events + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + pygame.display.quit() + # keyboard + elif event.type == pygame.KEYDOWN: + if event.key == pygame.K_BACKSPACE: + running = False + elif event.key == pygame.K_ESCAPE: + grabbed = False + pygame.event.set_grab(False) + pygame.mouse.set_visible(True) + elif event.key == pygame.K_0: + placing = False + elif event.key == pygame.K_1: + placing = True + # mouse + elif event.type == pygame.MOUSEBUTTONDOWN: + if event.button == pygame.BUTTON_MIDDLE: + pygame.mouse.get_rel() + pygame.event.set_grab(True) + pygame.mouse.set_visible(False) + grabbed = True + elif event.button == pygame.BUTTON_RIGHT: + if placing: + cubes.append(Cube(newCubePos[0], placingColours[currentTexture], True, keys[pygame.K_LCTRL])) + + elif event.button == pygame.BUTTON_LEFT: + if placing: + try: + for x, cube in enumerate(cubes): + if cube.position == newCubePos[0]: + cubes.pop(x) + except ValueError: + continue + elif event.type == pygame.MOUSEWHEEL: + if keys[pygame.K_LCTRL]: + currentTexture = (currentTexture + 1) % len(placingColours) + else: + blockDistance += event.y diff --git a/textures_0.png b/textures_0.png new file mode 100644 index 0000000..d12c01e --- /dev/null +++ b/textures_0.png Binary files differ