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 import copy
from matrix import Matrix4x4, Matrix3x3 from matrix import Matrix4x4, Matrix3x3
from vectors import Vector4d, Vector3d, Vector2d from vectors import Vector4d, Vector3d, Vector2d
from engine import Mesh
ui = window.Window(600, 600) ui = window.Window(600, 600)
near_plane = 0.1 near_plane = 0.1
far_plane = 1000.0 far_plane = 1000.0
fov = 90.0 fov = 90.0
tris = [ tris = Mesh()
# 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 ]]
]
def main(): def main():
tris.load_obj_file('shuttle.obj')
ui.thetax = 1 ui.thetax = 1
ui.window.bind('<Left>', left) ui.window.bind('<Left>', left)
ui.window.bind('<Right>', right) ui.window.bind('<Right>', right)
ui.window.bind('<ButtonPress-1>', mouse_start) 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.canvas.old_coords = None
ui.window.after(0, draw_scene) ui.window.after(0, draw_scene)
ui.show() ui.show()
@@ -97,30 +76,25 @@ def draw_scene():
rotation_matrix_x = Matrix4x4(row0, row1, row2, row3) rotation_matrix_x = Matrix4x4(row0, row1, row2, row3)
for t in tris: for tri in tris.triangles:
row0 = Vector3d(t[0][0], t[0][1], t[0][2]) # Rotate on the z-axis
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)
row0 = rotation_matrix_z.multiply_3d(tri.row0) row0 = rotation_matrix_z.multiply_3d(tri.row0)
row1 = rotation_matrix_z.multiply_3d(tri.row1) row1 = rotation_matrix_z.multiply_3d(tri.row1)
row2 = rotation_matrix_z.multiply_3d(tri.row2) row2 = rotation_matrix_z.multiply_3d(tri.row2)
rotated_z = Matrix3x3(row0, row1, row2) rotated_z = Matrix3x3(row0, row1, row2)
# Rotate on the x-axis
row0 = rotation_matrix_x.multiply_3d(rotated_z.row0) row0 = rotation_matrix_x.multiply_3d(rotated_z.row0)
row1 = rotation_matrix_x.multiply_3d(rotated_z.row1) row1 = rotation_matrix_x.multiply_3d(rotated_z.row1)
row2 = rotation_matrix_x.multiply_3d(rotated_z.row2) row2 = rotation_matrix_x.multiply_3d(rotated_z.row2)
rotated_x = Matrix3x3(row0, row1, row2) rotated_x = Matrix3x3(row0, row1, row2)
# The offset into the screen
translated = copy.copy(rotated_x) translated = copy.copy(rotated_x)
translated.row0.z += 3.0 translated.row0.z += 8.0
translated.row1.z += 3.0 translated.row1.z += 8.0
translated.row2.z += 3.0 translated.row2.z += 8.0
# Scale into view
row0 = projection_matrix.multiply_3d(translated.row0) row0 = projection_matrix.multiply_3d(translated.row0)
row1 = projection_matrix.multiply_3d(translated.row1) row1 = projection_matrix.multiply_3d(translated.row1)
row2 = projection_matrix.multiply_3d(translated.row2) row2 = projection_matrix.multiply_3d(translated.row2)
-9
View File
@@ -1,11 +1,6 @@
import vectors import vectors
class Matrix4x4: class Matrix4x4:
def __init__(self):
self.row0 = None
self.row1 = None
self.row2 = None
self.row3 = None
def __init__(self, row0, row1, row2, row3): def __init__(self, row0, row1, row2, row3):
if not isinstance(row0, vectors.Vector4d): if not isinstance(row0, vectors.Vector4d):
@@ -36,10 +31,6 @@ class Matrix4x4:
return vectors.Vector3d(x, y, z) return vectors.Vector3d(x, y, z)
class Matrix3x3: class Matrix3x3:
def __init__(self):
self.row0 = None
self.row1 = None
self.row2 = None
def __init__(self, row0, row1, row2): def __init__(self, row0, row1, row2):
self.row0 = row0 self.row0 = row0
+15
View File
@@ -11,6 +11,21 @@ class Vector3d:
self.y = y self.y = y
self.z = z 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: class Vector2d:
def __init__(self, x, y): def __init__(self, x, y):
self.x = x self.x = x