Skip to content

Instantly share code, notes, and snippets.

@TerryCavanagh
Created July 16, 2015 19:00
Show Gist options
  • Select an option

  • Save TerryCavanagh/c02fb300007645c1021d to your computer and use it in GitHub Desktop.

Select an option

Save TerryCavanagh/c02fb300007645c1021d to your computer and use it in GitHub Desktop.
-- raycast variables
camerax = 0.0 cameray = 0.0
rayposx = 0.0 rayposy = 0.0
raydirx = 0.0 raydiry = 0.0
sidedistx = 0.0 sidedisty=0.0
deltadistx = 0.0 deltadisty=0.0
newdeltadistx = 0.0 newdeltadisty=0.0
perpwalldist = 0.0
distwall = 0.0 distplayer=0.0
currentdist = 0.0
currentfloorx = 0.0
currentfloory = 0.0
drawframe = 0
dodrawing = false
transparentthisframe = false
transparent_start = 0
transparent_end = 0
_4pi = 12.5663706143592
_2pi = 6.28318530717959
pi = 3.14159265358979
pi_2 = 1.5707963267949
pi_4 = 0.785398163397448
emptysquare = 0
check = 0
zbuffer = {}
so = {}
spritedistance = {}
redrawstripe = {}
sss = {}
mapx = 0 mapy = 0
stepx = 0 stepy = 0
hit = 0 side = 0
rayheight = 0
drawstart = 0 drawend = 0
-- raycast coordinates
posx = 0.0 posy = 0.0
dirx = 0.0 diry = 0.0
planex = 0.0 planey = 0.0
olddirx = 0.0 olddiry = 0.0
oldplanex = 0.0 oldplaney = 0.0
wallbuffer = 0.5
playerwallbuffer = 0.3
turnspeed = (_2pi)/360
movespeed = 0.15
showfloor = true
showceiling = true
uselighting = true
ceilingcol = 0
floorcol = 0
w = 56 w2 = w*2
h = 48 h2 = h*2
xscale = 2 xdraw = 3
yscale = 2 ydraw = 1 ydrawfloor = 3
wallheight = (50/64)*h
texwidth = 8
texheight = 8
function raycast_init()
for i=0,129 do
zbuffer[i+1] = 0
redrawstripe[i+1] = 0
sss[i+1] = 0
end
end
wallat = 0
function checkwall(xpos, ypos)
wallat = mget(flr(xpos), flr(ypos));
if (blocks[wallat + 1].collidable) then
return true
end
return false
end
function getwall(xpos, ypos)
return mget(flr(xpos), flr(ypos))
end
function rotate(angle)
--both camera direction and camera plane must be rotated
olddirx = dirx
dirx = (dirx * cos(angle)) - (diry * sin(angle))
diry = (olddirx * sin(angle)) + (diry * cos(angle))
oldplanex = planex
planex = (planex * cos(angle)) - (planey * sin(angle))
planey = (oldplanex * sin(angle)) + (planey * cos(angle))
end
function storeolddir()
olddirx = dirx
olddiry = diry
oldplanex = planex
oldplaney = planey
end
function restoreolddir()
dirx = olddirx
diry = olddiry
planex = oldplanex
planey = oldplaney
end
function safesqrt(n)
if(n<=0.01) then
return 0.0
end
return sqrt(n)
end
shootthrough = 0
function drawstripe(x, layer)
shootthrough = 0
--calculate ray position and direction
camerax = 2 * x / w - 1 --x-coordinate in camera space
rayposx = posx
rayposy = posy
raydirx = dirx + (planex * camerax)
raydiry = diry + (planey * camerax)
--which box of the map we're in
mapx = flr(rayposx)
mapy = flr(rayposy)
--length of ray from one x or y-side to next x or y-side
newdeltadistx = safesqrt(1 + (raydiry * raydiry) / (raydirx * raydirx))
newdeltadisty = safesqrt(1 + (raydirx * raydirx) / (raydiry * raydiry))
if(newdeltadistx>=1) then
deltadistx = newdeltadistx
end
if(newdeltadisty>=1) then
deltadisty = newdeltadisty
end
hit = 0 --was there a wall hit?
--calculate step and initial sidedist
if (raydirx < 0) then
stepx = -1
sidedistx = (rayposx - mapx) * deltadistx
else
stepx = 1
sidedistx = (mapx + 1.0 - rayposx) * deltadistx
end
if (raydiry < 0) then
stepy = -1
sidedisty = (rayposy - mapy) * deltadisty
else
stepy = 1
sidedisty = (mapy + 1.0 - rayposy) * deltadisty
end
--perform dda
raylength = 0
while (hit == 0) do
--jump to next map square, or in x-direction, or in y-direction
if (sidedistx <= sidedisty) then
sidedistx = sidedistx + deltadistx
mapx = mapx + stepx
side = 0
else
sidedisty = sidedisty + deltadisty
mapy = mapy + stepy
side = 1
end
raylength = raylength + 1
if(raylength>=50) then hit=1 end
--check if ray has hit a wall
currentwall = mget(flr(mapx), flr(mapy))
if (blocks[currentwall+1].visible) then
hit = 1
if(blocks[currentwall+1].transparent or mapget(flr(mapx), flr(mapy)) > 0) then
if(layer == 0) then
-- count the transparent collisions, go to the end
hit = 0
shootthrough = shootthrough + 1
redrawstripe[x+1] = shootthrough
if(transparentthisframe) then
transparent_end = x
else
transparentthisframe = true
transparent_start = x
end
else
-- stop at the numbered wall
hit = 0
shootthrough = shootthrough + 1
if(shootthrough == layer) then
hit = 1
end
end
end
if(blocks[currentwall+1].halfway) then
if(hit == 1) then
if(side == 0) then
if (sidedistx <= sidedisty) then
--ray shoots straight through, so definitely door
rayposx = rayposx - (stepx/3)
else
-- take the smaller distance: the wall, or the door
walldist = abs(((mapy+stepy) - rayposy + (1 - stepy) / 2) / raydiry)
doordist = abs((mapx - (rayposx - (stepx/3)) + (1 - stepx) / 2) / raydirx)
if(doordist<walldist) then
side = 0
rayposx = rayposx - (stepx/3)
else
side = 1
mapy = mapy + stepy
end
end
elseif(side == 1) then
if (sidedistx <= sidedisty) then
-- take the smaller distance: the wall, or the door
walldist = abs(((mapx+stepx) - rayposx + (1 - stepx) / 2) / raydirx)
doordist = abs((mapy - (rayposy - (stepy/3)) + (1 - stepy) / 2) / raydiry)
if(doordist<walldist) then
side = 1
rayposy = rayposy - (stepy/3)
else
side = 0
mapx = mapx + stepx
end
else
--ray shoots straight through, so definitely door
rayposy = rayposy - (stepy/3)
end
end
end
end
end
end
--calculate distance of perpendicular ray (oblique distance will give fisheye effect!)
if (side == 0) then
perpwalldist = abs((mapx - rayposx + (1 - stepx) / 2) / raydirx)
else
perpwalldist = abs((mapy - rayposy + (1 - stepy) / 2) / raydiry)
end
--store walldistance in buffer for sprites later
zbuffer[x+1] = perpwalldist
--calculate height of line to draw on screen
rayheight = abs(flr(wallheight / perpwalldist))
--calculate lowest and highest pixel to fill in current stripe
drawstart = -(rayheight / 2) + (h / 2)
if(drawstart < 0) then
drawstart = 0
end
drawend = rayheight / 2 + h / 2
if (drawend >= h) then
drawend = h - 1
end
dodrawing = true
if(layer ~= 0) then
if(sss[x+1] > drawend - drawstart) then
dodrawing = false
end
end
if(dodrawing) then
--texturing calculations
texnum = mget(mapx, mapy)
--calculate value of wallx
if (side == 1) then
wallx = rayposx + ((mapy - rayposy + (1 - stepy) / 2) / raydiry) * raydirx
else
wallx = rayposy + ((mapx - rayposx + (1 - stepx) / 2) / raydirx) * raydiry
end
wallx = wallx - flr(wallx)
--x coordinate on the texture
texx = flr(wallx * blocks[texnum+1].tex_w)
if(side == 0) then
if (raydirx > 0) then
texx = blocks[texnum+1].tex_w - texx - 1
end
end
if(side == 1) then
if(raydiry < 0) then
texx = blocks[texnum+1].tex_w - texx - 1
end
end
for y = drawstart,drawend do
d = y - (h * 0.5) + (rayheight * 0.5)
texy = ((d * blocks[texnum+1].tex_h) / rayheight)
if (texy >= blocks[texnum+1].tex_h-1) then
texy = blocks[texnum+1].tex_h-1
end
if (blocks[texnum+1].door) then
--Draw partial wall
texy = texy + mapget(mapx, mapy)
if(texy <= blocks[texnum+1].tex_h) then
ci = sget(blocks[texnum+1].tex_x+texx,blocks[texnum+1].tex_y+texy)
if(ci < 15) then
if(uselighting) then ci = darker(ci, flr(perpwalldist-1.5+(glow/2))) end
rectfill((x * xscale), (y * yscale), (x * xscale) + xdraw, (y * yscale) + ydraw, ci)
end
end
else
ci = sget(blocks[texnum+1].tex_x+texx,blocks[texnum+1].tex_y+texy)
if(ci < 15) then
if(uselighting) then ci = darker(ci, flr(perpwalldist-1.5+(glow/2))) end
rectfill((x * xscale), (y * yscale), (x * xscale) + xdraw, (y * yscale) + ydraw, ci)
end
end
end
end
end
function drawfloorstripe(x)
if(showfloor) then
--floor casting
floorxwall = 0.0 floorywall = 0.0 --x, y position of the floor texel at the bottom of the wall
--4 different wall directions possible
if(side == 0) then
if(raydirx > 0) then
floorxwall = mapx
floorywall = mapy + wallx
else
floorxwall = mapx + 1.0
floorywall = mapy + wallx
end
end
if(side == 1) then
if(raydiry > 0) then
floorxwall = mapx + wallx
floorywall = mapy
else
floorxwall = mapx + wallx
floorywall = mapy + 1.0
end
end
distwall = perpwalldist
distplayer = 0.0
if (drawend < 0) then
drawend = h -- becomes < 0 when the integer overflows
end
--draw the floor from drawend to the bottom of the screen
for y = drawend + 1,h+1,2 do
currentdist = h / (2.0 * y - h) -- you could make a small lookup table for this instead
weight = (currentdist - distplayer) / (distwall - distplayer)
currentfloorx = weight * floorxwall + (1.0 - weight) * posx
currentfloory = weight * floorywall + (1.0 - weight) * posy
floortexx = flr(currentfloorx * texwidth) % texwidth;
floortexy = flr(currentfloory * texheight) % texheight;
--floortexture = map.floorat(int(currentfloorx), int(currentfloory));
--ciellingtexture = map.ceilingat(int(currentfloorx), int(currentfloory));
floortexture = 0;
ciellingtexture = 0;
if(floortexture>-1) then
ci = sget((floortexture*8)+floortexx,floortexy + 8)
if(uselighting) then ci = darker(ci, flr(currentdist+0.5-glow/4)) end
rectfill((x * xscale), (y * yscale), (x * xscale) + xdraw, (y * yscale) + ydrawfloor, ci)
end
if(showceiling) then
if(ciellingtexture>-1) then
ci = sget((ciellingtexture*8)+floortexx,floortexy + 8)
if(uselighting) then ci = darker(ci, flr(currentdist+0.5-glow/4)) end
rectfill((x * xscale), ((h-y) * yscale) - ydrawfloor + 1, (x * xscale) + xdraw, ((h-y) * yscale) + 1, ci)
end
end
end
end
end
function raycast()
palt(0, false)
palt(15, true)
raylength = 0
texx = 0 texy = 0 d = 0
x = 0 y = 0
transparentthisframe = false
transparent_start = 0 transparent_end = w
for x = 0,w,2 do
sss[x+1] = 0 sss[x+2] = 0
drawstripe(x, 0)
drawfloorstripe(x)
end
-- sprite mapping
for i=0,numentities do
so[i+1] = i
spritedistance[i+1] = ((posx - e[i+1].posx) * (posx - e[i+1].posx)) + ((posy - e[i+1].posy) * (posy - e[i+1].posy))
end
combsort()
for i=0,numentities do
if(e[so[i+1]+1].active) then
spritex = e[so[i+1]+1].posx - posx
spritey = e[so[i+1]+1].posy - posy
e[so[i+1]+1].insight = false
invdet = 1.0 / (planex * diry - dirx * planey)
transformx = invdet * (diry * spritex - dirx * spritey)
transformy = invdet * (-planey * spritex + planex * spritey)
spritescreenx = flr((w / 2) * (1 + transformx / transformy))
if(e[so[i+1]+1].position == "bottom") then
udiv = 2 vdiv = 2
vmove = wallheight/2
elseif(e[so[i+1]+1].position == "top") then
udiv = 2 vdiv = 2
vmove = -wallheight/2
elseif(e[so[i+1]+1].position == "center") then
udiv = 2 vdiv = 2
vmove = 0.0
elseif(e[so[i+1]+1].position == "huge") then
udiv = 0.5 vdiv = 0.5
vmove = -wallheight/2
else
udiv = 1 vdiv = 1
vmove = 0.0
end
--parameters for scaling and moving the sprites
vmovescreen = flr(vmove / transformy)
--calculate height of the sprite on screen
spriteheight = abs(flr(h / (transformy))) / vdiv;
--using "transformy" instead of the real distance prevents fisheye
--calculate lowest and highest pixel to fill in current stripe
drawstarty = -spriteheight / 2 + h / 2 + vmovescreen
if(drawstarty < 0) then
drawstarty = 0
end
drawendy = spriteheight / 2 + h / 2 + vmovescreen
if(drawendy >= h) then
drawendy = h - 1
end
--calculate width of the sprite
spritewidth = abs(flr(64 / (transformy))) / udiv
drawstartx = -spritewidth / 2 + spritescreenx
if(drawstartx < 0) then
drawstartx = 0
end
drawendx = spritewidth / 2 + spritescreenx
if(drawendx >= w) then
drawendx = w - 1
end
--loop through every vertical stripe of the sprite on screen
if(dospritedraw(drawstartx, drawendx, drawstarty, drawendy, transformy)) then
sspr(e[so[i+1]+1].tex_x,e[so[i+1]+1].tex_y,e[so[i+1]+1].tex_w,e[so[i+1]+1].tex_h,drawstartx*2,drawstarty*2,(drawendx - drawstartx)*2, (drawendy - drawstarty)*2)
e[so[i+1]+1].insight = true
else
check = 0
for stripe = drawstartx,drawendx do
texx = flr((stripe - ( -spritewidth / 2 + spritescreenx)) * (e[so[i+1]+1].tex_w) / spritewidth)
if(texx > e[so[i+1]+1].tex_w-1) then texx = e[so[i+1]+1].tex_w-1 end
if(transformy > 0 and stripe >= 0 and stripe < w and transformy < zbuffer[flr(stripe)+1]) then
check = 1 --at least one stripe drawn
sss[flr(stripe)+1] = drawendy - drawstarty,drawendy
for y = drawstarty,drawendy do
d = (y-vmovescreen) - ((h + spriteheight) * 0.5)
texy = ((d * (e[so[i+1]+1].tex_h)) / spriteheight)
if(texy>-1) then texy = -1 end
ci = sget(e[so[i+1]+1].tex_x + texx,e[so[i+1]+1].tex_y + e[so[i+1]+1].tex_h + texy)
if(ci < 15) then
rectfill((stripe * xscale), (y * yscale), (stripe * xscale)+3, (y * yscale) + 1, ci)
end
end
else
end
end
if(check == 1) then e[so[i+1]+1].insight = true end
end
if(e[so[i+1]+1].insight) then
if(abs(transformx)>1.5 or transformy<0) then
e[so[i+1]+1].insight = false
end
end
end
end
-- check for transparent walls
if(transparentthisframe) then
for x = transparent_start,transparent_end,2 do
while(redrawstripe[x+1] > 0) do
drawstripe(x, redrawstripe[x+1])
redrawstripe[x+1] = redrawstripe[x+1] - 1
end
end
end
end
function dospritedraw(sx, ex, sy, ey, ty)
check = 0
if(ty>0 and sy>0 and ey<h and sx>0 and ex<w-1) then
for stripe = sx,ex do
sss[flr(stripe)+1] = ey - sy
if(zbuffer[flr(stripe)+1]>0) then
if(ty > zbuffer[flr(stripe)+1]) then
check = 1
end
end
end
else
return false
end
if(check == 0) then
return true
end
return false
end
function combsort()
gap = numentities
swapped = false
loopcheck = false
if(gap > 1) then loopcheck = true end
if(swapped) then loopcheck = true end
while (loopcheck == true) do
--shrink factor 1.3
gap = (gap * 10) / 13
if (gap == 9) then gap = 11 end
if (gap == 10) then gap = 11 end
if (gap < 1) then gap = 1 end
swapped = false
for i = 0,numentities-gap do
j = flr(i + gap)
if (spritedistance[i+1] < spritedistance[j+1]) then
tempn = spritedistance[i+1];
spritedistance[i+1] = spritedistance[j+1];
spritedistance[j+1] = tempn;
tempi = so[i+1];
so[i+1] = so[j+1];
so[j+1] = tempi;
swapped = true;
end
end
loopcheck = false
if(gap > 1) then loopcheck = true end
if(swapped) then loopcheck = true end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment