# TODO # make todo list # import import colorsys import copy import pygame from functions import * import multiprocessing as mp from ctypes import * cFunctions = CDLL("cFunctions.so") # variables # screen size in pixels from model import Colour, Point3D, Face, Cube size = (1000, 1000) # screen with in units scrWidth = 1 # player information playerPos: Point3D = Point3D(0, 0, 0) playerSpeed: Point3D = Point3D(0, 0, 0) # todo implement these variables into the code 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: Colour = Colour([0, 0, 0]) fgColour: Colour = Colour([255, 255, 255]) # 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])]] currentTexture: int = 0 # create cubes 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(Point3D(x[0], x[1], x[2]), [Colour([255, 0, 0]) for x in range(6)], True, True)) # todo update or remove # axes points 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 rotX = 90 rotY = 0 rotZ = 0 # distance camDistance = 0 # fov camFOV = 120 FPDis = fov_calc(camFOV, scrWidth) print(FPDis) # matrices camMatrix: numpy.array = generate_cam_matrix(rotX, rotY, rotZ, playerPos) map_matrix = numpy.array([[1, 0, 0 / FPDis], [0, 1, 0 / FPDis], [0, 0, 1 / FPDis]]) # 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.get_rgb()) # initial values for variables so pycharm doesn't get mad keys = pygame.key.get_pressed() newCube = Cube(Point3D(0, 0, 0), placingColours[0], False, False) # settings pygame.mouse.set_visible(True) grabbed = False pygame.event.set_grab(False) # create surfaces mainSurface = pygame.Surface(size) 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 > 180: rotX = 180 elif rotX < 0: rotX = 0 if not triggerDraw: lastMillis[1] = pygame.time.get_ticks() triggerDraw = False # reset surface area mainSurface.fill(fgColour.get_rgb()) # save currently pressed keys keys = pygame.key.get_pressed() # generate matrices camMatrix = generate_cam_matrix(rotX, rotY, rotZ, playerPos) invRotMatrix = gen_inv_rot_matrix(rotX, rotY, rotZ) # __draw cubes__ # calculate 3d and project 3d point locations for cube in cubes: for face in cube.get_faces(): allFaces.append(face) # 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: # todo fix invRotMatrix and add translation # todo figure out why the snapping seems to apply to individual points instead of the entire cube # generate the position for the new cube ('ray cast' out of the camera, rotate the vector and then snap # that position to the nearest integer. newCubePos = Point3D(blockDistance, 0, 0).apply_matrix(invRotMatrix) newCubePos = newCubePos.inv_translate(playerPos) newCubePos = newCubePos.snap() # generate new cube object with position of newCubePos newCube = Cube(newCubePos, placingColours[currentTexture], keys[pygame.K_LCTRL], True) # take faces out of cube object and never speak of it again (woo efficiency!!) for face in newCube.get_faces(): allFaces.append(face) for face in allFaces: for i, point in enumerate(face.points): # apply the camera matrix to the points face.points[i] = face.points[i].apply_matrix(camMatrix) # fix points behind the camera face.fix_points_behind_camera(FPDis) # 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.xyz()[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 = [point.get_2d_point(map_matrix).conv_coord(size, scale) for point in face.points] try: if True: # face_angle([face.points[0], face.points[1], face.points[2]], FPDis, camDistance) > 90: if face.hasFaces and len(face.points) > 2: pygame.draw.polygon(mainSurface, tuple(face.colour.get_rgb()), [point.get() for point in _2dPoints], 0) if face.hasEdges: for i2 in range(len(_2dPoints)): pygame.draw.aaline(mainSurface, (0, 0, 0), _2dPoints[i2].get(), _2dPoints[(i2 + 1) % len(_2dPoints)].get()) except IndexError: pass allFaces.clear() # _____end face drawing_____ # _____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 = 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.x ** 2 + pressedKeys.y ** 2 + pressedKeys.z ** 2) except ZeroDivisionError: magnitude = 0 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.xyz())]) # handle speed and movement in that direction 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] # _____end player movement_____ 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: # check if there is an existing cube in the location. If not, # append the new cube and set faces and edges to visible. occupied = False for x, cube in enumerate(cubes): if cube.position == newCube.position: occupied = True if not occupied: cubes.append(copy.deepcopy(newCube).set_faces(True)) cubes.append(Cube(newCube.position, newCube.colours, True, True)) elif event.button == pygame.BUTTON_LEFT: if placing: try: for x, cube in enumerate(cubes): if cube.position == newCube.position: 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