Handle texture coords when clipping

This commit is contained in:
Alex Clink 2022-03-03 00:00:19 -05:00
parent 2d44da33b4
commit 2e6fca44e3
5 changed files with 53 additions and 25 deletions

View file

@ -1,9 +1,11 @@
v 0.0 0.0 0.0
v 0.0 1.0 0.0
v 1.0 1.0 0.0
v 1.0 1.0 0.0
v 1.0 0.0 0.0
v 1.0 1.0 1.0
v 1.0 0.0 1.0
v 0.0 1.0 1.0
v 0.0 0.0 1.0
@ -14,7 +16,7 @@ vt 1.0 0.0
vt 0.0 1.0
vt 1.0 0.0
vt 1.0 0.0
vt 1.0 1.0
# south
f 1/1 2/2 3/3

View file

@ -36,15 +36,18 @@ module PF
end
def forward_vector
Transform3d.apply(Vector3.new(0.0, 0.0, 1.0), rotation_matrix)
v, w = Transform3d.apply(Vector3.new(0.0, 0.0, 1.0), rotation_matrix)
v
end
def strafe_vector
Transform3d.apply(Vector3.new(1.0, 0.0, 0.0), rotation_matrix)
v, w = Transform3d.apply(Vector3.new(1.0, 0.0, 0.0), rotation_matrix)
v
end
def up_vector
Transform3d.apply(Vector3.new(0.0, 1.0, 0.0), rotation_matrix)
v, w = Transform3d.apply(Vector3.new(0.0, 1.0, 0.0), rotation_matrix)
v
end
def matrix

View file

@ -1,4 +1,6 @@
module PF
# Mesh represents a collection of points and triangles
# TODO: Keep points in a collection, removing duplicates, and keep triangle verticies pointing to the points in that collection
class Mesh
setter tris = [] of Tri
property origin : Vector3(Float64) = Vector[0.0, 0.0, 0.0]
@ -6,6 +8,7 @@ module PF
property position : Vector3(Float64) = Vector[0.0, 0.0, 0.0]
# Load an obj file
# TODO: Load meshes specified by the obj file
def self.load_obj(path, use_normals : Bool = false)
verticies = [] of Vector3(Float64)
texture_verticies = [] of Vector3(Float64)
@ -64,6 +67,7 @@ module PF
tris << tri
# Split a square into triangles
# TODO: Handle texture points
if face_verts.size > 3
tri = Tri.new(face_verts[0], face_verts[2], face_verts[3], normal: normal)
tris << tri

View file

@ -54,6 +54,7 @@ module PF
@fov_rad ||= 1.0 / Math.tan(@fov * 0.5 / 180.0 * Math::PI)
end
# Project an array of Triangles into screen space
def project(tris : Array(Tri), camera = @camera)
mat_view = camera.view_matrix
@ -96,6 +97,7 @@ module PF
end
# sort triangles
# TODO: Z-buffer
tris.sort! { |a, b| b.z <=> a.z }
# Clip against the edges of the screen

View file

