Working Raycasting example! WolfenLöve 3D if you will...

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
Jasoco
Inner party member
Posts: 3655
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Working Raycasting example! WolfenLöve 3D if you will...

Post by Jasoco » Thu Jan 26, 2012 2:11 am

Looking forward to comparing notes.

First one to create working doors that work like Wolfenstein is the winrar! Aaaaaannnnd.... GO!

I'm also working on floorcasting. But it's more difficult. I wanted to get it working and surprise everyone with it. Though it's probably so simple. It would be easier if Löve could distort an image into a non-parallelogram shape.

I didn't want to create an official thread for my engine until I had both doors and floors working. I already have enemies. Well, I have one that follows me around, but it has no clipping or AI, but I don't want to put time into AI until I'm sure I can get at least doors working.

Doors should be "easy" as Wolfenstein doors are simply a special block that instead of placing the wall right up against the rest, it places its wall recessed .5 units back and gives the two side walls a special side texture. Then when you open it, it simply draws it moving into the side wall at an offset, then makes the tile passable once opened, waits, makes it impassable and closes the animation. Sounds easy. Souuuunds easy.

I spent the past day watching videos of old Raycasting games like Wolfenstein, ROTT, Blake Stone, Nightmare 3D, Catacombs 3D, Shadowcaster, Walken (Ken's Labyrinth) and others. There were a lot of them back then. Half of them had floorcasting, and each one had different types of doors. I also read up on Ken Silverman's Build Engine which was used in Duke Nukem 3D and how he seeked advice from Romero when he was trying to design his engine.

jfroco
Prole
Posts: 24
Joined: Fri Jan 06, 2012 5:14 pm

Re: Working Raycasting example! WolfenLöve 3D if you will...

Post by jfroco » Thu Jan 26, 2012 2:23 pm

Hi,

I'm not big fan of "doors" in FPS games :) so I don't think I'm going to include it in the short term.

Regarding FPS videos, I recommend you this video series: http://www.youtube.com/user/Balgorg?feature=g-all-u Balgorg has published almost 50 videos with the history of FPS games.

Floorcasting on the other hand is serious business for me. I've tried several methods: half I could't make it work, half works at 1 frame per second.

So far, I have this.. I removed light effect:

Image

Best regards

JF

User avatar
Jasoco
Inner party member
Posts: 3655
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Working Raycasting example! WolfenLöve 3D if you will...

Post by Jasoco » Thu Jan 26, 2012 3:29 pm

That looks really neat. Also, I disagree about doors. I like them. They're very important for keeping enemies out when you don't want them in.

Also, I have a method I am going to use for "floorcasting" that runs at 300FPS but requires canvases. (Actually, my whole game requires extreme use of canvases so it'll probably not work at all on a few lower-end graphics cards sadly. But we'll figure that out when the time comes. Viva la canvas! Viva la freedom!)

Basically I need to use the same FOV I use for horizontal finding of walls and cast rays vertically from the players eye both up to the highest point you can see on the ceiling (For ceilings) and also down to the lowest point on the floor (For floors). I just need to figure out the calculating to cast the rays and figure out where along the ground canvas the rays hit so I know which quad of the ground canvas to draw at every pixel on the bottom and top halves of the screen.

It's all math, but me no good at many maths sometimes. Depending on whether I pre-calculate the rays (Meaning the eye level would never change including head bob) or calculate them every frame (If the head needs to move, say I jump or duck if I need those in the game) the FPS will either not suffer at all or possibly suffer a little because it will be casting up to either 200 or 400 rays every frame on top of the 320 or 640 it does now. (My game has both a classic 320x200 mode and a "High Resolution" mode of 640x400. Both of which are 16:10, but I plan on also getting in a 320x240 4:3 mode and a 320x180 16:9 mode so people can use fullscreen and not have bars. But that will come later. Technically the game could be as high resolution as you want it. I had it running 1440x900 meaning it had to cast 1440 rays every frame. It was crisp and awesome, but slow. Really slow. That's a lot of rays, and the raycasting is what takes the largest chunk of the rendering time. So cutting it down to 320 makes it run at 60FPS with cycles to spare. Of course, 320x200 looks really weird on a huge display. But then again, so would DOOM if run in DOSBox and not a modern day DOOM engine.

Also, Balgorg's YouTune channel isn't all that awesome. It's mostly modern FPS' and irrelevant ones to this conversation. CuteFloor's on the other hand is awesome. It's all classic DOS games including all the games I mentioned above and others:
http://www.youtube.com/user/CuteFloor

jfroco
Prole
Posts: 24
Joined: Fri Jan 06, 2012 5:14 pm

Re: Working Raycasting example! WolfenLöve 3D if you will...

Post by jfroco » Thu Jan 26, 2012 3:54 pm

Hello Jasoco,

Yes, I've already tested a similar method.

The problem with that method is that the total amount of rays is not going to be: 200 + 320 = 520, it is going to be: 200x320=64000.

Because for every vertical ray you will have to cast 200 rays for floor or ceil casting.

I could be wrong, so if you already implemented and runs at 300 frames per second, please let me know the magic :)

