Implemented the first version of the 3D engine. Added support for loading 'obj' files as a mesh.

This commit is contained in:
2020-07-14 16:53:06 -05:00
parent b318076f05
commit 05d2beb8ac
4 changed files with 75 additions and 49 deletions
+46
View File
@@ -0,0 +1,46 @@
from vectors import Vector2d, Vector3d, Vector4d
from matrix import Matrix3x3
class Engine:
def __init__(self, width, height):
self._width = width
self._height = height
@property
def width(self):
return self._width
@width.setter
def width(self, width):
raise ValueError("Not allowed")
class Mesh:
def __init__(self):
self.triangles = []
self.vertices = []
def load_obj_file(self, path):
lines = []
with open(path, 'r') as reader:
lines = reader.readlines()
for line in lines:
if line[0] == 'v':
tmp = line.split(' ')
x = float(tmp[1])
y = float(tmp[2])
z = float(tmp[3])
self.vertices.append(Vector3d(x, y, z))
if line[0] == 'f':
tmp = line.split(' ')
# Subtract one because the obj file isn't zero indexed.
row0 = self.vertices[int(tmp[1]) - 1]
row1 = self.vertices[int(tmp[2]) - 1]
row2 = self.vertices[int(tmp[3]) - 1]
tri = Matrix3x3(row0, row1, row2)
self.triangles.append(tri)#Vector3d(x - 1, y - 1, z - 1))
+13 -39
View File
@@ -3,44 +3,23 @@ import math
import copy
from matrix import Matrix4x4, Matrix3x3
from vectors import Vector4d, Vector3d, Vector2d
from engine import Mesh
ui = window.Window(600, 600)
near_plane = 0.1
far_plane = 1000.0
fov = 90.0
tris = [
# SOUTH
[ [0.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0, 0.0] ],
[ [0.0, 0.0, 0.0], [1.0, 1.0, 0.0], [1.0, 0.0, 0.0 ]],
# EAST
[ [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [1.0, 1.0, 1.0 ]],
[ [1.0, 0.0, 0.0], [1.0, 1.0, 1.0], [1.0, 0.0, 1.0 ]],
# NORTH
[ [1.0, 0.0, 1.0], [1.0, 1.0, 1.0], [0.0, 1.0, 1.0 ]],
[ [1.0, 0.0, 1.0], [0.0, 1.0, 1.0], [0.0, 0.0, 1.0 ]],
# WEST
[[ 0.0, 0.0, 1.0], [0.0, 1.0, 1.0], [0.0, 1.0, 0.0 ]],
[ [0.0, 0.0, 1.0], [0.0, 1.0, 0.0], [0.0, 0.0, 0.0 ]],
# TOP
[[ 0.0, 1.0, 0.0], [0.0, 1.0, 1.0], [1.0, 1.0, 1.0 ]],
[[ 0.0, 1.0, 0.0], [1.0, 1.0, 1.0], [1.0, 1.0, 0.0 ]],
# BOTTOM
[[ 1.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 0.0 ]],
[[ 1.0, 0.0, 1.0], [0.0, 0.0, 0.0], [1.0, 0.0, 0.0 ]]
]
tris = Mesh()
def main():
tris.load_obj_file('shuttle.obj')
ui.thetax = 1
ui.window.bind('<Left>', left)
ui.window.bind('<Right>', right)
ui.window.bind('<ButtonPress-1>', mouse_start)
ui.window.bind('<B1-Motion>', mouse_end)
ui.canvas.bind('<B1-Motion>', mouse_end)
ui.canvas.old_coords = None
ui.window.after(0, draw_scene)
ui.show()
@@ -97,30 +76,25 @@ def draw_scene():
rotation_matrix_x = Matrix4x4(row0, row1, row2, row3)
for t in tris:
row0 = Vector3d(t[0][0], t[0][1], t[0][2])
row1 = Vector3d(t[1][0], t[1][1], t[1][2])
row2 = Vector3d(t[2][0], t[2][1], t[2][2])
tri = Matrix3x3(row0, row1, row2)
for tri in tris.triangles:
# Rotate on the z-axis
row0 = rotation_matrix_z.multiply_3d(tri.row0)
row1 = rotation_matrix_z.multiply_3d(tri.row1)
row2 = rotation_matrix_z.multiply_3d(tri.row2)
rotated_z = Matrix3x3(row0, row1, row2)
# Rotate on the x-axis
row0 = rotation_matrix_x.multiply_3d(rotated_z.row0)
row1 = rotation_matrix_x.multiply_3d(rotated_z.row1)
row2 = rotation_matrix_x.multiply_3d(rotated_z.row2)
rotated_x = Matrix3x3(row0, row1, row2)
# The offset into the screen
translated = copy.copy(rotated_x)
translated.row0.z += 3.0
translated.row1.z += 3.0
translated.row2.z += 3.0
translated.row0.z += 8.0
translated.row1.z += 8.0
translated.row2.z += 8.0
# Scale into view
row0 = projection_matrix.multiply_3d(translated.row0)
row1 = projection_matrix.multiply_3d(translated.row1)
row2 = projection_matrix.multiply_3d(translated.row2)
-9
View File
@@ -1,11 +1,6 @@
import vectors
class Matrix4x4:
def __init__(self):
self.row0 = None
self.row1 = None
self.row2 = None
self.row3 = None
def __init__(self, row0, row1, row2, row3):
if not isinstance(row0, vectors.Vector4d):
@@ -36,10 +31,6 @@ class Matrix4x4:
return vectors.Vector3d(x, y, z)
class Matrix3x3:
def __init__(self):
self.row0 = None
self.row1 = None
self.row2 = None
def __init__(self, row0, row1, row2):
self.row0 = row0
+15
View File
@@ -11,6 +11,21 @@ class Vector3d:
self.y = y
self.z = z
def cross_product(self, vector3d):
# Returns a line that is perpendicular to the parameter
# and self.
# Returns the 'normal'.
x = self.y * vector3d.z - self.z * vector3d.y
y = self.z * vector3d.x - self.x * vector3d.z
z = self.x * vectored.y - self.y * vector3d.x
return Vector3d(x, y, z)
def dot_product(self, vector3d):
# Returns a scalar that defines how similar two
# vectors are to one another.
return self.x * vector3d.x + self.y * vector3d.y + self.z * vector3d.z
class Vector2d:
def __init__(self, x, y):
self.x = x