Raycasting engine optimizations

53 views
Skip to first unread message

caribou

unread,
Jan 28, 2012, 5:50:22 PM1/28/12
to pyglet-users
Hello,

I'm trying to make a raycasting engine with pyglet but i'm having
~15fps right now and i think i need more advanced opengl knowledge. If
i understand well, batchs are a way to reduce the number of calls but
since everything graphically changes at every movement i'm not sure if
i will benefit from it. I'm wondering what i could do to improve
performances.

I'm not focusing on the raycasting algorithm right now but maybe i
should use something simpler : I'm using the tutorial from permadi.com
on raycasting but i know there's a popular algorithm called B********
(I can't remember the name and google is not helping me)

This is the piece of code i'm using :

glBegin(GL_QUADS)
glVertex2f(x, y - h)
glVertex2f(x + self.wall_width, y - h)
glVertex2f(x + self.wall_width, y + h)
glVertex2f(x, y + h)
glEnd()

Sorry i'm never sure if i should paste so much code here but i don't
like to put links so here is the whole source (i've commented the
texture mapping part so it should work just by pasting it) :

If you want a more readable version > http://paste2.org/p/1886941

from math import cos, sin, tan, radians, sqrt, pow

import pyglet
from pyglet.gl import *
from pyglet.window import key


bmap = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1],
]


BLOCK_SIZE = 32
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 480


def rotate(v, angle):
vx = v[0] * cos(radians(-angle)) - v[1] * sin(radians(-angle))
vy = v[0] * sin(radians(-angle)) + v[1] * cos(radians(-angle))
return vx, vy


class Game(pyglet.event.EventDispatcher):
def __init__(self):
self.window = pyglet.window.Window(
vsync=False,
width=WINDOW_WIDTH,
height=WINDOW_HEIGHT)
self.keys = pyglet.window.key.KeyStateHandler()
self.window.push_handlers(self.keys)
self.window.push_handlers(self)
self.myplayer = Player(self, 100, 200)
self.fps = pyglet.clock.ClockDisplay()
self.fps.label.x = 200
pyglet.clock.schedule_interval(self.update, 1 / 60.)

self.Ax = self.Ay = self.Cx = self.Cy = 0
self.Bx = self.By = 0
self.H = self.V = self.C = None
self.collision_points = {}
self.map_active = False
self.rays_angles = []

angle_range = 60.
rays = 640
angle_step = angle_range / rays
angle = -angle_range / 2
while angle < angle_range / 2:
angle += angle_step
self.rays_angles.append(angle)

# self.img = pyglet.image.load('stonetest.png')
self.wall_width = WINDOW_WIDTH / rays
print "wall width", self.wall_width
# self.wall_tex = self.img.get_texture().id

### Blocks instanciation
for x, row in enumerate(bmap):
for y, kind in enumerate(row):
Block(self, x, y, kind)
print "Game Started"

def on_key_press(self, symbol, modifiers):
if symbol == key.F12:
if self.map_active == False:
self.map_active = True
else:
self.map_active = False

def update(self, dt):
self.myplayer.update(dt)

### RAAAAYCAAASTIIING
self.collision_points = {}
for col, angle in enumerate(self.rays_angles):
self.collision_points[col] = self.get_collision_point(col,
angle)

def get_collision_point(self, col, angle):
v = rotate(self.myplayer.v, angle)
x, y = self.myplayer.x, self.myplayer.y
dV = dH = 999999 # dummy
C = None
angle = radians(90 + angle + self.myplayer.rotation)

