# TODO
# make todo list
# import
import colorsys
import pygame
from functions import *
# variables
# screen size in pixels
from model import Colour, Point3D, Face, Cube
size = (1000, 1000)
# screen with in units
scrWidth = 3.2
# player information
playerPos: Point3D = Point3D([0, 10, 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), [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
rotY = 0
rotX = 0
rotZ = 0
# distance
camDistance = 0
# fov
camFOV = 120
# matrices
camMatrix: numpy.array = generate_cam_matrix(rotX, rotY, rotZ, playerPos)
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.get_rgb())
# initial values for variables so pycharm doesn't get mad
keys = pygame.key.get_pressed()
newCubePos: Point3D = Point3D([0, 0, 0])
# 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 > 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.get_rgb())
# save currently pressed keys
keys = pygame.key.get_pressed()
# generate matrices
camMatrix = generate_cam_matrix(rotX, rotY, rotZ, playerPos)
# __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)
# whyyyy
# add faces for placeable cube
if placing:
# generate the position for the new cube ('ray cast' out of camera and do weird things)
newCubePos = Point3D(reverse_rotate([[blockDistance, 0, 0]], rotX, rotY, -rotZ + 90)[0]) # todo change this to a .applymatrix
newCubePos.inv_translate(playerPos)
newCubePos.snap()
# generate cube object with position of newCubePos
newCube = Cube(newCubePos, placingColours[currentTexture], True, keys[pygame.K_LCTRL])
# take faces out of cube object and never speak of it again
for face in newCube.get_faces():
allFaces.append(face)
# # 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)
# ______draw all faces______
# apply camMatrix to allFaces
for face in allFaces:
# newpoints: list[Point3D] = []
for point in face.points:
point.apply_matrix(camMatrix)
# newpoints.append(Point3D([0, 0, 0]))
# point = Point3D(rotate(translate([point.xyz()], playerPos.xyz()), rotX, rotY, rotZ)[0])
# face.points = newpoints
# fix points behind the camera
face.fix_points_behind_camera()
# if len(face.points) < 3:
# allFaces.remove(face)
# 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 = Point3D([0, 0, 0])
for index in range(3):
for point in face.points:
average.xyz()[index] += point.xyz()[index]
average.xyz()[index] = average.xyz()[index] / 3
# 3d pythagoras to find the distance to the camera
allFaces_sort.append(-(average.x ** 2 + average.y ** 2 + average.z ** 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(FPDis).conv_coord(size, scale) for point in face.points]
# _2dPoints = [point.conv_coord(size, scale) for point in _2dPoints] # this seems really illegal
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:
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 = Point3D([(speed + ((acc / frameRate) * kPress)) * (1 - (((acc-1) / maxSpeed)/frameRate))
for speed, kPress in zip(playerSpeed.xyz(), rotatedVector)])
# handle speed and movement in that direction
move = Point3D([x / frameRate for x in playerSpeed.xyz()])
playerPos.translate(move)
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, 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:
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