@ -12,8 +12,6 @@ module PF
property t2 : Vector3(Float64) = Vector[0.0, 0.0, 0.0]
property t3 : Vector3(Float64) = Vector[0.0, 0.0, 0.0]
property texture : Sprite*? = nil
property color : PF::Pixel
setter normal : Vector3(Float64)?
@ -21,7 +19,7 @@ module PF
def initialize(@p1 : Vector3(Float64), @p2 : Vector3(Float64), @p3 : Vector3(Float64), @color = PF::Pixel.white, @normal = nil)
end
def initialize(@p1, @p2, @p3, @t1, @t2, @t3, @texture = nil, @color = PF::Pixel.white)
def initialize(@p1, @p2, @p3, @t1, @t2, @t3, @color = PF::Pixel.white)
end
# Return the normal assuming clockwise pointing winding
@ -50,14 +48,15 @@ module PF
# Multiply all points by a *Matrix*, returning a new *Tri*
def *(mat : Matrix)
pp1, w1 = Transform3d.apply(@p1, mat)
pp2, w2 = Transform3d.apply(@p2, mat)
pp3, w3 = Transform3d.apply(@p3, mat)
Tri.new(
Transform3d.apply(@p1, mat),
Transform3d.apply(@p2, mat),
Transform3d.apply(@p3, mat),
pp1, pp2, pp3,
@t1,
@t2,
@t3,
@texture,
@color
)
end
@ -69,70 +68,88 @@ module PF
plane_normal = plane_normal.normalized
# Create two temporary storage arrays to classify points either side of plane
inside_points = StaticArray(Vector3(Float64), 3).new(Vector[0.0, 0.0, 0.0])
inside_points = uninitialized StaticArray(Vector3(Float64), 3)
inside_count = 0
outside_points = StaticArray(Vector3(Float64), 3).new(Vector[0.0, 0.0, 0.0])
outside_points = uninitialized StaticArray(Vector3(Float64), 3)
outside_count = 0
# Create the same for texture points
inside_texts = uninitialized StaticArray(Vector3(Float64), 3)
outside_texts = uninitialized StaticArray(Vector3(Float64), 3)
# Classify each point as inside or outside of the plane
{p1, p2, p3}.each do |p|
{ {p1, t1}, {p2, t2}, {p3, t3} }.each do |p, t|
# Get the distance of the point to the clipping plane
distance = plane_normal.x * p.x + plane_normal.y * p.y + plane_normal.z * p.z - plane_normal.dot(plane)
# If the distance is positive, our point lies on inside of plane
# If the distance is positive, our point lies on inside of the plane
if distance >= 0
inside_points[inside_count] = p
inside_texts[inside_count] = t
inside_count += 1
else
outside_points[outside_count] = p
outside_texts[outside_count] = t
outside_count += 1
end
end
# Clip the entire triangle
return Tuple.new if inside_count == 0
# All points are inside of the plane
# No clipping required, return the original triangle
return {self} if inside_count == 3
# Clip two points of the tri into one tri
# One point inside the plane, 2 outside
if inside_count == 1 && outside_count == 2
# One point inside the plane
# the two intersection points and the one inside point form a new triangle
clip_p1, t = G3d.line_intersects_plane(plane, plane_normal, inside_points[0], outside_points[0])
int_t1 = (outside_texts[0] - inside_texts[0]) * t + inside_texts[0]
i_p1, t1 = G3d.line_intersects_plane(plane, plane_normal, inside_points[0], outside_points[0])
i_p2, t2 = G3d.line_intersects_plane(plane, plane_normal, inside_points[0], outside_points[1])
clip_p2, t = G3d.line_intersects_plane(plane, plane_normal, inside_points[0], outside_points[1])
int_t2 = (outside_texts[1] - inside_texts[0]) * t + inside_texts[0]
return {
Tri.new(
inside_points[0], i_p1, i_p2,
inside_points[0], clip_p1, clip_p2,
inside_texts[0], int_t1, int_t2,
color: @color
),
}
end
# Clip one point of the tri, return two tris
if inside_count == 2 && outside_count == 1
# Two points are inside the plane, this will form a quad
# We must now split the quad into two new triangles
# Calculate the two intersection points
i_p1, t1 = G3d.line_intersects_plane(plane, plane_normal, inside_points[0], outside_points[0])
i_p2, t2 = G3d.line_intersects_plane(plane, plane_normal, inside_points[1], outside_points[0])
clip_p1, t = G3d.line_intersects_plane(plane, plane_normal, inside_points[0], outside_points[0])
int_t1 = (outside_texts[0] - inside_texts[0]) * t + inside_texts[0]
clip_p2, t = G3d.line_intersects_plane(plane, plane_normal, inside_points[1], outside_points[0])
int_t2 = (outside_texts[0] - inside_texts[1]) * t + inside_texts[1]
return {
# The first triangle will have the two inside points, and first intersection point
Tri.new(
inside_points[0], inside_points[1], i_p1,
inside_points[0], inside_points[1], clip_p1,
inside_texts[0], inside_texts[1], int_t1,
color: @color
),
# The second triangle will have the second inside point, the second intersection, then the first intersection
# This order preserves clockwise winding
Tri.new(
inside_points[1], i_p2, i_p1,
inside_points[1], clip_p2, clip_p1,
inside_texts[1], int_t2, int_t1,
color: @color
),
}
end
# No points are inside the plane
# Return an empty tuple with no triangles
# So the compiler doesn't complain about nil return type
Tuple.new
end
end