### A
if v[1] > 0:
Ay = (y // BLOCK_SIZE) * BLOCK_SIZE + BLOCK_SIZE
else:
Ay = (y // BLOCK_SIZE) * BLOCK_SIZE - 0.1

try:
Ax = x + (y - Ay) / tan(angle)
except ZeroDivisionError:
Ax = x

### Xa / Ya
if v[1] > 0:
try:
Xa = BLOCK_SIZE / tan(-angle)
except ZeroDivisionError:
Xa = 9999
Ya = BLOCK_SIZE
else:
try:
Xa = BLOCK_SIZE / tan(angle)
except ZeroDivisionError:
Xa = 9999
Ya = -BLOCK_SIZE

Px = Ax
Py = Ay

### Horizontal Collisions
while True:
self.H = None
posx = int(Px // BLOCK_SIZE)
posy = int(Py // BLOCK_SIZE)

try:
if Block._registry[(posx, posy)].kind == 1:
self.H = (Px, Py)
dH = sqrt(pow(self.H[0] - x, 2) + pow(self.H[1] -
y, 2))
break
else:
Px += Xa
Py += Ya
except KeyError:
break

########################################################

### B
if v[0] > 0:
Bx = (x // BLOCK_SIZE) * BLOCK_SIZE + BLOCK_SIZE
else:
Bx = (x // BLOCK_SIZE) * BLOCK_SIZE - 0.1

By = y + (x - Bx) * tan(angle)

### Xa / Ya
if v[0] > 0:
Ya = BLOCK_SIZE * tan(-angle)
Xa = BLOCK_SIZE
else:
Ya = BLOCK_SIZE * tan(angle)
Xa = -BLOCK_SIZE

Px = Bx
Py = By

### Vertical Collisions
while True:
self.V = None
posx = int(Px // BLOCK_SIZE)
posy = int(Py // BLOCK_SIZE)

try:
if Block._registry[(posx, posy)].kind == 1:
self.V = (Px, Py)
dV = sqrt(pow(self.V[0] - x, 2) + pow(self.V[1] -
y, 2))
break
else:
Px += Xa
Py += Ya
except KeyError:
break

if dV < dH:
C = self.V
d = dV
coltype = "V"
else:
C = self.H
d = dH
coltype = "H"

offset = x = y = h = sx = sx2 = None # Ugly but whatever
if C is not None:
if coltype == "V":
offset = C[1] % BLOCK_SIZE
else:
offset = C[0] % BLOCK_SIZE

x = col * self.wall_width
y = WINDOW_HEIGHT / 2
h = (BLOCK_SIZE / d) * 277

k = 1. / BLOCK_SIZE
sx = offset * k
sx2 = (offset + self.wall_width) * k

return C, d, offset, x, y, h, sx, sx2

def on_draw(self):
self.window.clear()

pyglet.gl.glColor3f(0.3, 0.3, 0.3)
pyglet.gl.glRectf(0, 0,
WINDOW_WIDTH, WINDOW_HEIGHT / 2)

for block in Block._registry.values():
block.draw()

self.myplayer.draw()

if self.map_active:
# H point
if self.H is not None:
pyglet.gl.glColor3f(1, 1, 0.3)
pyglet.gl.glRectf(self.H[0], self.H[1],
self.H[0] + 2, self.H[1] + 2)

# V point
if self.V is not None:
pyglet.gl.glColor3f(1, 0.3, 1)
pyglet.gl.glRectf(self.V[0], self.V[1],
self.V[0] + 2, self.V[1] + 2)

for col, (point, d, offset, x, y, h, sx, sx2) \
in self.collision_points.items():
if point is not None:
if self.map_active:
# RAY
pyglet.gl.glColor3f(0.6, 0.6, 0.6)
pyglet.graphics.draw(2, pyglet.gl.GL_LINES,
('v2f', (self.myplayer.x, self.myplayer.y,
point[0], point[1])))
# POINT
pyglet.gl.glColor3f(1, 0.1, 0.1)
pyglet.gl.glRectf(point[0], point[1],
point[0] + 2, point[1] + 2)

# WALL, here is the interesting part
pyglet.gl.glColor3f(0.6, 1, 0.3)

# glEnable(GL_TEXTURE_2D)
# glBindTexture(GL_TEXTURE_2D, self.wall_tex)
glBegin(GL_QUADS)
# glTexCoord2f(sx, 0.0)
glVertex2f(x, y - h)
# glTexCoord2f(sx2, 0.0)
glVertex2f(x + self.wall_width, y - h)
# glTexCoord2f(sx2, 1.0)
glVertex2f(x + self.wall_width, y + h)
# glTexCoord2f(sx, 1.0)
glVertex2f(x, y + h)
glEnd()
# glDisable(GL_TEXTURE_2D)

self.fps.draw()


class Block:
_registry = {}

def __init__(self, game, posx, posy, kind):
self.game = game
self._registry[(posx, posy)] = self
self.x = posx * BLOCK_SIZE
self.y = posy * BLOCK_SIZE
self.posx = posx
self.posy = posy
self.kind = kind

def draw(self):
if self.game.map_active:
if self.kind == 0:
pyglet.gl.glColor3f(0.5, 0.5, 0.5)
else:
pyglet.gl.glColor3f(0.5, 0.5, 1)
pyglet.gl.glRectf(self.x, self.y,
self.x + BLOCK_SIZE, self.y + BLOCK_SIZE)

pyglet.gl.glColor3f(0.4, 0.4, 0.4)
pyglet.graphics.draw(8, pyglet.gl.GL_LINES,
('v2f', (self.x, self.y, self.x, self.y + BLOCK_SIZE,
self.x, self.y + BLOCK_SIZE, self.x + BLOCK_SIZE,
self.y + BLOCK_SIZE,
self.x + BLOCK_SIZE, self.y + BLOCK_SIZE, self.x +
BLOCK_SIZE, self.y,
self.x + BLOCK_SIZE, self.y, self.x, self.y)
))


class Player:
SPEED = 400

def __init__(self, game, x, y):
self.game = game
self.x, self.y = x, y
self.rotation = 0
self.v = self.vo = (0, 1)

def update(self, dt):
if self.game.keys[key.W]:
self.x += self.v[0] * self.SPEED * dt
self.y += self.v[1] * self.SPEED * dt
if self.game.keys[key.S]:
self.x -= self.v[0] * self.SPEED * dt
self.y -= self.v[1] * self.SPEED * dt
if self.game.keys[key.A]:
self.rotation -= 5
self.v = rotate(self.vo, self.rotation)
if self.game.keys[key.D]:
self.rotation += 5
self.v = rotate(self.vo, self.rotation)

def draw(self):
if self.game.map_active:
# Char
pyglet.gl.glColor3f(1, 0.5, 0.5)
pyglet.gl.glRectf(self.x - 2, self.y - 2,
self.x + 2, self.y + 2)

# View ray
pyglet.gl.glColor3f(0.6, 1, 0.6)
pyglet.graphics.draw(2, pyglet.gl.GL_LINES,
('v2f', (self.x, self.y,
self.x + 100 * self.v[0], self.y + 100 *
self.v[1])))

# View
pyglet.gl.glColor3f(1, 0.5, 0.5)
pyglet.graphics.draw(2, pyglet.gl.GL_LINES,
('v2f', (self.x, self.y,
self.x + 10 * self.v[0], self.y + 10 *
self.v[1])))


if __name__ == '__main__':
app = Game()
pyglet.app.run()
Reply all
Reply to author
Forward
0 new messages