# Pygame rotating cubes around axis

It is not sufficient to sort the faces of each cube separately by its depth. You’ve to sort the faces of all objects of the entire scene by its depth.

Create a list of tuples, which consists of the projected (transformed) points ofa face and the average depth (z value):

``````polygons = []
for cube in self._cubes:
transformed_vectors = cube.transform_vectors(self._angle)
avg_z = cube.calculate_average_z(transformed_vectors)
for z in avg_z:
face_index = z[0]
face = cube._faces[face_index]
pointlist = cube.create_polygon(face, transformed_vectors)
polygons.append((pointlist, z[1]))
``````

Draw the faces of all objects in (reverse) sorted order:

``````for poly in sorted(polygons, key=lambda x: x[1], reverse=True):
pygame.draw.polygon(self.screen, Color.SILVER.value,poly[0])
pygame.draw.polygon(self.screen, Color.BLACK.value, poly[0], 3)
``````

Minimal example: repl.it/@Rabbid76/PyGame-3D

``````import math
import pygame

def project(vector, w, h, fov, distance):
factor = math.atan(fov / 2 * math.pi / 180) / (distance + vector.z)
x = vector.x * factor * w + w / 2
y = -vector.y * factor * w + h / 2
return pygame.math.Vector3(x, y, vector.z)

def rotate_vertices(vertices, angle, axis):
return [v.rotate(angle, axis) for v in vertices]
def scale_vertices(vertices, s):
return [pygame.math.Vector3(v[0]*s[0], v[1]*s[1], v[2]*s[2]) for v in vertices]
def translate_vertices(vertices, t):
return [v + pygame.math.Vector3(t) for v in vertices]
def project_vertices(vertices, w, h, fov, distance):
return [project(v, w, h, fov, distance) for v in vertices]

class Mesh():

def __init__(self, vertices, faces):
self.__vertices = [pygame.math.Vector3(v) for v in vertices]
self.__faces = faces

def rotate(self, angle, axis):
self.__vertices = rotate_vertices(self.__vertices, angle, axis)
def scale(self, s):
self.__vertices = scale_vertices(self.__vertices, s)
def translate(self, t):
self.__vertices = translate_vertices(self.__vertices, t)

def calculate_average_z(self, vertices):
return [(i, sum([vertices[j].z for j in f]) / len(f)) for i, f in enumerate(self.__faces)]

def get_face(self, index):
return self.__faces[index]
def get_vertices(self):
return self.__vertices

def create_polygon(self, face, vertices):
return [(vertices[i].x, vertices[i].y) for i in [*face, face[0]]]

class Scene:
def __init__(self, mehses, fov, distance):
self.meshes = mehses
self.fov = fov
self.distance = distance
self.euler_angles = [0, 0, 0]

def transform_vertices(self, vertices, width, height):
transformed_vertices = vertices
axis_list = [(1, 0, 0), (0, 1, 0), (0, 0, 1)]
for angle, axis in reversed(list(zip(list(self.euler_angles), axis_list))):
transformed_vertices = rotate_vertices(transformed_vertices, angle, axis)
transformed_vertices = project_vertices(transformed_vertices, width, height, self.fov, self.distance)
return transformed_vertices

def draw(self, surface):

polygons = []
for mesh in self.meshes:
transformed_vertices = self.transform_vertices(mesh.get_vertices(), *surface.get_size())
avg_z = mesh.calculate_average_z(transformed_vertices)
for z in avg_z:
#for z in sorted(avg_z, key=lambda x: x[1], reverse=True):
pointlist = mesh.create_polygon(mesh.get_face(z[0]), transformed_vertices)
polygons.append((pointlist, z[1]))
#pygame.draw.polygon(surface, (128, 128, 192), pointlist)
#pygame.draw.polygon(surface, (0, 0, 0), pointlist, 3)

for poly in sorted(polygons, key=lambda x: x[1], reverse=True):
pygame.draw.polygon(surface, (128, 128, 192), poly[0])
pygame.draw.polygon(surface, (0, 0, 0), poly[0], 3)

vertices = [(-1,-1,1), (1,-1,1), (1,1,1), (-1,1,1), (-1,-1,-1), (1,-1,-1), (1,1,-1), (-1,1,-1)]
faces = [(0,1,2,3), (1,5,6,2), (5,4,7,6), (4,0,3,7), (3,2,6,7), (1,0,4,5)]

cube_origins = [(-1, -1, 0), (0, -1, 0), (1, -1, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (-1, 1, 0), (-1, 0, 0)]
meshes = []
for origin in cube_origins:
cube = Mesh(vertices, faces)
cube.scale((0.5, 0.5, 0.5))
cube.translate(origin)
meshes.append(cube)

scene = Scene(meshes, 90, 5)

pygame.init()
window = pygame.display.set_mode((400, 300))
clock = pygame.time.Clock()

run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False

window.fill((255, 255, 255))
scene.draw(window)
scene.euler_angles[1] += 1
pygame.display.flip()

pygame.quit()
``````