diff --git a/functions.py b/functions.py index c78c8a0..747c6db 100644 --- a/functions.py +++ b/functions.py @@ -1,8 +1,5 @@ import math import copy -import colorsys -import random -import imageio import numpy # todo (once everything is mildly sorted) - find out which of these are unused @@ -10,6 +7,9 @@ # 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 + + def conv_coord(points2d, size, scale): for i in range(len(points2d)): points2d[i][0] = (points2d[i][0] * scale) + size[0] / 2 @@ -174,234 +174,6 @@ return[p1, p2] -# new 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 Point2D: - x: float - y: float - - def __init__(self, x, y): - self.x = x - self.y = y - - def get(self): - return [self.x, self.y] - - def conv_coord(self, size, scale): - self.x = (self.x * scale) + size[0]/2 - self.y = (self.y * scale) + size[0]/2 - - -class Point3D: - # todo: - # check if i have missed anything obvious - x: float - y: float - z: float - - def __init__(self, xyz : list[float]): - self.x = xyz[0] - self.y = xyz[1] - self.z = xyz[2] - - def xyz(self): - xyz: list[float] = [self.x, self.y, self.z] - return xyz - - def translate(self, t_vec): # but whyy - self.x += t_vec.x - self.y += t_vec.y - self.z += t_vec.z - - return self - - def inv_translate(self, t_vec): # but whyy - self.x -= t_vec.x - self.y -= t_vec.y - self.z -= t_vec.z - - return self - - def snap(self): # sets x y z to the nearest integer - self.x = round(self.x) - self.y = round(self.y) - self.z = round(self.z) - - def apply_matrix(self, rot_matrix: numpy.array): - point = numpy.array([self.x, self.y, self.z]) - numpy.multiply(point, rot_matrix) - - def get_2d_point(self, fp_distance): - # converts a 3d point into a 2d point (replacement for the projection function) - distance = 0 - # calculate x pos - try: - x = ((fp_distance * self.x) / (self.y - distance)) - # calculate y pos - y = ((fp_distance * self.z) / (self.y - distance)) - except ZeroDivisionError: - x = 0 - y = 0 - return Point2D(x, y) - - def to_list(self): - return [self.x, self.y, self.z] - - -class Texture: - # this class *should* be finished - # 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 Face: - # The face class stores a face with N points, a colour, information about edges and faces with nothing else - # todo: - # make a cleaner way of extracting values - - points: list[Point3D] - colour: Colour - has_faces: bool - has_edges: bool - - def __init__(self, points: list[Point3D], colour: Colour, has_edges: bool, has_faces: bool): - self.hasEdges = has_edges # whether the main face has edges - if type(colour) is list: - raise ZeroDivisionError - self.colour = colour - self.points = points - self.hasFaces = has_faces - - def get_points(self): - # returns the positions of points (not rotated, not relative to the player) - return self.points - - def fix_points_behind_camera(self): - for point in self.points: - if point.y <= 0.5: - self.points.remove(point) - - -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) - - texture: Texture - # This class inherits from Face, and instead of a base colour it has a texture - - def set_texture(self, texture: Texture): - # sets the texture of the textured Face - self.texture = texture - - def get_sub_faces(self, face_point_dict): - # idk - self.texture = 0 - - -class Cube: - position: Point3D - colour: list[Colour] - texture: list[Texture] - - faces: list[Face] - # ordered top - north - east - south - west - bottom - cubePoints = [Point3D([.5, .5, .5]), Point3D([.5, .5, -.5]), Point3D([.5, -.5, .5]), Point3D([.5, -.5, -.5]), - Point3D([-.5, .5, .5]), Point3D([-.5, .5, -.5]), Point3D([-.5, -.5, .5]), Point3D([-.5, -.5, -.5])] - - # same as the dictionary, but no longer used. not sure why it still exists. - # 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]] - - # hardcoded dict for the relative positions of cube faces - cubeFaces: list[list[Point3D]] = [ - [cubePoints[3], cubePoints[5], cubePoints[7], cubePoints[1]], # top - [cubePoints[2], cubePoints[6], cubePoints[7], cubePoints[3]], # north - [cubePoints[4], cubePoints[5], cubePoints[7], cubePoints[6]], # east - [cubePoints[0], cubePoints[1], cubePoints[5], cubePoints[4]], # south - [cubePoints[0], cubePoints[2], cubePoints[3], cubePoints[1]], # west - [cubePoints[6], cubePoints[2], cubePoints[0], cubePoints[4]]] # bottom] - - def __init__(self, position: Point3D, texture : list[Colour], has_faces, has_edges): - self.faces = [] - self.position = Point3D([0, 0, 0]) - # todo - # implement texture - self.position = position - self.colours = texture - - self.has_faces = has_faces - self.has_edges = has_edges - - # initialises faces when the cube is placed. Faces are in global coordinates - for i in range(6): - new_points = 0 - self.faces.append(Face([point.translate(self.position) for point in copy.deepcopy(self.cubeFaces[i])], - self.colours[i], self.has_edges, self.has_faces)) - print(self.position.xyz()) - - def get_faces(self): - return copy.deepcopy(self.faces) - - # new functions def generate_cam_matrix(rx: int, ry: int, rz: int, player_pos): rotation_matrix = numpy.array( diff --git a/main.py b/main.py index fba8d44..7845eb4 100644 --- a/main.py +++ b/main.py @@ -2,13 +2,15 @@ # make todo list # import -import copy +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 @@ -211,8 +213,8 @@ # draw allFaces i = 0 for i, face in enumerate(allFaces): - _2dPoints = [point.get_2d_point(FPDis) for point in face.points] - [point.conv_coord(size, scale) for point in _2dPoints] # this seems really illegal + _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: diff --git a/model.py b/model.py new file mode 100644 index 0000000..0dd350d --- /dev/null +++ b/model.py @@ -0,0 +1,228 @@ +import colorsys +import copy +from typing import NamedTuple + +import imageio +import numpy + + +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 WindowSize(NamedTuple): + height: int + width: int + + +class Point2D(NamedTuple): + x: float + y: float + + def get(self): + return [self.x, self.y] + + def conv_coord(self, size, scale): + return Point2D((self.x * scale) + size[0]/2, (self.y * scale) + size[1]/2) + + +class Point3D(NamedTuple): + # todo: + # check if i have missed anything obvious + x: float + y: float + z: float + + def xyz(self): + xyz: list[float] = [self.x, self.y, self.z] + return xyz + + def translate(self, t_vec): # but whyy + self.x += t_vec.x + self.y += t_vec.y + self.z += t_vec.z + + return self + + def inv_translate(self, t_vec): # but whyy + self.x -= t_vec.x + self.y -= t_vec.y + self.z -= t_vec.z + + return self + + def snap(self): # sets x y z to the nearest integer + self.x = round(self.x) + self.y = round(self.y) + self.z = round(self.z) + + def apply_matrix(self, rot_matrix: numpy.array): + point = numpy.array([self.x, self.y, self.z]) + numpy.multiply(point, rot_matrix) + + def get_2d_point(self, fp_distance): + # converts a 3d point into a 2d point (replacement for the projection function) + distance = 0 + # calculate x pos + try: + x = ((fp_distance * self.x) / (self.y - distance)) + # calculate y pos + y = ((fp_distance * self.z) / (self.y - distance)) + except ZeroDivisionError: + x = 0 + y = 0 + return Point2D(x, y) + + def to_list(self): + return [self.x, self.y, self.z] + + +class Texture: + # this class *should* be finished + # 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 Face: + # The face class stores a face with N points, a colour, information about edges and faces with nothing else + # todo: + # make a cleaner way of extracting values + + points: list[Point3D] + colour: Colour + has_faces: bool + has_edges: bool + + def __init__(self, points: list[Point3D], colour: Colour, has_edges: bool, has_faces: bool): + self.hasEdges = has_edges # whether the main face has edges + if type(colour) is list: + raise ZeroDivisionError + self.colour = colour + self.points = points + self.hasFaces = has_faces + + def get_points(self): + # returns the positions of points (not rotated, not relative to the player) + return self.points + + def fix_points_behind_camera(self): + for point in self.points: + if point.y <= 0.5: + self.points.remove(point) + + +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) + + texture: Texture + # This class inherits from Face, and instead of a base colour it has a texture + + def set_texture(self, texture: Texture): + # sets the texture of the textured Face + self.texture = texture + + def get_sub_faces(self, face_point_dict): + # idk + self.texture = 0 + + +class Cube: + position: Point3D + colour: list[Colour] + texture: list[Texture] + + faces: list[Face] + # ordered top - north - east - south - west - bottom + cubePoints = [Point3D([.5, .5, .5]), Point3D([.5, .5, -.5]), Point3D([.5, -.5, .5]), Point3D([.5, -.5, -.5]), + Point3D([-.5, .5, .5]), Point3D([-.5, .5, -.5]), Point3D([-.5, -.5, .5]), Point3D([-.5, -.5, -.5])] + + # same as the dictionary, but no longer used. not sure why it still exists. + # 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]] + + # hardcoded dict for the relative positions of cube faces + cubeFaces: list[list[Point3D]] = [ + [cubePoints[3], cubePoints[5], cubePoints[7], cubePoints[1]], # top + [cubePoints[2], cubePoints[6], cubePoints[7], cubePoints[3]], # north + [cubePoints[4], cubePoints[5], cubePoints[7], cubePoints[6]], # east + [cubePoints[0], cubePoints[1], cubePoints[5], cubePoints[4]], # south + [cubePoints[0], cubePoints[2], cubePoints[3], cubePoints[1]], # west + [cubePoints[6], cubePoints[2], cubePoints[0], cubePoints[4]]] # bottom] + + def __init__(self, position: Point3D, texture : list[Colour], has_faces, has_edges): + self.faces = [] + self.position = Point3D([0, 0, 0]) + # todo + # implement texture + self.position = position + self.colours = texture + + self.has_faces = has_faces + self.has_edges = has_edges + + # initialises faces when the cube is placed. Faces are in global coordinates + for i in range(6): + new_points = 0 + self.faces.append(Face([point.translate(self.position) for point in copy.deepcopy(self.cubeFaces[i])], + self.colours[i], self.has_edges, self.has_faces)) + print(self.position.xyz()) + + def get_faces(self): + return copy.deepcopy(self.faces) \ No newline at end of file