From eb67b6a9cc60be0e9e267185c5cffdd3f11e6fa5 Mon Sep 17 00:00:00 2001 From: Miguel Angel Date: Wed, 1 Nov 2017 15:39:20 -0400 Subject: [PATCH] Added intersection class. --- py_caster.py | 138 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 88 insertions(+), 50 deletions(-) diff --git a/py_caster.py b/py_caster.py index e32e294..16a0ee7 100755 --- a/py_caster.py +++ b/py_caster.py @@ -6,22 +6,23 @@ import pygame # Game parameters ############################################################## -TITLE = "Py Caster" -FPS = 60 -SCREEN_SIZE = (800, 600) +TITLE = "Py Caster" +FPS = 60 +SCREEN_SIZE = (800, 600) CEILING_COLOR = (75, 119, 208) -FLOOR_COLOR = (229, 138, 132) -FOG_COLOR = (128, 128, 128) -FOG_NEAR = 1.0 -FOG_FAR = 5.0 +FLOOR_COLOR = (229, 138, 132) +FOG_COLOR = (128, 128, 128) +FOG_NEAR = 1.0 +FOG_FAR = 5.0 +TOLERANCE = 0.000001 ############################################################## # Projection parameters ############################################################## -FB_SIZE = (320, 200) -DEG2RAD = 3.1415926535897932384626433 / 180.0 -FOV = 66.84962236520761 +FB_SIZE = (320, 200) +DEG2RAD = 3.1415926535897932384626433 / 180.0 +FOV = 66.84962236520761 ANGLE_INCREMENT = FOV / float(FB_SIZE[0]) ############################################################## @@ -91,31 +92,50 @@ class vec2(object): def cross(self, v): return vec3(0.0, 0.0, (self.x * v.y) - (self.y * v.x)) + def mix(self, v, t): + return vec2((self.x * t) + (v.x * (1.0 - t)), (self.y * t) + (v.y * (1.0 - t))) + ############################################################## # Ray Class ############################################################## -class Ray: +class Ray(object): def __init__(self, origin = vec2(0.0, 0.0), direction = vec2(0.0, 1.0)): self.o = origin self.d = direction.normalize() - def __str__(self): return "Origin: " + str(self.o) + " :: Direction: " + str(self.d) +############################################################## +# Intersection Class +############################################################## + +class Intersection(object): + def __init__(self, ray, point, tex_coords): + self.p = point + self.d = ray.o.distance(point) + self.tc = tex_coords + ############################################################## # Line Segment Class ############################################################## -class LineSegment: - def __init__(self, a, b, c = (0, 0, 0)): +class LineSegment(object): + def __init__(self, a, b, tca, tcb, c = (0, 0, 0)): self.a = a self.b = b self.v = b.sub(a).normalize() + self.tca = tca + self.tcb = tcb self.color = c def intersect(self, r): + def classifyPoint2D(point): + v1 = point.sub(self.a).normalize() + v2 = vec2(self.v.y, -self.v.x) + return v1.dot(v2) + def sign(n): if n == 0: return 0 @@ -124,33 +144,23 @@ class LineSegment: else: return -1 - side = self._classifyPoint2D(r.o) - + side = classifyPoint2D(r.o) v2 = self.b.sub(self.a) if sign(side) > 0 else self.a.sub(self.b) v3 = vec2(-r.d.y, r.d.x) - det = v2.dot(v3) - if abs(det) < 0.00001: + if abs(det) < TOLERANCE: return None else: v1 = r.o.sub(self.a) if sign(side) > 0 else self.a.sub(r.o) t1 = v2.cross(v1).length() / det t2 = v1.dot(v3) / det - if t2 >= 0.0 and t2 <= 1.0: - if t1 > 0.0: - return r.o.add(r.d.scale(t1)) - else: - return None + if t2 >= 0.0 and t2 <= 1.0 and t1 > 0.0: + return Intersection(r, r.o.add(r.d.scale(t1)), self.tca.mix(self.tcb, t2)) else: return None - def _classifyPoint2D(self, point): - v1 = point.sub(self.a) - v2 = vec2(self.v.y, -self.v.x) - return v1.dot(v2) - ############################################################## # Main Function ############################################################## @@ -163,6 +173,7 @@ def main(): player_pos = vec2(0.0, 0.0) player_dir = vec2(-1.0, 0.0) plane = vec2(0.0, 0.66) + arrow_keys = {pygame.K_UP: False, pygame.K_DOWN: False, pygame.K_LEFT: False, pygame.K_RIGHT: False} # Initialize Pygame. pygame.init() @@ -173,11 +184,11 @@ def main(): pygame.key.set_repeat(17, 17) # Define walls. - walls = [LineSegment(vec2(-3.0, 3.0), vec2(3.0, 3.0), (255, 0, 0)), - LineSegment(vec2(3.0, 3.0), vec2(3.0, -3.0), (0, 255, 0)), - LineSegment(vec2(1.5, 1.5), vec2(3.0, 3.0), (255, 255, 0)), - LineSegment(vec2(3.0, -3.0), vec2(-3.0, -3.0), (0, 0, 255)), - LineSegment(vec2(-3.0, -3.0), vec2(-3.0, 3.0), (255, 0, 255))] + walls = [LineSegment(vec2(-3.0, 3.0), vec2(3.0, 3.0), vec2(0.0, 1.0), vec2(0.0, 1.0), (255, 0, 0)), + LineSegment(vec2(3.0, 3.0), vec2(3.0, -3.0), vec2(0.0, 1.0), vec2(0.0, 1.0), (0, 255, 0)), + LineSegment(vec2(1.5, 1.5), vec2(3.0, 3.0), vec2(0.0, 1.0), vec2(0.0, 1.0), (255, 255, 0)), + LineSegment(vec2(3.0, -3.0), vec2(-3.0, -3.0), vec2(0.0, 1.0), vec2(0.0, 1.0), (0, 0, 255)), + LineSegment(vec2(-3.0, -3.0), vec2(-3.0, 3.0), vec2(0.0, 1.0), vec2(0.0, 1.0), (255, 0, 255))] # Main game loop. try: @@ -191,26 +202,28 @@ def main(): done = True if event.type == pygame.KEYDOWN and event.key == pygame.K_UP: - player_pos = player_pos.sub(player_dir.scale(PLAYER_MOVE_SPEED)) + arrow_keys[pygame.K_UP] = True if event.type == pygame.KEYDOWN and event.key == pygame.K_DOWN: - player_pos = player_pos.add(player_dir.scale(PLAYER_MOVE_SPEED)) + arrow_keys[pygame.K_DOWN] = True if event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT: - oldDirX = player_dir.x; - player_dir.x = player_dir.x * math.cos(PLAYER_TURN_SPEED) - player_dir.y * math.sin(PLAYER_TURN_SPEED); - player_dir.y = oldDirX * math.sin(PLAYER_TURN_SPEED) + player_dir.y * math.cos(PLAYER_TURN_SPEED); - oldPlaneX = plane.x; - plane.x = plane.x * math.cos(PLAYER_TURN_SPEED) - plane.y * math.sin(PLAYER_TURN_SPEED); - plane.y = oldPlaneX * math.sin(PLAYER_TURN_SPEED) + plane.y * math.cos(PLAYER_TURN_SPEED); + arrow_keys[pygame.K_LEFT] = True if event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT: - oldDirX = player_dir.x; - player_dir.x = player_dir.x * math.cos(-PLAYER_TURN_SPEED) - player_dir.y * math.sin(-PLAYER_TURN_SPEED); - player_dir.y = oldDirX * math.sin(-PLAYER_TURN_SPEED) + player_dir.y * math.cos(-PLAYER_TURN_SPEED); - oldPlaneX = plane.x; - plane.x = plane.x * math.cos(-PLAYER_TURN_SPEED) - plane.y * math.sin(-PLAYER_TURN_SPEED); - plane.y = oldPlaneX * math.sin(-PLAYER_TURN_SPEED) + plane.y * math.cos(-PLAYER_TURN_SPEED); + arrow_keys[pygame.K_RIGHT] = True + + if event.type == pygame.KEYUP and event.key == pygame.K_UP: + arrow_keys[pygame.K_UP] = False + + if event.type == pygame.KEYUP and event.key == pygame.K_DOWN: + arrow_keys[pygame.K_DOWN] = False + + if event.type == pygame.KEYUP and event.key == pygame.K_LEFT: + arrow_keys[pygame.K_LEFT] = False + + if event.type == pygame.KEYUP and event.key == pygame.K_RIGHT: + arrow_keys[pygame.K_RIGHT] = False if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE: if not toggle_fog: @@ -221,6 +234,29 @@ def main(): if toggle_fog: toggle_fog = False + # Camera movement + if arrow_keys[pygame.K_UP]: + player_pos = player_pos.sub(player_dir.scale(PLAYER_MOVE_SPEED)) + + if arrow_keys[pygame.K_DOWN]: + player_pos = player_pos.add(player_dir.scale(PLAYER_MOVE_SPEED)) + + if arrow_keys[pygame.K_LEFT]: + oldDirX = player_dir.x; + player_dir.x = player_dir.x * math.cos(PLAYER_TURN_SPEED) - player_dir.y * math.sin(PLAYER_TURN_SPEED); + player_dir.y = oldDirX * math.sin(PLAYER_TURN_SPEED) + player_dir.y * math.cos(PLAYER_TURN_SPEED); + oldPlaneX = plane.x; + plane.x = plane.x * math.cos(PLAYER_TURN_SPEED) - plane.y * math.sin(PLAYER_TURN_SPEED); + plane.y = oldPlaneX * math.sin(PLAYER_TURN_SPEED) + plane.y * math.cos(PLAYER_TURN_SPEED); + + if arrow_keys[pygame.K_RIGHT]: + oldDirX = player_dir.x; + player_dir.x = player_dir.x * math.cos(-PLAYER_TURN_SPEED) - player_dir.y * math.sin(-PLAYER_TURN_SPEED); + player_dir.y = oldDirX * math.sin(-PLAYER_TURN_SPEED) + player_dir.y * math.cos(-PLAYER_TURN_SPEED); + oldPlaneX = plane.x; + plane.x = plane.x * math.cos(-PLAYER_TURN_SPEED) - plane.y * math.sin(-PLAYER_TURN_SPEED); + plane.y = oldPlaneX * math.sin(-PLAYER_TURN_SPEED) + plane.y * math.cos(-PLAYER_TURN_SPEED); + # Render ceiling and floor. frame_buffer.fill(CEILING_COLOR, pygame.Rect(0, 0, FB_SIZE[0], FB_SIZE[1] / 2)) frame_buffer.fill(FLOOR_COLOR, pygame.Rect(0, FB_SIZE[1] / 2, FB_SIZE[0], FB_SIZE[1] / 2)) @@ -228,6 +264,7 @@ def main(): # Render walls. angle = -FOV / 2.0 for i in xrange(FB_SIZE[0]): + # Generate camera ray camera_x = 2.0 * (float(i) / float(FB_SIZE[0])) - 1; r = Ray(vec2(player_pos.x, player_pos.y), vec2(player_dir.x + plane.x * camera_x, player_dir.y + plane.y * camera_x)) @@ -236,9 +273,8 @@ def main(): for l in walls: p = l.intersect(r) if p is not None: - _d = player_pos.distance(p) - if _d < d: - d = _d + if p.d < d: + d = p.d def lerp(col, dst): lt = 0.0 if dst < FOG_NEAR else (1.0 if dst > FOG_FAR else (dst - FOG_NEAR) / (FOG_FAR - FOG_NEAR)) @@ -258,8 +294,10 @@ def main(): angle += ANGLE_INCREMENT + # Render framebuffer to the screen pygame.transform.scale(frame_buffer, SCREEN_SIZE, screen) + # Update screen pygame.display.update() clock.tick(FPS)