Best regards

User avatar
Jasoco
Inner party member
Posts: 3655
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Working Raycasting example! WolfenLöve 3D if you will...

Post by Jasoco » Thu Jan 26, 2012 8:34 pm

Nope. It's only one line for each line below and above the horizon because you simply scale the ground quad horizontally depending on distance. I'll explain later when I get off work.

jfroco
Prole
Posts: 24
Joined: Fri Jan 06, 2012 5:14 pm

Re: Working Raycasting example! WolfenLöve 3D if you will...

Post by jfroco » Fri Jan 27, 2012 12:49 am

Hi,

I don't see how can you cast a complex floor with just one cast per line.

This is what I have...

Image
http://dl.dropbox.com/u/4281970/love2d/raycast4.png

Please let me know what you are doing :)

Code: Select all

local player = {x=5,y=5,h=200,
			dir=0,rot=0,speed=0,moveSpeed=4,rotSpeed=60}

local map = {}
local miniMapScale = 12
local mapScale  = 512
local screenWidth = 800;
local screenHeight = 600
local stripWidth = 2;
local stripHeight = 4;
local fov = 60 * math.pi / 180;
local numRays = math.ceil(screenWidth / stripWidth);
local fovHalf = fov / 2;
local viewDist = (screenWidth/2) / math.tan((fov / 2));
local twoPI = math.pi * 2;
local fy0 = screenHeight * 0.6

local function pdist(y)
	return viewDist*player.h/(y-screenHeight*0.5)
end

local function celda(x,y)
	fx =math.floor(x)
	fy=math.floor(y)
	if fx>10 or fy>10 then return 0 end
	if fx<0 or fy<0 then return 0 end
	return map[fx][fy]
end

local function drawHero()
	love.graphics.setColor( 255,255,0)
	love.graphics.rectangle( "fill", 
					player.x * miniMapScale-2,
					player.y * miniMapScale-2,
					miniMapScale*0.5,miniMapScale*0.5
				)
	love.graphics.line(player.x * miniMapScale, player.y * miniMapScale,
				(player.x + math.cos(player.rot) * 4) * miniMapScale,
				(player.y + math.sin(player.rot) * 4) * miniMapScale
				);

end

local function drawMiniMap()
	local wall 
	for x=0,10 do
		for y=0,10 do
			wall=celda(x,y)	
			if (wall > 0)  then
				love.graphics.setColor( wall,wall,wall)
				love.graphics.rectangle( "fill", 
					x * miniMapScale,
					y * miniMapScale,
					miniMapScale,miniMapScale
				)
			end
		end
	end
end

function love.load()
	--valor = 1
	for i=0,10 do
		map[i]={}
		for j=0,10 do
			valor =math.random(0,255)
			map[i][j]= valor
			--valor = 1-valor
		end 
	end

	-- for i=0,10 do
		-- for j=0,10 do
			-- print(i,j,celda(i,j))
		-- end
	-- end
	
