Implemented the first version of the 3D engine. Added support for loading 'obj' files as a mesh.
This commit is contained in:
@@ -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))
|
||||
@@ -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)
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
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):
|
||||
raise ValueError("Row must be a Vector4d object.", row0)
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user