# 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