end

local function castVerticalFloorRay(x,angulo)
	for y=fy0,screenHeight,stripHeight do
		local coseno = math.cos(angulo-player.rot)
		if coseno ==0 then coseno=0.0001 end
		local dist = (pdist(y)/coseno)/mapScale
		local px = player.x + math.cos(angulo) * dist
		local py = player.y + math.sin(angulo) * dist
		local piso = celda(px,py)		
		if piso>0 then
			love.graphics.setColor( piso,piso,piso)
				love.graphics.rectangle( "fill", 
					x * stripWidth,
					y ,
					stripWidth,1
				)
		end
	end
end

local function floorCasting()
	for i=0,numRays-1 do
		local rayScreenPos = (-numRays/2 + i) * stripWidth;
		local rayViewDist = math.sqrt(rayScreenPos*rayScreenPos + viewDist*viewDist);
		local rayAngle = math.asin(rayScreenPos / rayViewDist);
		castVerticalFloorRay(i,player.rot+rayAngle)
	end
end


function love.draw()
	floorCasting()
	drawMiniMap()
	drawHero()
end

function love.keypressed(key)
   if key == "up" then
      player.speed = 1;
	  return;
   end
   if key == "down" then
      player.speed = -1;
	  return;
   end
   if key == "left" then
      player.dir = -1;
	  return;
   end
   if key == "right" then
      player.dir = 1;
	  return;
   end
end

function love.keyreleased(key)
   if key == "up" then
      player.speed = 0;
	  return
   end
   if key == "down" then
      player.speed = 0;
	  return
   end
   if key == "left" then
      player.dir = 0;
	  return
   end
   if key == "right" then
      player.dir = 0;
	  return
   end
end



local function isBlocking(x,y)
	return false
end

local function move(dt)
    local moveStep = player.speed * player.moveSpeed*dt;	-- player will move this far along the current direction vector
	player.rot = player.rot + player.dir * dt*player.rotSpeed * 3.1415169 / 180; -- add rotation if player is rotating (player.dir != 0)
	local newX = player.x + math.cos(player.rot) * moveStep;	-- calculate new player position with simple trigonometry
	
    if not (isBlocking(newX, player.y)) then
        player.x = newX; 
    end
	
	local newY = player.y + math.sin(player.rot) * moveStep;
	if not (isBlocking(player.x, newY)) then
           player.y = newY;
    end
end

function love.update(dt)
	move(dt)
end

User avatar
Jasoco
Inner party member
Posts: 3655
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Working Raycasting example! WolfenLöve 3D if you will...

Post by Jasoco » Fri Jan 27, 2012 1:25 am

While I get up the resources to create some screenshots and examples, I will explain it..

First I have a Ground canvas. I draw the ground into this at full resolution. (Say each tile is 64x64 pixels.) This canvas is the maximum size it can be at most the furthest distance you will be able to see.

Second, there's a Ground Container canvas. The game draws the Ground onto the Ground Container with the rotation being the players direction angle and the offsets being the players X and Y * 64 (The tile size since X and Y will be a decimal number, but the image is much bigger.) and its drawing position being half the width of the container and the full height of the container. This will give you the basis for the ground being laid out in front of you.

I create quads from the Container canvas at every pixel from bottom to top. Since 0.8.0 can do quads on canvases, even if the canvas changes.

I just need to calculate TWO things...

ONE being the distance of the furthest wall is the starting point. I need to figure out where on the GROUND CONTAINER is the distance from the player to that wall * 64. Then I need to trace it back one pixel at a time from the furthest walls bottom pixel location + 1 all the way to the bottom of the screen. (Do a similar thing to get the ceiling if needed.)

TWO being the scale of each quad depending on its distance. This is the easy part as it's as simple as using the same calculations used to calculate wall drawing height and sprite scale.

