Added intersection class.
This commit is contained in:
138
py_caster.py
138
py_caster.py
@@ -6,22 +6,23 @@ import pygame
|
|||||||
# Game parameters
|
# Game parameters
|
||||||
##############################################################
|
##############################################################
|
||||||
|
|
||||||
TITLE = "Py Caster"
|
TITLE = "Py Caster"
|
||||||
FPS = 60
|
FPS = 60
|
||||||
SCREEN_SIZE = (800, 600)
|
SCREEN_SIZE = (800, 600)
|
||||||
CEILING_COLOR = (75, 119, 208)
|
CEILING_COLOR = (75, 119, 208)
|
||||||
FLOOR_COLOR = (229, 138, 132)
|
FLOOR_COLOR = (229, 138, 132)
|
||||||
FOG_COLOR = (128, 128, 128)
|
FOG_COLOR = (128, 128, 128)
|
||||||
FOG_NEAR = 1.0
|
FOG_NEAR = 1.0
|
||||||
FOG_FAR = 5.0
|
FOG_FAR = 5.0
|
||||||
|
TOLERANCE = 0.000001
|
||||||
|
|
||||||
##############################################################
|
##############################################################
|
||||||
# Projection parameters
|
# Projection parameters
|
||||||
##############################################################
|
##############################################################
|
||||||
|
|
||||||
FB_SIZE = (320, 200)
|
FB_SIZE = (320, 200)
|
||||||
DEG2RAD = 3.1415926535897932384626433 / 180.0
|
DEG2RAD = 3.1415926535897932384626433 / 180.0
|
||||||
FOV = 66.84962236520761
|
FOV = 66.84962236520761
|
||||||
ANGLE_INCREMENT = FOV / float(FB_SIZE[0])
|
ANGLE_INCREMENT = FOV / float(FB_SIZE[0])
|
||||||
|
|
||||||
##############################################################
|
##############################################################
|
||||||
@@ -91,31 +92,50 @@ class vec2(object):
|
|||||||
def cross(self, v):
|
def cross(self, v):
|
||||||
return vec3(0.0, 0.0, (self.x * v.y) - (self.y * v.x))
|
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
|
# Ray Class
|
||||||
##############################################################
|
##############################################################
|
||||||
|
|
||||||
class Ray:
|
class Ray(object):
|
||||||
def __init__(self, origin = vec2(0.0, 0.0), direction = vec2(0.0, 1.0)):
|
def __init__(self, origin = vec2(0.0, 0.0), direction = vec2(0.0, 1.0)):
|
||||||
self.o = origin
|
self.o = origin
|
||||||
self.d = direction.normalize()
|
self.d = direction.normalize()
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Origin: " + str(self.o) + " :: Direction: " + str(self.d)
|
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
|
# Line Segment Class
|
||||||
##############################################################
|
##############################################################
|
||||||
|
|
||||||
class LineSegment:
|
class LineSegment(object):
|
||||||
def __init__(self, a, b, c = (0, 0, 0)):
|
def __init__(self, a, b, tca, tcb, c = (0, 0, 0)):
|
||||||
self.a = a
|
self.a = a
|
||||||
self.b = b
|
self.b = b
|
||||||
self.v = b.sub(a).normalize()
|
self.v = b.sub(a).normalize()
|
||||||
|
self.tca = tca
|
||||||
|
self.tcb = tcb
|
||||||
self.color = c
|
self.color = c
|
||||||
|
|
||||||
def intersect(self, r):
|
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):
|
def sign(n):
|
||||||
if n == 0:
|
if n == 0:
|
||||||
return 0
|
return 0
|
||||||
@@ -124,33 +144,23 @@ class LineSegment:
|
|||||||
else:
|
else:
|
||||||
return -1
|
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)
|
v2 = self.b.sub(self.a) if sign(side) > 0 else self.a.sub(self.b)
|
||||||
v3 = vec2(-r.d.y, r.d.x)
|
v3 = vec2(-r.d.y, r.d.x)
|
||||||
|
|
||||||
det = v2.dot(v3)
|
det = v2.dot(v3)
|
||||||
|
|
||||||
if abs(det) < 0.00001:
|
if abs(det) < TOLERANCE:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
v1 = r.o.sub(self.a) if sign(side) > 0 else self.a.sub(r.o)
|
v1 = r.o.sub(self.a) if sign(side) > 0 else self.a.sub(r.o)
|
||||||
t1 = v2.cross(v1).length() / det
|
t1 = v2.cross(v1).length() / det
|
||||||
t2 = v1.dot(v3) / det
|
t2 = v1.dot(v3) / det
|
||||||
|
|
||||||
if t2 >= 0.0 and t2 <= 1.0:
|
if t2 >= 0.0 and t2 <= 1.0 and t1 > 0.0:
|
||||||
if t1 > 0.0:
|
return Intersection(r, r.o.add(r.d.scale(t1)), self.tca.mix(self.tcb, t2))
|
||||||
return r.o.add(r.d.scale(t1))
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
else:
|
else:
|
||||||
return None
|
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
|
# Main Function
|
||||||
##############################################################
|
##############################################################
|
||||||
@@ -163,6 +173,7 @@ def main():
|
|||||||
player_pos = vec2(0.0, 0.0)
|
player_pos = vec2(0.0, 0.0)
|
||||||
player_dir = vec2(-1.0, 0.0)
|
player_dir = vec2(-1.0, 0.0)
|
||||||
plane = vec2(0.0, 0.66)
|
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.
|
# Initialize Pygame.
|
||||||
pygame.init()
|
pygame.init()
|
||||||
@@ -173,11 +184,11 @@ def main():
|
|||||||
pygame.key.set_repeat(17, 17)
|
pygame.key.set_repeat(17, 17)
|
||||||
|
|
||||||
# Define walls.
|
# Define walls.
|
||||||
walls = [LineSegment(vec2(-3.0, 3.0), vec2(3.0, 3.0), (255, 0, 0)),
|
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), (0, 255, 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), (255, 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), (0, 0, 255)),
|
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), (255, 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.
|
# Main game loop.
|
||||||
try:
|
try:
|
||||||
@@ -191,26 +202,28 @@ def main():
|
|||||||
done = True
|
done = True
|
||||||
|
|
||||||
if event.type == pygame.KEYDOWN and event.key == pygame.K_UP:
|
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:
|
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:
|
if event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT:
|
||||||
oldDirX = player_dir.x;
|
arrow_keys[pygame.K_LEFT] = True
|
||||||
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 event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
|
if event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
|
||||||
oldDirX = player_dir.x;
|
arrow_keys[pygame.K_RIGHT] = True
|
||||||
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);
|
if event.type == pygame.KEYUP and event.key == pygame.K_UP:
|
||||||
oldPlaneX = plane.x;
|
arrow_keys[pygame.K_UP] = False
|
||||||
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 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 event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
|
||||||
if not toggle_fog:
|
if not toggle_fog:
|
||||||
@@ -221,6 +234,29 @@ def main():
|
|||||||
if toggle_fog:
|
if toggle_fog:
|
||||||
toggle_fog = False
|
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.
|
# Render ceiling and floor.
|
||||||
frame_buffer.fill(CEILING_COLOR, pygame.Rect(0, 0, FB_SIZE[0], FB_SIZE[1] / 2))
|
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))
|
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.
|
# Render walls.
|
||||||
angle = -FOV / 2.0
|
angle = -FOV / 2.0
|
||||||
for i in xrange(FB_SIZE[0]):
|
for i in xrange(FB_SIZE[0]):
|
||||||
|
# Generate camera ray
|
||||||
camera_x = 2.0 * (float(i) / float(FB_SIZE[0])) - 1;
|
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))
|
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:
|
for l in walls:
|
||||||
p = l.intersect(r)
|
p = l.intersect(r)
|
||||||
if p is not None:
|
if p is not None:
|
||||||
_d = player_pos.distance(p)
|
if p.d < d:
|
||||||
if _d < d:
|
d = p.d
|
||||||
d = _d
|
|
||||||
|
|
||||||
def lerp(col, dst):
|
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))
|
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
|
angle += ANGLE_INCREMENT
|
||||||
|
|
||||||
|
# Render framebuffer to the screen
|
||||||
pygame.transform.scale(frame_buffer, SCREEN_SIZE, screen)
|
pygame.transform.scale(frame_buffer, SCREEN_SIZE, screen)
|
||||||
|
|
||||||
|
# Update screen
|
||||||
pygame.display.update()
|
pygame.display.update()
|
||||||
clock.tick(FPS)
|
clock.tick(FPS)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user