Changed matrix object names to be more natural. Fixed a bug in the matrix projection math.
This commit is contained in:
@@ -38,8 +38,8 @@ class Mesh:
|
|||||||
tmp = line.split(' ')
|
tmp = line.split(' ')
|
||||||
|
|
||||||
# Subtract one because the obj file isn't zero indexed.
|
# Subtract one because the obj file isn't zero indexed.
|
||||||
row0 = self.vertices[int(tmp[1]) - 1]
|
row1 = self.vertices[int(tmp[1]) - 1]
|
||||||
row1 = self.vertices[int(tmp[2]) - 1]
|
row2 = self.vertices[int(tmp[2]) - 1]
|
||||||
row2 = self.vertices[int(tmp[3]) - 1]
|
row3 = self.vertices[int(tmp[3]) - 1]
|
||||||
|
|
||||||
self.triangles.append(Triangle(Matrix3x3(row0, row1, row2)))
|
self.triangles.append(Triangle(Matrix3x3(row1, row2, row3)))
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ 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 =[
|
||||||
Matrix3x3(Vector3d(0.0,0.0,0.0),Vector3d(0.0,1.0,0.0),Vector3d(1.0,1.0,0.0)),
|
Matrix3x3(Vector3d(0.0,0.0,0.0),Vector3d(0.0,1.0,0.0),Vector3d(1.0,1.0,0.0)),
|
||||||
Matrix3x3(Vector3d(0.0,0.0,0.0),Vector3d(1.0,1.0,0.0),Vector3d(1.0,0.0,0.0)),
|
Matrix3x3(Vector3d(0.0,0.0,0.0),Vector3d(1.0,1.0,0.0),Vector3d(1.0,0.0,0.0)),
|
||||||
@@ -25,14 +25,16 @@ Matrix3x3(Vector3d(0.0,1.0,0.0),Vector3d(1.0,1.0,1.0),Vector3d(1.0,1.0,0.0)),
|
|||||||
Matrix3x3(Vector3d(1.0,0.0,1.0),Vector3d(0.0,0.0,1.0),Vector3d(0.0,0.0,0.0)),
|
Matrix3x3(Vector3d(1.0,0.0,1.0),Vector3d(0.0,0.0,1.0),Vector3d(0.0,0.0,0.0)),
|
||||||
Matrix3x3(Vector3d(1.0,0.0,1.0),Vector3d(0.0,0.0,0.0),Vector3d(1.0,0.0,0.0))
|
Matrix3x3(Vector3d(1.0,0.0,1.0),Vector3d(0.0,0.0,0.0),Vector3d(1.0,0.0,0.0))
|
||||||
]
|
]
|
||||||
|
#'''
|
||||||
#tris = Mesh()
|
#tris = Mesh()
|
||||||
def main():
|
def main():
|
||||||
#tris.load_obj_file('shuttle.obj')
|
#tris.load_obj_file('teddy.obj')
|
||||||
|
|
||||||
ui.thetax = 1
|
ui.thetax = 1
|
||||||
|
ui.thetay = 1
|
||||||
ui.window.bind('<Left>', left)
|
ui.window.bind('<Left>', left)
|
||||||
ui.window.bind('<Right>', right)
|
ui.window.bind('<Right>', right)
|
||||||
|
ui.window.bind('<Up>', up)
|
||||||
|
ui.window.bind('<Down>', down)
|
||||||
ui.window.bind('<ButtonPress-1>', mouse_start)
|
ui.window.bind('<ButtonPress-1>', mouse_start)
|
||||||
ui.canvas.bind('<B1-Motion>', mouse_end)
|
ui.canvas.bind('<B1-Motion>', mouse_end)
|
||||||
ui.canvas.old_coords = None
|
ui.canvas.old_coords = None
|
||||||
@@ -40,27 +42,35 @@ def main():
|
|||||||
ui.show()
|
ui.show()
|
||||||
|
|
||||||
def left(event):
|
def left(event):
|
||||||
ui.theta -= .01
|
ui.thetax -= .1
|
||||||
|
draw_scene()
|
||||||
|
|
||||||
def right(event):
|
def right(event):
|
||||||
ui.theta += .01
|
ui.thetax += .1
|
||||||
|
draw_scene()
|
||||||
|
|
||||||
|
def up(event):
|
||||||
|
ui.thetay += .1
|
||||||
|
draw_scene()
|
||||||
|
|
||||||
|
def down(event):
|
||||||
|
ui.thetay -= .1
|
||||||
|
draw_scene()
|
||||||
|
|
||||||
def mouse_start(event):
|
def mouse_start(event):
|
||||||
ui.canvas.old_coords = event.x, event.y
|
ui.canvas.old_coords = event.x, event.y
|
||||||
|
|
||||||
def mouse_end(event):
|
def mouse_end(event):
|
||||||
x, y = ui.canvas.old_coords[0] - event.x, ui.canvas.old_coords[1] - event.y
|
x, y = event.x - ui.width / 2, event.y - ui.height / 2#ui.canvas.old_coords[0] - event.x, ui.canvas.old_coords[1] - event.y
|
||||||
if x < 0:
|
if x < 0:
|
||||||
ui.theta += .01
|
ui.thetax -= .01
|
||||||
else:
|
else:
|
||||||
ui.theta -= .01
|
ui.thetax += .01
|
||||||
|
|
||||||
print(ui.theta)
|
|
||||||
|
|
||||||
if y < 0:
|
if y < 0:
|
||||||
ui.thetax += .01
|
ui.thetay -= .01
|
||||||
else:
|
else:
|
||||||
ui.thetax -= .01
|
ui.thetay += .01
|
||||||
|
|
||||||
draw_scene()
|
draw_scene()
|
||||||
#ui.theta += x / ui.width
|
#ui.theta += x / ui.width
|
||||||
@@ -69,109 +79,97 @@ def draw_scene():
|
|||||||
ui.canvas.delete("all")
|
ui.canvas.delete("all")
|
||||||
#ui.theta += .01 # scaling factor
|
#ui.theta += .01 # scaling factor
|
||||||
aspect_ratio = ui.height / ui.width
|
aspect_ratio = ui.height / ui.width
|
||||||
fov_rad = ui.theta / math.tan(fov * 0.5 / 180.0 * math.pi)
|
|
||||||
|
|
||||||
row0 = Vector4d(aspect_ratio * fov_rad, 0, 0, 0)
|
projection_matrix = Matrix4x4.get_projection_matrix(fov, aspect_ratio, near_plane, far_plane)
|
||||||
row1 = Vector4d(0, fov_rad, 0, 0)
|
|
||||||
row2 = Vector4d(0, 0, far_plane / (far_plane - near_plane), 1.0)
|
|
||||||
row3 = Vector4d(0, 0, -far_plane * near_plane / (far_plane - near_plane), 0)
|
|
||||||
|
|
||||||
projection_matrix = Matrix4x4(row0, row1, row2, row3)
|
rotation_matrix_y = Matrix4x4.get_y_rotation_matrix(ui.thetax)
|
||||||
|
|
||||||
row0 = Vector4d(math.cos(ui.theta), math.sin(ui.theta), 0, 0)
|
rotation_matrix_x = Matrix4x4.get_x_rotation_matrix(ui.thetay)
|
||||||
row1 = Vector4d(-math.sin(ui.theta), math.cos(ui.theta), 0, 0)
|
|
||||||
row2 = Vector4d(0, 0, 1, 0)
|
|
||||||
row3 = Vector4d(0, 0, 0, 1)
|
|
||||||
|
|
||||||
rotation_matrix_z = Matrix4x4(row0, row1, row2, row3)
|
|
||||||
|
|
||||||
row0 = Vector4d(1, 0, 0, 0)
|
|
||||||
row1 = Vector4d(0, math.cos(ui.thetax * 0.5), math.sin(ui.thetax * 0.5), 0)
|
|
||||||
row2 = Vector4d(0, -math.sin(ui.thetax * 0.5), math.cos(ui.thetax * 0.5), 0)
|
|
||||||
row3 = Vector4d(0, 0, 0, 1)
|
|
||||||
|
|
||||||
rotation_matrix_x = Matrix4x4(row0, row1, row2, row3)
|
|
||||||
|
|
||||||
triangles = []
|
triangles = []
|
||||||
|
|
||||||
for tri in tris:
|
for tri in tris:
|
||||||
# Rotate on the z-axis
|
# Rotate on the z-axis
|
||||||
row0 = rotation_matrix_z.multiply_3d(tri.row0)
|
row1 = rotation_matrix_y.multiply_3d(tri.row1)
|
||||||
row1 = rotation_matrix_z.multiply_3d(tri.row1)
|
row2 = rotation_matrix_y.multiply_3d(tri.row2)
|
||||||
row2 = rotation_matrix_z.multiply_3d(tri.row2)
|
row3 = rotation_matrix_y.multiply_3d(tri.row3)
|
||||||
|
|
||||||
rotated_z = Matrix3x3(row0, row1, row2)
|
rotated_y = Matrix3x3(row1, row2, row3)
|
||||||
# Rotate on the x-axis
|
# Rotate on the x-axis
|
||||||
row0 = rotation_matrix_x.multiply_3d(rotated_z.row0)
|
row1 = rotation_matrix_x.multiply_3d(rotated_y.row1)
|
||||||
row1 = rotation_matrix_x.multiply_3d(rotated_z.row1)
|
row2 = rotation_matrix_x.multiply_3d(rotated_y.row2)
|
||||||
row2 = rotation_matrix_x.multiply_3d(rotated_z.row2)
|
row3 = rotation_matrix_x.multiply_3d(rotated_y.row3)
|
||||||
|
|
||||||
rotated_x = Matrix3x3(row0, row1, row2)
|
rotated_x = Matrix3x3(row1, row2, row3)
|
||||||
# The offset into the screen
|
# The offset into the screen
|
||||||
translated = copy.copy(rotated_x)
|
translated = copy.copy(rotated_x)
|
||||||
translated.row0.z += 3.0
|
|
||||||
translated.row1.z += 3.0
|
translated.row1.z += 3.0
|
||||||
translated.row2.z += 3.0
|
translated.row2.z += 3.0
|
||||||
|
translated.row3.z += 3.0
|
||||||
|
|
||||||
# Use cross-product to get the surface normal (a Vector3d)
|
# Use cross-product to get the surface normal (a Vector3d)
|
||||||
x = translated.row1.x - translated.row0.x
|
x = translated.row2.x - translated.row1.x
|
||||||
y = translated.row1.y - translated.row0.y
|
y = translated.row2.y - translated.row1.y
|
||||||
z = translated.row1.z - translated.row0.z
|
z = translated.row2.z - translated.row1.z
|
||||||
line1 = Vector3d(x, y, z)
|
line1 = Vector3d(x, y, z)
|
||||||
|
|
||||||
x = translated.row2.x - translated.row0.x
|
x = translated.row3.x - translated.row1.x
|
||||||
y = translated.row2.y - translated.row0.y
|
y = translated.row3.y - translated.row1.y
|
||||||
z = translated.row2.z - translated.row0.z
|
z = translated.row3.z - translated.row1.z
|
||||||
line2 = Vector3d(x, y, z)
|
line2 = Vector3d(x, y, z)
|
||||||
|
|
||||||
normal = line1.cross_product(line2)
|
normal = line1.cross_product(line2)
|
||||||
|
|
||||||
# Now we normalize the normal
|
# Now we normalize the normal
|
||||||
w = math.sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z)
|
length = math.sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z)
|
||||||
normal.x /= w
|
normal.x /= length
|
||||||
normal.y /= w
|
normal.y /= length
|
||||||
normal.z /= w
|
normal.z /= length
|
||||||
|
|
||||||
# Do we display this triangle?
|
# Do we display this triangle?
|
||||||
if normal.dot_product(translated.row0) < 0.0:
|
if normal.dot_product(translated.row1) < 0.0:
|
||||||
# Lighting
|
# Lighting
|
||||||
light = Vector3d(0, 0, -1) # Shining at the player.
|
light = Vector3d(0, 0, -1) # Shining at the player.
|
||||||
|
|
||||||
w = math.sqrt(light.x * light.x + light.y * light.y + light.z * light.z)
|
length = math.sqrt(light.x * light.x + light.y * light.y + light.z * light.z)
|
||||||
light.x /= w
|
light.x /= length
|
||||||
light.y /= w
|
light.y /= length
|
||||||
light.z /= w
|
light.z /= length
|
||||||
|
|
||||||
dot_product = normal.dot_product(light)
|
dot_product = normal.dot_product(light)
|
||||||
r = abs(int(255 * dot_product))
|
r = abs(int(255 * dot_product))
|
||||||
|
|
||||||
# Scale into view
|
# Scale into view
|
||||||
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)
|
||||||
|
row3 = projection_matrix.multiply_3d(translated.row3)
|
||||||
|
|
||||||
projected = Matrix3x3(row0, row1, row2)
|
projected = Matrix3x3(row1, row2, row3)
|
||||||
|
|
||||||
projected.row0.x += 1
|
|
||||||
projected.row0.y += 1
|
|
||||||
projected.row1.x += 1
|
projected.row1.x += 1
|
||||||
projected.row1.y += 1
|
projected.row1.y += 1
|
||||||
projected.row2.x += 1
|
projected.row2.x += 1
|
||||||
projected.row2.y += 1
|
projected.row2.y += 1
|
||||||
projected.row0.x *= .5 * ui.width
|
projected.row3.x += 1
|
||||||
projected.row0.y *= .5 * ui.height
|
projected.row3.y += 1
|
||||||
projected.row1.x *= .5 * ui.width
|
projected.row1.x *= .5 * ui.width
|
||||||
projected.row1.y *= .5 * ui.height
|
projected.row1.y *= .5 * ui.height
|
||||||
projected.row2.x *= .5 * ui.width
|
projected.row2.x *= .5 * ui.width
|
||||||
projected.row2.y *= .5 * ui.height
|
projected.row2.y *= .5 * ui.height
|
||||||
|
projected.row3.x *= .5 * ui.width
|
||||||
|
projected.row3.y *= .5 * ui.height
|
||||||
|
|
||||||
t = Triangle(projected)
|
t = Triangle(projected)
|
||||||
t.color = r
|
t.color = r
|
||||||
|
t.average = (t.coords.row1.z + t.coords.row2.z + t.coords.row3.z) / 3
|
||||||
triangles.append(t)
|
triangles.append(t)
|
||||||
|
|
||||||
|
# Sort the triangles so they are drawn in order.
|
||||||
|
triangles.sort(key=lambda r: r.average, reverse = True)
|
||||||
|
|
||||||
for projected in triangles:
|
for projected in triangles:
|
||||||
r = projected.color
|
r = projected.color
|
||||||
coords = [projected.coords.row0.x, projected.coords.row0.y, projected.coords.row1.x, projected.coords.row1.y, projected.coords.row2.x, projected.coords.row2.y]
|
coords = [projected.coords.row1.x, projected.coords.row1.y, projected.coords.row2.x, projected.coords.row2.y, projected.coords.row3.x, projected.coords.row3.y]
|
||||||
ui.canvas.create_polygon(coords, fill=from_rgb(r, r, r), outline="black")
|
ui.canvas.create_polygon(coords, fill=from_rgb(r, r, r), outline="black")
|
||||||
|
|
||||||
ui.canvas.update()
|
ui.canvas.update()
|
||||||
|
|||||||
@@ -1,38 +1,82 @@
|
|||||||
import vectors
|
import math
|
||||||
|
from vectors import Vector3d, Vector4d
|
||||||
|
|
||||||
class Matrix4x4:
|
class Matrix4x4:
|
||||||
|
|
||||||
def __init__(self, row0, row1, row2, row3):
|
def __init__(self, row1, row2, row3, row4):
|
||||||
if not isinstance(row0, vectors.Vector4d):
|
if not isinstance(row1, Vector4d):
|
||||||
raise ValueError("Row must be a Vector4d object.", row0)
|
|
||||||
if not isinstance(row1, vectors.Vector4d):
|
|
||||||
raise ValueError("Row must be a Vector4d object.", row1)
|
raise ValueError("Row must be a Vector4d object.", row1)
|
||||||
if not isinstance(row2, vectors.Vector4d):
|
if not isinstance(row2, Vector4d):
|
||||||
raise ValueError("Row must be a Vector4d object.", row2)
|
raise ValueError("Row must be a Vector4d object.", row2)
|
||||||
if not isinstance(row3, vectors.Vector4d):
|
if not isinstance(row3, Vector4d):
|
||||||
raise ValueError("Row must be a Vector4d object.", row3)
|
raise ValueError("Row must be a Vector4d object.", row3)
|
||||||
|
if not isinstance(row4, Vector4d):
|
||||||
|
raise ValueError("Row must be a Vector4d object.", row4)
|
||||||
|
|
||||||
self.row0 = row0
|
|
||||||
self.row1 = row1
|
self.row1 = row1
|
||||||
self.row2 = row2
|
self.row2 = row2
|
||||||
self.row3 = row3
|
self.row3 = row3
|
||||||
|
self.row4 = row4
|
||||||
|
|
||||||
def multiply_3d(self, vector3d):
|
def multiply_3d(self, vector3d):
|
||||||
x = vector3d.x * self.row0.x + vector3d.y * self.row1.x + vector3d.z * self.row2.x + self.row3.x
|
x = vector3d.x * self.row1.x + vector3d.y * self.row2.x + vector3d.z * self.row3.x + self.row4.x
|
||||||
y = vector3d.x * self.row0.y + vector3d.y * self.row1.y + vector3d.z * self.row2.y + self.row3.y
|
y = vector3d.x * self.row1.y + vector3d.y * self.row2.y + vector3d.z * self.row3.y + self.row4.y
|
||||||
z = vector3d.x * self.row0.z + vector3d.y * self.row1.z + vector3d.z * self.row2.z + self.row3.z
|
z = vector3d.x * self.row1.z + vector3d.y * self.row2.z + vector3d.z * self.row3.z + self.row4.z
|
||||||
w = vector3d.x * self.row0.w + vector3d.y * self.row1.w + vector3d.z * self.row2.w + self.row3.w
|
w = vector3d.x * self.row1.w + vector3d.y * self.row2.w + vector3d.z * self.row3.w + self.row4.w
|
||||||
|
|
||||||
if w != 0:
|
if w != 0:
|
||||||
x /= w
|
x /= w
|
||||||
y /= w
|
y /= w
|
||||||
z /= w
|
z /= w
|
||||||
|
|
||||||
return vectors.Vector3d(x, y, z)
|
return Vector3d(x, y, z)
|
||||||
|
|
||||||
|
|
||||||
|
def get_projection_matrix(fov_degrees, aspect_ratio, near_plane, far_plane):
|
||||||
|
fov_rad = 1 / math.tan(fov_degrees * 0.5 / 180.0 * math.pi)
|
||||||
|
|
||||||
|
row1 = Vector4d(aspect_ratio * fov_rad, 0, 0, 0)
|
||||||
|
row2 = Vector4d(0, fov_rad, 0, 0)
|
||||||
|
row3 = Vector4d(0, 0, far_plane / (far_plane - near_plane), 1.0)
|
||||||
|
row4 = Vector4d(0, 0, -far_plane * near_plane / (far_plane - near_plane), 0)
|
||||||
|
|
||||||
|
return Matrix4x4(row1, row2, row3, row4)
|
||||||
|
|
||||||
|
def get_identity_matrix():
|
||||||
|
row1 = Vector4d(1, 0, 0, 0)
|
||||||
|
row2 = Vector4d(0, 1, 0, 0)
|
||||||
|
row3 = Vector4d(0, 0, 1, 0)
|
||||||
|
row4 = Vector4d(0, 0, 0, 1)
|
||||||
|
|
||||||
|
return Matrix4x4(row1, row2, row3, row4)
|
||||||
|
|
||||||
|
def get_y_rotation_matrix(angle_rad):
|
||||||
|
row1 = Vector4d(math.cos(angle_rad), 0, math.sin(angle_rad), 0)
|
||||||
|
row2 = Vector4d(0, 1, 0, 0)
|
||||||
|
row3 = Vector4d(-math.sin(angle_rad), 0, math.cos(angle_rad), 0)
|
||||||
|
row4 = Vector4d(0, 0, 0, 1)
|
||||||
|
|
||||||
|
return Matrix4x4(row1, row2, row3, row4)
|
||||||
|
|
||||||
|
def get_z_rotation_matrix(angle_rad):
|
||||||
|
row1 = Vector4d(math.cos(angle_rad), math.sin(angle_rad), 0, 0)
|
||||||
|
row2 = Vector4d(-math.sin(angle_rad), math.cos(angle_rad), 0, 0)
|
||||||
|
row3 = Vector4d(0, 0, 0, 0)
|
||||||
|
row4 = Vector4d(0, 0, 1, 0)
|
||||||
|
|
||||||
|
return Matrix4x4(row1, row2, row3, row4)
|
||||||
|
|
||||||
|
def get_x_rotation_matrix(angle_rad):
|
||||||
|
row1 = Vector4d(1, 0, 0, 0)
|
||||||
|
row2 = Vector4d(0, math.cos(angle_rad), math.sin(angle_rad), 0)
|
||||||
|
row3 = Vector4d(0, -math.sin(angle_rad), math.cos(angle_rad), 0)
|
||||||
|
row4 = Vector4d(0, 0, 0, 1)
|
||||||
|
|
||||||
|
return Matrix4x4(row1, row2, row3, row4)
|
||||||
|
|
||||||
class Matrix3x3:
|
class Matrix3x3:
|
||||||
|
|
||||||
def __init__(self, row0, row1, row2):
|
def __init__(self, row1, row2, row3):
|
||||||
self.row0 = row0
|
|
||||||
self.row1 = row1
|
self.row1 = row1
|
||||||
self.row2 = row2
|
self.row2 = row2
|
||||||
|
self.row3 = row3
|
||||||
|
|||||||
Reference in New Issue
Block a user