So basically, ALL I NEED TO DO is cast rays from the furthest wall's location all the way to the bottom of my eye line then use the appropriate quad for that location. If done correctly it will be close enough to looking like Mode 7. Sure there'll be graphical weirdness on edges of textures, but it won't matter since it'll be FLOORCASTING! And so much nicer than just solid colored ground.

In other words, it just ends up being "Here's all the pixels from the bottom of the furthest wall all the way to the bottom of the game screen. Now figure out the distance each pixel along that line is from the player." If ANYONE can help figure that out, it would be awesome as pie.

User avatar
Jasoco
Inner party member
Posts: 3655
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Working Raycasting example! WolfenLöve 3D if you will...

Post by Jasoco » Fri Jan 27, 2012 2:26 am

Only double-posting to keep both posts from being extremely cluttered. Here is an image diagramming my problem and progress...

Please view the entire image if you can't see it all. It is 1100 pixels wide.
Image

Assumedly, if I can A) figure out the angle that my eye's bottom pixel is from my "eye level" and B) the angle of the bottom pixel of the furthest floor strip I can then cast a ray at each of those pixels, in this case 105 rays and find out their distance from the player and thus which quad to use and how small to scale them.

You'll also note that the players eye level is not .5 as it was in the demo code, I changed it to make it make more sense. If it is .5, your eyes are right up at the center of the wall and thus right at the enemy pictured above's heart. This is silly and never really made sense in Wolfenstein and clones because it meant the player is a short little dude when he's so obviously the same size as all the normal enemies. So I changed it to .37 (Which inverted means my eyes are .63 units off the ground.) The ground code will have to be able to account for this.
Last edited by Jasoco on Fri Jan 27, 2012 2:32 am, edited 1 time in total.

jfroco
Prole
Posts: 24
Joined: Fri Jan 06, 2012 5:14 pm

Re: Working Raycasting example! WolfenLöve 3D if you will...

Post by jfroco » Fri Jan 27, 2012 2:29 am

Sorry to hear that... you have explained exactly what I'm doing, please see the code, but it seems that you need to implemented by yourself to realize that in order to figure out the distance each pixel along every line you need to cast nxm rays.... but it's OK...thank you for taking the time to explain.

Wait... Quads on the fly on rotated canvas.?.. It could work... It could be good reason to move from 0.7.2 to 0.8
Last edited by jfroco on Fri Jan 27, 2012 2:38 am, edited 1 time in total.

User avatar
Jasoco
Inner party member
Posts: 3655
Joined: Mon Jun 22, 2009 9:35 am
Location: Pennsylvania, USA
Contact:

Re: Working Raycasting example! WolfenLöve 3D if you will...

Post by Jasoco » Fri Jan 27, 2012 2:37 am

No, your method is quite a bit different. You seem to be drawing each ground pixel every single wall strip. As you said making it a LOT of pixels being drawn.

What I am doing is entering the 21st century and rotating the image beforehand, then using quads. I will only need at maximum 200 to 400 draw calls and ray calculations instead of casting a ray and drawing hundreds of pixels each time.

Your method seems to be following the old-school method DOOM and other RC games used of drawing the ground one pixel at a time. I am using the power of rotation and quad splitting to speed it up a notch. BAM!

Edit: NOW you get it! Now yer on the trolley! Your example is choppy because you're drawing every pixel. Mine is 60+FPS (Vsync is on) because it only has to do 200-400 casts a frame. A lot faster. I JUST need to figure out the math to calculate the angles to start from and end on. It's SO SIMPLE and SO WITHIN MY GRASP I can FEEL it! I'll be the hero of the internet and share my awesomeness with everyone here that wants to make a 3D raycasted game.

In other words, I bet you have the right idea. You only need to cast rays from the furthest wall's position back to the player assuming that that distance is directly in front of you. Once you have the numbers to work with, you just cast the rays back to your eye's bottom.
Last edited by Jasoco on Fri Jan 27, 2012 2:43 am, edited 3 times in total.

Post Reply

Who is online

Users browsing this forum: No registered users and 44 guests