3d_box_v1
This is an old revision of the document!
3d Box
This is the first demo which will use real 3d math to do projection into 2d. The code will be presented as a monolithic file but it is intended that it be split into many files such as putting Object3d into it's own file, and then moving functions like rotate_x into there (maybe using staticmethod) or into their own library class. Object data could also stand to be moved out, maybe into an objects.py sort of class, or into files like cube.obj (i.e. cube.txt) which could be read in at runtime.
The Code
import pygame
import sys
import math
# Initialize Pygame
pygame.init()
# Constants
WIDTH, HEIGHT = 800, 600
FOV = 256 # Field of view
CAMERA_Z = 4 # Camera distance from the object
SQUARE_SIZE = 1 # Half the size of the square for vertex calculations
rotation_angles = [0, 0, 0] # Rotation angles for x, y, z
# Set up the display
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("3D Cube with Polygons")
# Define colors for each face
face_colors = [
(255, 0, 0), # Red for Back face
(0, 255, 0), # Green for Front face
(0, 0, 255), # Blue for Left face
(255, 255, 0), # Yellow for Right face
(255, 0, 255), # Magenta for Top face
(0, 255, 255) # Cyan for Bottom face
]
# Object3D class to store vertices and position
class Object3D:
def __init__(self, vertices, position=(0, 0, 0)):
self.vertices = vertices
self.position = list(position)
self.rotation = [0, 0, 0] # Rotation angles for x, y, z
def rotate(self, angle_x, angle_y, angle_z):
self.rotation[0] += angle_x
self.rotation[1] += angle_y
self.rotation[2] += angle_z
def move(self, dx, dy, dz):
self.position[0] += dx
self.position[1] += dy
self.position[2] += dz
def get_transformed_vertices(self):
transformed_faces = []
for face in self.vertices:
transformed_face = [
rotate_x(rotate_y(rotate_z(vertex, self.rotation[2]), self.rotation[1]), self.rotation[0])
for vertex in face
]
transformed_faces.append(transformed_face)
return transformed_faces
# Define the vertices of a 3D cube
cube_vertices = [
[ # Back face
(-SQUARE_SIZE, -SQUARE_SIZE, -SQUARE_SIZE),
(SQUARE_SIZE, -SQUARE_SIZE, -SQUARE_SIZE),
(SQUARE_SIZE, SQUARE_SIZE, -SQUARE_SIZE),
(-SQUARE_SIZE, SQUARE_SIZE, -SQUARE_SIZE)
],
[ # Front face
(-SQUARE_SIZE, -SQUARE_SIZE, SQUARE_SIZE),
(SQUARE_SIZE, -SQUARE_SIZE, SQUARE_SIZE),
(SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE),
(-SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE)
],
[ # Left face
(-SQUARE_SIZE, -SQUARE_SIZE, -SQUARE_SIZE),
(-SQUARE_SIZE, -SQUARE_SIZE, SQUARE_SIZE),
(-SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE),
(-SQUARE_SIZE, SQUARE_SIZE, -SQUARE_SIZE)
],
[ # Right face
(SQUARE_SIZE, -SQUARE_SIZE, -SQUARE_SIZE),
(SQUARE_SIZE, -SQUARE_SIZE, SQUARE_SIZE),
(SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE),
(SQUARE_SIZE, SQUARE_SIZE, -SQUARE_SIZE)
],
[ # Top face
(-SQUARE_SIZE, SQUARE_SIZE, -SQUARE_SIZE),
(SQUARE_SIZE, SQUARE_SIZE, -SQUARE_SIZE),
(SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE),
(-SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE)
],
[ # Bottom face
(-SQUARE_SIZE, -SQUARE_SIZE, -SQUARE_SIZE),
(SQUARE_SIZE, -SQUARE_SIZE, -SQUARE_SIZE),
(SQUARE_SIZE, -SQUARE_SIZE, SQUARE_SIZE),
(-SQUARE_SIZE, -SQUARE_SIZE, SQUARE_SIZE)
]
]
# Function to project 3D points to 2D
def project(point):
x, y, z = point
scale = FOV / (CAMERA_Z + z)
x_proj = int(x * scale + WIDTH // 2)
y_proj = int(-y * scale + HEIGHT // 2)
return (x_proj, y_proj)
# Functions for rotating points
def rotate_x(point, angle):
rad = math.radians(angle)
cos_angle = math.cos(rad)
sin_angle = math.sin(rad)
x, y, z = point
y_rotated = y * cos_angle - z * sin_angle
z_rotated = y * sin_angle + z * cos_angle
return (x, y_rotated, z_rotated)
def rotate_y(point, angle):
rad = math.radians(angle)
cos_angle = math.cos(rad)
sin_angle = math.sin(rad)
x, y, z = point
x_rotated = z * sin_angle + x * cos_angle
z_rotated = z * cos_angle - x * sin_angle
return (x_rotated, y, z_rotated)
def rotate_z(point, angle):
rad = math.radians(angle)
cos_angle = math.cos(rad)
sin_angle = math.sin(rad)
x, y, z = point
x_rotated = x * cos_angle - y * sin_angle
y_rotated = x * sin_angle + y * cos_angle
return (x_rotated, y_rotated, z)
# Function to draw the cube using faces
def draw_obj(obj):
face_data = []
transformed_faces = obj.get_transformed_vertices()
for index, face in enumerate(transformed_faces):
projected_vertices = [project((x + obj.position[0], y + obj.position[1], z + obj.position[2])) for (x, y, z) in
face]
# Calculate average Z value for sorting
avg_z = sum(vertex[2] for vertex in face) / len(face)
face_data.append((avg_z, projected_vertices, face_colors[index]))
# Sort faces by average Z value (back to front)
face_data.sort(key=lambda x: x[0], reverse=True)
# Draw each face in sorted order
for _, projected_vertices, color in face_data:
pygame.draw.polygon(screen, color, projected_vertices, False)
# Main loop
def main():
clock = pygame.time.Clock()
# Create cube object
cube = Object3D(cube_vertices)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
keys = pygame.key.get_pressed()
# Movement controls
if keys[pygame.K_w]: # Move forward along Z
cube.move(0, 0, 0.1)
if keys[pygame.K_s]: # Move backward along Z
cube.move(0, 0, -0.1)
if keys[pygame.K_a]: # Move left along X
cube.move(-0.1, 0, 0)
if keys[pygame.K_d]: # Move right along X
cube.move(0.1, 0, 0)
if keys[pygame.K_UP]: # Move up along Y
cube.move(0, 0.1, 0)
if keys[pygame.K_DOWN]: # Move down along Y
cube.move(0, -0.1, 0)
# Rotation controls
if keys[pygame.K_1]: # Rotate around X-axis
cube.rotate(1, 0, 0)
if keys[pygame.K_2]: # Rotate around X-axis
cube.rotate(-1, 0, 0)
if keys[pygame.K_3]: # Rotate around Y-axis
cube.rotate(0, 1, 0)
if keys[pygame.K_4]: # Rotate around Y-axis
cube.rotate(0, -1, 0)
if keys[pygame.K_5]: # Rotate around Z-axis
cube.rotate(0, 0, 1)
if keys[pygame.K_6]: # Rotate around Z-axis
cube.rotate(0, 0, -1)
# Fill the screen with black
screen.fill((0, 0, 0))
# Draw the cube
draw_obj(cube)
# Draw the bounding box (optional)
pygame.draw.rect(screen, (255, 0, 0), (WIDTH // 2 - 400, HEIGHT // 2 - 300, 800, 600), 1)
# Update the display
pygame.display.flip()
clock.tick(60)
if __name__ == "__main__":
main()
3d_box_v1.1726793588.txt.gz · Last modified: 2024/09/20 00:53 by appledog
