I just read part of the "Sin & Cos: The Programmer's Pals!" tutorial by amarillion. I previously didn't know how to apply these functions to movement, but I do now!! (BTW: Great tutorial!!!)
Well, after experimenting with them and making a simple "game" to test them out, I've decided to do a raycasting engine. I just want it simple like Wolfenstein 3D's engine. I've got some code "working", but there's a problem with the displaying of the level, it's really blocky, and it doesn't look like walls or anything, just big straight blocks. Ther is some difference in heights and such, but I can't figure out where corners are and stuff. Like I said, I'm new to using angles and sin&cos and stuff like that in my programs, so maybe that's one of the problems, too. Here's the display/casting code:
1 | /* The view variable is declared like this: |
2 | * int view[85], and when the rays are casted, the distances are stored here. |
3 | * The following code is the cast function, the |
4 | * raycasting handler function and the drawing. |
5 | */ |
6 | |
7 | int cast_ray(int x,int y,float a) |
8 | { |
9 | int distance=0; |
10 | int the_x=x; |
11 | int the_y=y; |
12 | while(level[the_y/TILE][the_x/TILE]!=1) |
13 | { |
14 | the_x+=(int)cos(a); |
15 | the_y+=(int)sin(a); |
16 | distance++; |
17 | } |
18 | return distance; |
19 | } |
20 | |
21 | void do_raycasting() |
22 | { |
23 | float angle=pa-42; |
24 | for(int i=0;i<85;i++) |
25 | { |
26 | view<i>=cast_ray(px,py,angle); |
27 | angle++; |
28 | } |
29 | } |
30 | |
31 | void draw_view() |
32 | { |
33 | int tall=0; //Height of a line (represents distance of ray |
34 | int distance_from_top=0; //So it's centered. |
35 | int color=255; //This is for depth shading (not implemented yet) |
36 | for(int i=0;i<85;i++) |
37 | { |
38 | //This area is to calculate how tall the line should be |
39 | //There's probly an error here... |
40 | if(view<i><200) //This is so it has a distance limit |
41 | { |
42 | tall=200-view<i>; |
43 | distance_from_top=(200-tall)/2; |
44 | //This draws the line, I don't think this is a problem |
45 | line(front,i,distance_from_top,i,distance_from_top+tall,makecol(color,color,color)); |
46 | } |
47 | } |
48 | } |
If that's not enough to find the problem, here's the whole program's code:
1 | #include <allegro.h> |
2 | |
3 | #define TILE 64 |
4 | |
5 | int level[5][5]={ |
6 | {1,1,1,1,1}, |
7 | {1,0,0,0,1}, |
8 | {1,0,0,0,1}, |
9 | {1,0,0,0,1}, |
10 | {1,1,1,1,1}}; |
11 | |
12 | int view[85]; |
13 | |
14 | int px=160; |
15 | int py=160; |
16 | int ps=5; |
17 | float pa=64.0; |
18 | |
19 | BITMAP*front; |
20 | |
21 | int cast_ray(int x,int y,float a); |
22 | void draw_view(); |
23 | void do_raycasting(); |
24 | |
25 | |
26 | int main() |
27 | { |
28 | allegro_init(); |
29 | install_keyboard(); |
30 | set_color_depth(16); |
31 | set_gfx_mode(GFX_AUTODETECT,320,200,0,0); |
32 | front=create_bitmap(320,200); |
33 | while(!key[KEY_ESC]) |
34 | { |
35 | clear(front); |
36 | do_raycasting(); |
37 | draw_view(); |
38 | if(key[KEY_LEFT]) pa-=1; |
39 | if(key[KEY_RIGHT]) pa+=1; |
40 | if(pa<0) pa+=255; |
41 | if(pa>255) pa-=255; |
42 | if(key[KEY_UP]) |
43 | { |
44 | px+=(int)(ps*cos(pa)); |
45 | py+=(int)(ps*sin(pa)); |
46 | } |
47 | if(key[KEY_DOWN]) |
48 | { |
49 | px-=(int)(ps*cos(pa)); |
50 | py-=(int)(ps*sin(pa)); |
51 | } |
52 | line(front,100,100,100+(25*cos(pa)),100+(25*sin(pa)),makecol(255,255,255)); |
53 | vsync(); |
54 | blit(front,screen,0,0,0,0,320,200); |
55 | } |
56 | destroy_bitmap(front); |
57 | return 0; |
58 | } |
59 | |
60 | int cast_ray(int x,int y,float a) |
61 | { |
62 | int distance=0; |
63 | int the_x=x; |
64 | int the_y=y; |
65 | while(level[the_y/TILE][the_x/TILE]!=1) |
66 | { |
67 | the_x+=(int)cos(a); |
68 | the_y+=(int)sin(a); |
69 | distance++; |
70 | } |
71 | return distance; |
72 | } |
73 | |
74 | void do_raycasting() |
75 | { |
76 | float angle=pa-42; |
77 | for(int i=0;i<85;i++) |
78 | { |
79 | view<i>=cast_ray(px,py,angle); |
80 | angle++; |
81 | } |
82 | } |
83 | |
84 | void draw_view() |
85 | { |
86 | int tall=0; |
87 | int distance_from_top=0; |
88 | int color=255; |
89 | for(int i=0;i<85;i++) |
90 | { |
91 | if(view<i><200) |
92 | { |
93 | tall=200-view<i>; |
94 | distance_from_top=(200-tall)/2; |
95 | line(front,i,distance_from_top,i,distance_from_top+tall,makecol(color,color,color)); |
96 | } |
97 | } |
98 | } |
I'm almost certain it's a problem with the display or the casting function, but I'm not sure.
the_x+=(int)cos(a);
This is equivalent to:
if(a == M_PI) x--;
if(!a) x++;
i.e. cos(a) only varies from -1 to +1, so the (int) essentially throws all the information away.
So do I need to have the x&y floats or fixeds or doubles? As I said all the movement and angle code worked in my other "game" test. Well, why would it matter? You can't put a point at a non-integer coordinate anyway, so I don't need that extra information, right?
Yes, you have to have the x&y float or fixed or double. Trust us, it matters.
Think about it. cos(a) and sin(a) are going to be the same thing every time you calculate them. So, if your x increment for a particular ray is either -1, 0, or 1 all the time, and your y increment is either -1, 0, or 1 all the time, any rays you cast will automagically be clamped to 45 degree increments, which is definitely not what you want.
P.S. You can precalculate the values of cos(a) and sin(a) instead of calling those functions repeatedly in your raycasting loop. It will make for a huge speed increase.
So instead of haveing it shoot a ray out at 1 degree increments, it shoots them out a t 45 degree increments? That explains the blockyness of the output. So, I need to have the_x & the_y be floats, to get an accurate reading, right?
You can precalculate the values of cos(a) and sin(a) instead of calling those functions repeatedly in your raycasting loop. It will make for a huge speed increase.
I think I'll probly do this later, but for now, I'll just call it every time. I don't think its a big problem, cuz it runs fine on my old 586 computer. But, how is this done? I'm guessing just by filling something like float sin_table[85] or something, right? How much would I increment it for each part of the table? 1 or .1 or somthing?
float sin_table[85]; for(int i=0;i<85;i++) { sin_table<i>=sin(i); }
?
the allegro functions fixsin and fixcos are already implemented using tables.
Trust me, don't worry about optimization too much at first. Just get it working first, optimize later.
If you aren't getting any compile errors or runtime errors, i would say try a different, more complicated level. Probably whats happening is that the level is too simple, so naturally all you see is blocks. [edit] or not I really need to speed up my typing [/edit]
Also what you might want to do is modify your code a bit so that walls can be different colour so that you can differentiate them. All that would take to do that would be to make your level array with different values that one, then you would change your ray casting code, and add another array parallel to 'view' which contains colour information:
1 | ... |
2 | int level[5][5] = |
3 | {{ makecol(255, 255, 0), makecol(255, 0, 255), /* ... etc */ }}; |
4 | |
5 | int view[85]; |
6 | int colinfo[85]; |
7 | |
8 | ... |
9 | |
10 | int cast_ray(int *x,int *y,float a) |
11 | { /* ^ ^ */ |
12 | ... |
13 | while(level[the_y/TILE][the_x/TILE]==0) |
14 | { /* ^ */ |
15 | ... |
16 | } |
17 | |
18 | *x = (the_x/TILE); /* <-- */ |
19 | *y = (the_y/TILE); |
20 | return distance; |
21 | } |
22 | void do_raycasting() |
23 | { |
24 | float angle=pa-42; |
25 | for(int i=0;i<85;i++) |
26 | { |
27 | tx = px; ty = py; |
28 | view<i>=cast_ray(tx,ty,angle); |
29 | colinfo<i> = level[tx][ty]; |
30 | angle++; |
31 | } |
32 | } |
33 | |
34 | void draw_view() |
35 | { |
36 | ... |
37 | for(int i=0;i<85;i++) |
38 | { |
39 | if(view<i><200) |
40 | { |
41 | ... |
42 | line(front,i,distance_from_top,i,distance_from_top+tall,colinfo<i>); |
43 | } |
44 | } |
45 | } |
just a suggestion. I don't know how far you intended to go with this.
Image...
Yay!
As you can see, I've got it displaying right (changed the_x&y to floats), and it has a primitive form of depth shading! (The line in the middle is just to show the direction you're facing.) Also, you'll notice that it's got that nasty curved effect. How do I fix that?
Well, it is because raycasting doesn't work the way you think it works. It's hard to explain but you are in the wrong line of thinking. Try to see if you can find other raycasting tutorials.
What's wrong with my line of thinking?
Well, I've read another nice tutorial, and it says to fix the fishbowl effect, multiply the distance by cos(BETA) and beta is -30 (since I'm using the 255 degree circle, -21) for the left-most ray, and 30 (21) for the rightmost ray. I've solved this by doing distance*=cos(0-a); and it works. But, the angle should be between -21 and 21, but in reality, it can be anywhere between two numbers that are 42 different. SO, the effect I get is that when you turn 90 degrees (64 allegro-degrees) you get an incorrect reading! I think I'm stuck...
aybabtu: Try changing 0 to the direction you're facing so that 0-a is between -21 and 21 regardless of how offset a is. e.g. if you turn 64 alleg-degs then you multiply distance by cos(64-a).
Anyway, you should really try to get a grasp of the trig underlying what you're doing ...
I think this calls for some smiling art:
:-X:(
:-X:-X:-*:-X:-X:(
:-X:-*:-X:-X:-X:(
;D8-)8-)8-)8-):(
:-X == Whitespace
:-* == Distance you calculate
8-) == Distance you should calculate, also your line of sight
;D == You
:( == Wall
The size of each bit of wall you draw is determined by the distance parallel to your line of sight that you'd need to walk to get to that wall -- perpendicular distance should not "shrink" the wall. How do we find that parallel distance? Well, if we look at the diagram above as a right-triangle, we see that we already have the length of the hypotenuse and the angle, and we can apply simple trig to find that that parallel distance is hypot*cos(angle) ... exactly the formula you're using.
Here you have a raycast tutorial. It explains what's happening with that nasty curved effect and how to prevent it.
Let me know when you release this project, please;).
You see? That other tutorial is much better. You see, mine is not really intended as a raycasting tutorial, just a trig primer.
amarillion: Yeah, I know. I've learned a little trigonometry in my geometry class at school, but we never learned really how to apply it to a computer game ! But, even though your tutorial is not for raycasting in particular, it was what I was missing to at least get started. After all, at least it's farther than I've ever gotten before.
Niuino: Yeah! That tutorial's great, it's the same one you linked to in my other raycasting thread. I'm trying to use that method, but it's not working very well. I've gotten rid of some of the fishbowl, but in some parts, it's actually reversed! Like, I'll look at a flat wall, and it will have slight curvature to it, but instead of the middle being larger, the ends are. Here's the code I'm using to "fix" the distance problem:
void do_raycasting() { float angle=pa-21; for(int i=0;i<42;i++) { view<i>=cos(i-21)*cast_ray(px,py,angle); angle++; } }
This is what the tutorial says to do, right?
Thus to remove the viewing distortion, the resulting distance obtained from equations in Figure 17 must be multiplied by cos(BETA); where BETA is the angle of the ray that is being cast relative to the viewing angle. On the figure above, the viewing angle (ALPHA) is 90 degrees because the player is facing straight upward. Because we have 60 degrees field of view, BETA is 30 degrees for the leftmost ray and it is -30 degrees for the rightmost ray.
correct_distance=distorted_distance*cos(BETA);
Of course, I changed 30 degrees into it's equivalent (about 21) in allegro degrees.
Maybe I'm doing the formula wrong, but it seems it should work, I've even got it in the do_raycasting function, where the anlge measures are between -21 & 21.
Let me know when you release this project, please.
Hmmm...I don't know even what I'm going to do with it yet! Probly a shooter like Wolfenstein, but between then and now, I've gotta get it working right! My next task is texture mapping. goes to the tutorial...
view<i> = cos (i-21) * cast_ray (px, py, angle);
That cos function, doesn't it get the angle in radians? But you're using binary angles (0-255), don't you? Try with fixcos or fcos, and do not forget to translate it to fixed point.
If you go to http://www.neozones.com and search a bit, you'll find some raycasting tutorials, they're in qbasic but I've made some advancements in c++.
I attach my work and the original tutorials.
Enjoy!
Well, given that the cos has worked as well as it has, I'd guess that aybabtu hasn't #included math.h and the allegro functions are stepping in automatically, so that isn't the problem. However it would be a good idea to follow Niunio's advice all the same so that the eventual possible #include math.h won't break all your math.
Anyway, the function looks right too me, so if Niunio isn't on to something then all I can guess is that there's something minorly wrong with "cast_ray()" ... perhaps the your steps are too big so the granularity is causing some small error in the drawing? How significant is the distortion?
Here's a pic with my crappy texture mapping and distortion correction working:
[url http://www.angelfire.com/empire/jonhome/WOLF.gif]Image...[/url]
As you can see, there's a few problems in the texture mapping, but it's ok for now. But, you'll notice the distortion in that cube.
Niuino: As Zaphos said, I don't have math.h included, so allegro's helping me out! I could change them to radians, but this works. Later when I get all the bugs worked out of it, I'll make all the code "the way it should be".;D
Trezker: Yeah! Even if they're in QBASIC, I know enough (not a lot still) to see how they work, and implement that code in C.
Zaphos: Um...as for the distortion...it sorta looks like a reverse-fishbowl effect. Instead of the distortion being on the sides, it's in the middle. And, I think I may know the reason...maybe. Maybe because 30 degrees isn't exactly equal to 21 allegro degrees, but 21.25 rather. So, the view is being cut off by half a degree, possibly screwing it up. Actually, it shouldn't do that kind of damage, just make the screen smaller...oh well.
Here's the code for the program...
I've got the ability to switch on and off the texture mapping and distortion correction, as well as a custom circle function for the "compass"! (The circle function isn't the best, it's not the one from the tutorial, but it works, and it's neat to see it...you know...whatever...::))
(Note: if you want to compile this or anything, you need a 64x64 texture bitmap for the walls...duh!:P)
EDIT: In the files that Trezker attached, I found this:
So, I need to construct a table like ViewFix, then multiply the reading from cast_ray by that value that corresponds to the angle, right? Or is that exacly what I'm doing...?
I would actually suggest implementing the faster, more accurate method of detecting walls, explained on this page of Nuino's linked tutorial, as I think the distortion may be caused by your current distance calculations (which are only of mediocre accuracy ...)
Zaphos: Yup. I'm working on making it so that the do_raycasting fuction shoots out 320 rays, 1 for each pixel on the screen, which will eliminate all bugs in the texture mapping, and possibly fix some of the distortion. It shouldn't take too long...
BTW: That qbasic fix for the distortion didn't hardly help at all...:-/ just a little. Oh well...
I'm working on making it so that the do_raycasting fuction shoots out 320 rays
I wasn't talking about that, but rather the way in which you calculate the distance, but then again for a large enough value of TILE using the method in that tutorial will probably be more of a speed boost than an accuracy one.
Perfect texture mapping, courtesy of 320 rays being cast:
[url http://www.angelfire.com/empire/jonhome/WOLF.gif]Image...[/url]
But, 320 rays is A LOT of math per frame!! If I hold down an arrow key, it's at least .75 seconds between frames/moves. How do I display the exact fps? I belive the floating point numbers are causing part of the slowdown (but fixed looks like it'd be too hard to convert to from it's current state...), but another problem is that it does a lot of math! That optimization that Zaphos showed me would be great, but I'm afraid I don't exactly understand it. I'll look at it some more, but any help would be greatly appreciated!
I modified the code a lot:
Ack...::)
EDIT: I have a theory about the distortion. It's compensating too much. Like I said, before, the middle was bulged out. Now, the middle is right, but the sides are bulged. This would lead me to belive that it's compensating too much for the distortion! Maybe:
int distance=(int)cast_ray(px,py,angle); int compensated=distance*cos((i/7.529411765)-21.25); int nice_distance=(compensated+distance)/2;
I know, this would give me the average. But, from what it looks lke, this would produce straight walls! I dunno, I'll try it...probly won't be the solution, though. But, hey! Anything's worth a try!
Heh; that distortion is funny I stole your code, so I figure I give you a hand. FPS is pretty low for me too; less than 10fps. I notice it gets pretty fast when a wall is right in your face though, so maybe your engine is wasting its time finding far walls? Comment your code some and maybe I could understand it
I'm going to play with it some; I'm bored ...
EDIT: The tutorial Niunio linked to states (correctly) that you can speed up calculations using bitshifts. A quick Find reveals you aren't doing any bitshifting in your engine; only multiplication and division ... I just tried it, and not much difference ...
EDIT2: I found a major problem; in map_texture(), these two lines:
Do you have any clue how you're inching along there? Those are small values; try this instead:
Not sure how correct that is, but it's a nice healthy speed boost ...
EDIT3: Eek! cast_ray() is pretty bodgy; I gotta figure out how to rewrite that (see Niunio's link again).
Do you have any clue how you're inching along there? Those are small values; try this instead:
Okay!
But, most of the values that come out of cos(a) are close to -1 or 1 ! So, multiplying by 64, wouldn't that give it less accuracy. But, I guess less accuracy is better than my 1 fps. Wait...I just did some math...
cos(-30)*64 is about 55.425, so isn't that a LARGE step? My math could be wrong. Did you actually try that *64 thing? If so, how does it work out for you?
EDIT: Commented code for 23:
It worked well, actually. But I've been working on a "proper" version using the math on that site (thread bump is t3h win, btw) and here's my best effort so far. In the plus column, it's blazingly fast; in the minus column I think I'm screwing up the math and I'm too stupid to figure out where It looks like a mess if you try it, but actually about half the walls are being drawn correctly and the other half aren't. The program also tends to crash on me if the angle I pass to tan() is undesirably too close to a multiple of 90 degrees, which has a bad hack to fix. Anyone want to give us some pointers here?
Yeah, I know, it sucks I'm getting the math for CastRay() from [url http://www.permadi.com/tutorial/raycast/rayc7.html]here[/url], btw ...
EDIT: Found a bug and fixed it above, but still needs work; sucker's gone back to crashing depending on location and angle >_< I'm done with it for now ...
I'll try to compile it today...
BTW: I editted in some commented code to that last post...
I once wrote a raycasting engine (many a moon ago). I had the same speed problem. 23yrold is going in the right direction, take larger steps at first, until you know your going to hit something, then when collision has been verified, you can actually calculate the point of collision from there.
My method involved a tile based system. Each "tile" (or block) was fixed size (I used 128 which was also the texture width and height). I only stepped the tiles, so it went pretty fast. When the tile was hit I did a little extra trig to find out the horizontal (x) coord of the surface it hit which then went into texture mapping.
If you want your textures to look even better I would suggest using some kind of mip-mapping scheme. Since your already sampling the texture in a linear fashion (vertically) it would simply be a matter of summing non-visible texels around the visible one and taking an average.
Also I would suggest you switch to fixed point math. I'm not a big proprieter of fixed point since (in most cases) floating point is not as slow anymore, but converting floating point to integer is (on many cpu's). In the core of loop you are converting the step values to integers so that they can index the map array.
this:
x_tile = x_tile_step >> 16;
y_tile = z_tile_step >> 16;
if(map[x_tile][y_tile] == EMPTY)
/* do stuff */
is much better than this:
x_tile = (int) (x_trig_step / MAP_SIZE);
y_tile = (int) (y_trig_step / MAP_SIZE);
if(map[x_tile][y_tile] == EMPTY)
/* do stuff */
I wish I still had that source code for raycaster. It ran swift on my POS 100mhz laptop. And it was light too, only about 30 lines or so in the core loop.
Thanks for the tip about fixed, MC, but I'll worry about just getting it working for the moment
Here's my latest attempt. It stopped crashing as soon as I added the fstream to log certian values; it's almost like it knows I'm watching it But it doesn't crash, and most of the walls work, but there's always the odd one sticking out. Plus the strafing and moving don't always go in the right direction. I still have no idea what I'm doing (other than waiting for a math guru to show up ). I'm going back to my platformer ...
EDIT: Wow I thought I was doing pretty well until I started wandering around the map. Depending on the angle and how you move, it either works really well or absolutely horrid
Well, I sped it up a lot by merging the cast_ray() and map_texture() functions into one, inside the for() loop in do_raycasting. This give a substantial speed increase, but it's still very slow (about 2 frames per second). I still don't have the distortion fixed. Maybe I should rewrite it...I only have about 100 lines of code, it shouldn't be too hard to fix it up, though...
Did you try mine? It's still buggy, but I think my method is more on the right track than yours.
Now where's that math pro ...
Did you try mine? It's still buggy, but I think my method is more on the right track than yours.
I just saved it to a disk so I can take it to my room and compile it. I'm sure yours is better than mine, as your A) More experienced than me...and B) Used that tutorial more (I've only used it so far to do texture mapping and "fix" the distortion).
Now where's that math pro ...
We need to lure some of those people from "What is 0?" thread...:P
I've never done a raycaster and I only just read the tutorial. You're doing something totally different
Well...help me fix it! This: "You're just doing something totally different " doesn't really help me, does it!? Whatever...I'm not a very "mathy" person. If I was, I bet I could fix this up very nicely. But, I'm not, so this is what I come up with...
I'm going to change most of the floats in cast_ray to fixed point. Also, contributing to my problem is the fact that I do 320 stretch blits every frame. The drawing alone is probly enough to slow it down substantially.
Wait! I think I see an advantage to yours right now! Your finding the X_inc and Y_inc once every ray, that way there isn't so much math being done!! Yay! I think that makes much more sense! Happy day!8-)
Well...help me fix it!
Help me fix mine and I'll help you fix yours
aybabtu: your code won't compile here.. I keep getting the error "conversion from `float' to non-scalar type `fix' requested" over and over again.
There's a raycasting section in "Tricks of the Game Programming Gurus" by Andre LaMothe, if any of you have it or can get it. If either of you can't get access to it, I can type of the code for the examples and post it here (I have no clue where my cd is, grrrrr).
I think I'm going to join in on the action and do a small raycasting game for the upcoming RPGDX como.
- Ace
If you (or anyone) could post a small example, that would be dandy. If it's any easier, you could just point out the error in my code, since it's pretty close to the desired effect ...
Eeek, well I'm not really sure how they work. I'll try to get the example's code typed up. It may help you out some and maybe I can figure it out as I type it up.
- Ace
Yay! I have no more speed problems!! I changed my raycasting code so that it doesn't redo the math to move the ray every loop. I get at least 5-10 fps, which is a great improvement. I know it's not good, but it's a lot better. Help: How do I get/display the fps?
Kitty Cat: Sorry...I don't know how to fix that...possibly because of the lines that say (float)cos(angle); My code isn't exactly "right".
I think I'm going to join in on the action and do a small raycasting game for the upcoming RPGDX como.
What's the "RPGDX com[p]o?" RPG competition?
23: Those sites are good. I remember t3h second one from when I was just starting out programming in QBASIC (they were way over my head:P).
Heh. Como. I've been The King of Typos lately. Blech. The RPGDX comPo is a weekend competition coming up on the weekend of the 28th. You have the weekend to design an RPG. You can use preexisting code, but everything else has to be done during the weekend.
Check out RPGDX for more information.
- Ace
How do I get/display the fps?
Have a variable that counts the number of screen redraws (just increment the variable everytime you blit the buffer to the screen). Declare an int fps variable which will hold the fps. Finally, have a timer variable increment every second.
Now, display fps onscreen. Whenever the second timer increments, assign the variable counting screen redraws to fps, and reset it. So you have a timer counting the seconds, a variable counting the frames drawn, and a variable to assign the fps so you can display it onscreen. Geddit?
Geddit?
Yup! That's simple.
Ace: How are you going to use raycasting in your RPG? Are you going to make one of those "3D" dungeon things? Although, yours wouldn't be as fake as those, because they just use predrawn images.::)
EDIT:
Ace: That's a cool site! (RPGDX):D
The only thing I can think of to increase the speed of the program is to use a lookup table (which I think has been already suggested earlier)
Although, if you just do this once, it shouldn't be to hard...
Some other possiblities of things that you could do to increase speed is... lets see...
You could eliminate the floats/doubles. I'm pretty sure that if you used FIXED point stuff it would run faster (just because of some of the CPU opimizations behind it).
You might even be able to just get a fixed accuracy or something by eliminating the decimal part of the return value. I mean, when you get right down to it, I don't think you are really going to need much beyond the thousands decimal place, maybe even the hundreths...
You could also implement some max viewing distance (as suggested in the tutorail) just to prevent excessive calculation.
<u><b>*NO DISTORTION*</u></b>:D8-);D:-*:o:):P;)
It was my displaying!!!!! I changed mine to 23's way to get the "tall" variable, and it works great!
BTW: 23, I ran your program, and I see what you're talking about. It looks nice, but I couldn't figure out what's causing that bug. Does it do the same thing without texture mapping? Hmm...I'm not very experienced, so I'm not the one to go to.
It was my displaying!!!!! I changed mine to 23's way to get the "tall" variable, and it works great!
I got the math for that from that one site up there, btw You should have had that a long time ago
Yeah, it's the same without texture maps, since it's the walls that move, not the textures. I'll probably hack at it for fun tonight, maybe look at that Java engine (how hard could it be to understand? ) and see where I get. Still wouldn't mind a math guru saving me some time though (wink, wink).
I got the math for that from that one site up there, btw You should have had that a long time ago
Why didn't you guys point it out sooner? Too obvious? Well, the important thing is that I can move on now .
maybe look at that Java engine (how hard could it be to understand?
It seems it could be almost directly ported to C. Just change some of the function declarations and such (like remove the "public" from them).
Still wouldn't mind a math guru saving me some time though (wink, wink).
Ya'd think that the title "Raycasting problems" would just scream "MATH GURUS!! GET OVER HERE!!". Well, I guess it depends on if they know what raycasting is.
What exactly have you tried when trying to make it work? Not that I can probly help, but there's always a chance...::):P
The only thing I can think of to increase the speed of the program is to use a lookup table (which I think has been already suggested earlier)
I'm going to do that. I've found (with the help of 23) that the cos() and sin() functions take up a lot of time. And, every time I draw a line, I'm using a cos() function to fix the distortion. I'm going to use that ViewFix table thing for the distances. I don't really know how, though...:-/mostly because the distortion correction uses player_angle (PA) to do it's thing...
Well the cos() and sin() in my CastRay() function can be removed completely in favour of just checking if the angle is in a certian range. The Java code is a bit tricky for me to understand, but the render() function seems to be doing things not quite in line with the article linking to it, so it's still confusing me. And some of the more useful engines on that big list are broken links. I made a fresh post on GameDev; hopefully I'll get a bite quickly.
And your function went slowly not just because you were using sin() and cos(), but because you were adding infintesimal values. You could just precalculate those values outside the loop, you know
And your function went slowly not just because you were using sin() and cos(), but because you were adding infintesimal values. You could just precalculate those values outside the loop, you know
Did I fix it?
As of the last bit of code you posted ... no.
If you're ever calling cos(angle) in a loop when angle never changes, you should just precalculate cos(angle) and use that instead.
As of the last bit of code you posted ... no.
Oh. That's not the latest code. I changed it so I have an X_Inc and Y_Inc variable like you do, which is calculated before it enters the actual casting loop. the_x and the_y are just incremented by that.
I've tried looking for my old DOS raycaster, but I think it got junked on one of my many memory purges. I've tried to recreate some of the source code too but, needles to say, I don't have such a great memory. It looks more like a scaled down version of what you guys already did.
1 | #define SCREEN_W (320) |
2 | #define SCREEN_H (240) |
3 | #define FOV (64) |
4 | #define FOV_DIV2 (FOV/2) |
5 | #define FOV_INCR ((FOV/SCREEN_W) << FIX_SHIFT) |
6 | #define FIX_SHIFT (16) |
7 | #define EMPTY (0) |
8 | |
9 | typedef signed long fixed; |
10 | typedef unsigned char angle; |
11 | |
12 | // I'll pretend these are filled already. |
13 | |
14 | fixed fix_cos[256]; |
15 | fixed fix_sin[256]; |
16 | |
17 | int map_data[32][32]; |
18 | |
19 | // I'll also pretend that I change these with user input |
20 | |
21 | fixed player_x_pos; |
22 | fixed player_y_pos; |
23 | angle player_dir; |
24 | |
25 | |
26 | fixed x_pos = player_x_pos; |
27 | fixed y_pos = player_y_pos; |
28 | fixed ray = (player_dir - FOV_DIV2) << FIX_SHIFT; |
29 | |
30 | // this will be the core renderer |
31 | |
32 | for(int w = 0; w < SCREEN_W; w++) |
33 | { |
34 | int distance = 0; |
35 | |
36 | ray += FOV_INCR; |
37 | |
38 | fixed x = x_pos; |
39 | fixed y = y_pos; |
40 | |
41 | while(map_data[x >> FIX_SHIFT][y >> FIX_SHIFT] == EMTPY) |
42 | { |
43 | x += fix_cos[ray]; |
44 | y += fix_sin[ray]; |
45 | distance++; |
46 | } |
47 | |
48 | // this fixes the "fisheye" effect |
49 | distance *= fix_cos[player_dir]; |
50 | |
51 | // use your imagination with this function |
52 | verticle_line(w, distance); |
53 | } |
I think you guys inspired me enough to rewrite the whole thing. This means I'll have to install allegro again.
I was just curious so I compiled the code posted
aybabtu: don't forget END_OF_MAIN()
23yrold3yrold: your code crashes as soon as I try to move to the right. I can rotate left for a while but then it crashes too. Which would suggest there is value getting out of bounds somewhere.
Fun stuff, I'll look into it some more. I might just stumble over your bug..
aybabtu: Yeah, the player will be searching through a "dungeon." Though I'm not sure what they're going to be doing yet. I hope I'll be able to add enemies and such, but if I run out of time, I'll just make it so they have to find their way out of a maze. Mwahahahahhaaha.
- Ace
aybabtu: don't forget END_OF_MAIN()
If I do, the program doesn't compile! I had to remove it from 23's go get it compile. Maybe it's a DJGPP thing.
I got the FPS of my engine (It needs lots of optimation!):
Standing: 4
Walking: 3
Close to a wall: 6
Need more speed!!
If I do, the program doesn't compile!
I had to add it to get to compile ...
I had to remove it from 23's go get it compile. Maybe it's a DJGPP thing.
Oh right; you use DJGPP and Allegro -2.0
lambik: Yes, I know, it crashes and I hate it. My code logs values to a file and constrains the crap out of tan(angle) in CastRay() but I still can't see the problem I hate math, math is 3vil ...
EDIT: Aaaaaaaaand the GameDevers were useless Yeah, like I'm getting lots of help here
I hate math, math is 3vil ...
You kind of picked the wrong hobby eh?
Oh right; you use DJGPP and Allegro -2.0
Yeah. I'm suprised my version even has blit...:P::)
You know, if you really want to get highly optimized code, It might be worth it to look at the FAT (Fixed Angle Texturing) engine developed by Thomas Nussambamer of TICT.ticalc.org. It has to be quit optimized, right? I mean, if you are able to run a raycaster on a graphing calculator, you should be able to optimize it to run on a regular computer, right?
At least the source could provide some insights. It is extremely optimized though, using some 68k ASM. I can't help you in understanding it... I kinda suck at ASM...
Well, Give it a Try...
Here is a link to the supposed "FAT" rendering engine SDK... I'm not sure exactly what it contains... might contain useful source, might not. But somebody should at least take a look at it.
Sure hope this helps... It might just complicate things, though...
It might just complicate things, though...
That's almost guarenteed. What; no one has the answer? Spellcaster? Thomas Harte? Bob? Anyone? Is this thing on?
I'm dumb! 23...
Looking at the FAT engine code probably would only prove useful in the final optimization stages though. It is pretty amazing though, you can get fairly decent framerates on the TI-89. (Considering the TI-89 has approximately a 5mhz processor, and less then 1MB of RAM)
I have to admit, I haven't really grasped everything that's been said in this thread, because it is just so expansive, but here are a few things I expect to have come up:
- the Wolfenstein optimisation of using a tile based map is very important for speed purposes
- as is the decision to use a predetermined texturing density
And two things which I think may not have done:
- the Allegro software bitmap scaler is the worst possible choice for a ray casting engine
- a polygon filler for rectangles which form walls in the style of Wolfenstein or Doom may be written without any divides whatsoever. Doom uses one, and this is where even people who know the engine sometimes claim that Doom has a ray casting step (BIG HINT)
Given this second point, there are very many situations broadly in the style of a ray caster (i.e. same height walls, all perfectly vertical) where a 2 dimensional portal or BSP approach will be faster - but with the benefit of any angled walls. Especially if you have 100s of kbs for levels rather than 10s.
How much faster is fixed than float?
How much faster is fixed than float?
On a 386 or 486SX, a fixed is several orders of magnitude faster than a float. On a 486DX a fixed is substantially faster than a float. On Pentiums, the fixed is certainly faster than the float for addition/subtraction, but they probably draw about equal for division/multiplication. Somewhere around Pentium 2/3s, fixeds become slower than floats for multiplication/division operations and the two become equal for addition/subtraction.
Naturally there are many situations for which one type or the other simply isn't suitable - in which case that type is much faster than the other.
I'm gonna change some/most of the math and stuff in the do_raycasting function to fixed. I've gotta get this thing going faster!
Mine flies, but I still have the errors. C'mon, where's that math whiz ...
On my programming computer, 23, I'm sorry to say that yours isn't much faster than mine. It is, but not a whole lot. What's your FPS?
Is it just the casting of the rays? Whatever...
Keep in mine my last posted code does constant file IO to log values 320 times a logic cycle. That slows it down a bloody lot. Take it out and it's lightning.
Oh.
Mine's so slow, you can see the FPS counting up.
FPS: 0 ... FPS: 1 ... FPS: 2 ... FPS: 3 ... FPS: 0 ...
Blah.:P:-/::)
23yrold3yrold: I've noticed that the angle between the two crash points is about 180 degrees. I haven't had the chance to look at your code, but I do know that tangent is undefined when cosine is equal to 0. cosine is 0 at 90 degrees (PI/2 radians). That should have something to do with the crashing since cosine and sine are continuous.
I'll check your example out some more.
- Ace
I've looked into that and it's not really crashing anymore; it may be the cause but I'm a little more interested in the graphical glitches.
EDIT: I fixed the major wall glitch with two negative signs; why exactly this worked I'm not sure There's still a minor glitch in the texture mapping though Newest code (minus the file IO) follows. Slows down when you get close to a wall, so it seems the drawing is the slowest part of the engine right now
Comments?
PS: I have the vague suspicion that my bodgy way of dealing with the crashing is causing the glitch, but I can't check it since whenever I remove those bits and try to look in the direction of the glitch it - you guessed it - crashes
EDIT3: Okay, I think I've ironed out most of it, and I'm going to start a fresh thread so someone may actually notice, but I just realized something. Aybabtu, why did you write your my_draw_circle() function? It does exactly what Allegro's circle function does (only much slower)?
aybabtu: found it,u asked this kind of question lastyear!:P
23: it still crash(and its not valid C code,plenty of error!:P),
try modify this 2 lines:
while((int)X_Xcheck>>6<9&&(int)X_Ycheck>>6<9&&!level[(int)X_Xcheck>>6][(int)X_Ycheck>>6]) {X_Xcheck+=X_Xinc; X_Ycheck+=X_Yinc;} while((int)X_Xcheck>>6<9&&(int)X_Ycheck>>6<9&&!level[(int)X_Xcheck>>6][(int)X_Ycheck>>6]) {X_Xcheck+=X_Xinc; X_Ycheck+=X_Yinc;}
shouldn't crash anymoreÂ…
Yeah, I think I've already figured it all out (including that) but it still seems hackish. I wish I knew how to do it right. Oh well, it doesn't seem to be crashing anymore, at least.
Anyway, since J13 gave me a bump, here's my best shot now. It works, but my rounding of the tangent values makes some walls look choppy if you stand beside it and look down it. It's acceptable though. I'm getting 60 fps if my face is in a wall, and well over 100fps otherwise.
Only problem now is that player movement at odd angles is a little "off", but making the player's position floats instead of ints should help that (EDIT: yup, that did it). I ought to add joystick code so I can move more accurately using analog joysticks and L/R strafe buttons
Have you seen this?
I've only been pimping that site all thread, TH If I fixed my math correctly, then that site has some math errors on it ...
EDIT: Actually, I think I'm right; that site does have an error, in that it doesn't negate the tangent value if a ray is going in certian directions. Anyway, it's pretty much perfect now ... I just need to figure out how to call tan() without having it crash. The fix I'm using right now produces minor texture problems ...
I looked at 23's engine, then at mine, then I noticed a major flaw in mine. In 23's the wall slices are all 1 pixel. In mine, the wall slices are 2 pixels! What the hell? What's going on?
I "cheated" to get a bit more speed out of it last night. I have it multiplying x_inc and y_inc by 3, and distance by 3. Of course, this limits the accuracy of the texture mapping, and I needed to create more of a collision buffer so the player couldn't get within 3 px of a wall. I get about 8 or 9 fps standing, 6 walking, and 4 up way close to a wall. With the texture mapping problem above, it doesn't look pretty, though. How do I fix that?
Latest code:
Well, your methods are wrong It's not very exact and its slow and there's bound to be precision loss or what have you. Why not just use my engine? It's faster and prettier and it's based on yours; how hard could it be?
Did you notice the fps on my engine?
23, what's your oppinion on my files?
Didn't look at them. Aybabtu says they're QBASIC ... yuck
The original was qbasic, I translated its last tutorial code to c++, then I took the engine a bit further.
Compiled fine. 80fps windowed, potentially higher if windowed. I like how you can follow walls, something my current code doesn't do yet (but that's collision detection, not raytracing, anyway ) Looks and works great.
EDIT: Oooh, I like my engine in 640x480 The stretch_blit() is definitely killing it though ...
I was about to go in way over my head when I abandoned it, I was planning to make every block have its own floor and roof height, but that felt like too much so I lost the enthusiasm.
I doubt it would bring much value to the game anyway, so I dropped it over an unnessesary feature.
If you need help with my engine, just ask.
I got an idea of making a nice RPG game with it.
Scratch what I said; your second and third examples don't compile using the latest MinGW.
I believe that has something to do with #define M_PI. I changed it from #define M_PI 3.14(w*.5-1)265358979323846 to #define M_PI 3.14 and it compiles. Looks like M_PI is suppose to take a parameter. I don't know much about #defines.
- Ace
23: to make the tangent not return an infinity you can try doing a hack like:
(or, to optimize, take out the cos and replace with appropriate values ... although the inherent inaccuracy in your PI define might mean you'd have to account for a bit of error in the equality, then)
I have that hack. It's not crashing anymore; there's just some texture erroneousness ...
And how accurate does my pi need to be?
#define M_PI 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679
clear(front);
blit(backdrop,front,0,0,0,0,320,200);
aybabtu:
thats why you have low FPS!
ok,let me post my modified version here and attach a screenshot.;)
1 | #include<math.h> |
2 | #include<al419.h> |
3 | #include<loadpng.h> |
4 | char NYAH[9][9]={ |
5 | {9,9,9,9,9,9,9,9,9}, |
6 | {9,0,0,0,0,0,0,0,9}, |
7 | {9,0,9,0,0,0,9,0,9}, |
8 | {9,0,0,0,0,0,0,0,9}, |
9 | {9,0,9,9,9,9,9,0,9}, |
10 | {9,0,0,0,4,4,4,0,9}, |
11 | {9,0,0,0,4,4,4,0,9}, |
12 | {9,0,0,0,0,0,0,0,9}, |
13 | {9,9,9,9,9,9,9,9,9}}; BITMAP*b,*TEX,*MAP; void raycasting(); float CastRay(int xpos,int ypos,double ang,int*t); |
14 | #define MC(RR,GG,BB) makecol(RR,GG,BB) |
15 | #define COLL (NYAH[px>>6][py>>6]) |
16 | #define J1(X) joy[0].stick[0].axis[X].d1 |
17 | #define J2(X) joy[0].stick[0].axis[X].d2 |
18 | #define AL_RAND ((rand()>>8)|(rand()&0xff00)) |
19 | int W=320,H=240,view[320],tmap[320],i,px=65,py=65,p_ox=65,p_oy=65,ps=2,tall,dist,c; float a,pa=1; |
20 | int main(){allegro_init();install_mouse();install_joystick(-1); |
21 | set_color_depth(16);set_gfx_mode(2,W,H,0,0); set_window_title(allegro_id); loadpng_init(); |
22 | MAP=create_bitmap_ex(8,9,9); for(i=0;i<9;i++)for(c=0;c<9;c++)MAP->line<i>[c]=NYAH<i>[c]; |
23 | TEX=load_bitmap("http://allegro.cc/members/avatars/2891.png",NULL); b=create_bitmap_ex(16,W,H); while(!mouse_b) {poll_joystick(); |
24 | if(J1(0)) pa-=0.04; if(pa<0) pa+=AL_PI*2; |
25 | if(J2(0)) pa+=0.04; if(pa>AL_PI*2) pa-=AL_PI*2; |
26 | if(J1(1)) {px+=(int)(ps*cos(pa)); py+=(int)(ps*sin(pa));} |
27 | if(J2(1)) {px-=(int)(ps*cos(pa)); py-=(int)(ps*sin(pa));} |
28 | if(joy[0].button[6].b) {px+=(int)(ps*cos(pa-AL_PI/2)); py+=(int)(ps*sin(pa-AL_PI/2));} |
29 | if(joy[0].button[7].b) {px-=(int)(ps*cos(pa-AL_PI/2)); py-=(int)(ps*sin(pa-AL_PI/2));} |
30 | if (COLL&&px>0&&py>0) {px=p_ox;py=p_oy;} else {p_ox=px;p_oy=py;} |
31 | for(i=0;i<H;i++)hline(b,0,i,W,(i<H/2)?MC(200,200,255-i):MC(0,i,0)); raycasting(); |
32 | for(i=0;i<W;i++) {tall=(int)(64.0/view<i>*277.0); dist=(H-tall)/2; |
33 | masked_stretch_blit(TEX,b,tmap<i>,0,1,64,i,dist,1,tall); |
34 | } for(a=0;a<360;a+=.1)putpixel(b,(int)(W-13+(13*cos(a))),(int)(13+(13*sin(a))),31); |
35 | line(b,W-13,13,W-13+(13*cos(pa)),13+(13*sin(pa)),MC(255,0,0)); |
36 | line(b,W-13,13,W-13+(13*cos(pa-0.5)),13+(13*sin(pa-0.5)),1337); |
37 | line(b,W-13,13,W-13+(13*cos(pa+0.5)),13+(13*sin(pa+0.5)),1337); |
38 | draw_sprite(b,MAP,W-36,0); putpixel(b,W-36+(px>>6),py>>6,c); |
39 | textprintf_ex(b,font,0,0,MC(0,255,0),0,"%d,%d,%.4f",px,py,pa); vsync();blit(b,screen,0,0,0,0,W,H); |
40 | c=AL_RAND; for(i=0;i<W;i++){putpixel(screen,i,view<i>,c); putpixel(screen,i,tmap<i>,0xf800);} |
41 | }}END_OF_MAIN(); |
42 | void raycasting() |
43 | {double a=pa-0.5236; for(i=0;i<W;i++){view<i>=(int)(cos(a-pa)*CastRay(px,py,a,&tmap<i>)); a+=1.04719/W;} |
44 | } |
45 | float CastRay(int xpos,int ypos,double ang,int*t) |
46 | {float XYchk,XXchk,YYchk,YXchk,XYinc,XXinc,YYinc,YXinc; if(ang<0.01&&ang>0)ang=0.01; |
47 | if(ang<AL_PI+0.01&&ang>AL_PI)ang=AL_PI+0.01; if(ang<AL_PI&&ang>AL_PI-0.01)ang=AL_PI-0.01; |
48 | if(cos(ang)==0)ang+=.01; double TAN=tan(ang); |
49 | if(TAN>=0.0&&TAN<0.01)TAN=0.01; if(TAN<=0.0&&TAN>-0.01)TAN=-0.01; if(TAN>999)TAN=999; if(TAN<-999)TAN=-999; |
50 | if(sin(ang)>0) //inc Y |
51 | {YYchk=(ypos/64)*64+64; YXchk=xpos+(YYchk-ypos)/TAN; YYinc=64; YXinc=64/TAN; |
52 | while(!getpixel(MAP,(int)YXchk>>6,(int)YYchk>>6)) {YXchk+=YXinc; YYchk+=YYinc;} |
53 | } else //dec Y |
54 | {YYchk=(ypos/64)*64-1; YXchk=xpos+(YYchk-ypos)/TAN; YYinc=-64; YXinc=-64/TAN; |
55 | while(!getpixel(MAP,(int)YXchk>>6,(int)YYchk>>6)) {YXchk+=YXinc; YYchk+=YYinc;} |
56 | } |
57 | if(cos(ang)>0) //inc X |
58 | {XXchk=(xpos/64)*64+64; XYchk=ypos+(XXchk-xpos)*TAN; XXinc=64; XYinc=64*TAN; |
59 | while(!getpixel(MAP,(int)XXchk>>6,(int)XYchk>>6)) {XXchk+=XXinc; XYchk+=XYinc;} |
60 | } else //dec X |
61 | {XXchk=(xpos/64)*64-1; XYchk=ypos+(XXchk-xpos)*TAN; XXinc=-64; XYinc=-64*TAN; |
62 | while(!getpixel(MAP,(int)XXchk>>6,(int)XYchk>>6)) {XXchk+=XXinc; XYchk+=XYinc;} |
63 | } |
64 | float x=(XXchk-xpos)*(XXchk-xpos)+(XYchk-ypos)*(XYchk-ypos),y=(YXchk-xpos)*(YXchk-xpos)+(YYchk-ypos)*(YYchk-ypos); |
65 | if(x<y){*t=(int)XYchk%64;return sqrt(x);}else{*t=(int)YXchk%64;return sqrt(y);}} |
seems that we are interested in aybabtu's project:P
Nyah
Responses / rambling:
Oh, sorry! Erm ... now that I think about it, PI accuracy probably won't be an issue either, heh.
Did you edit in this ("The fix I'm using right now produces minor texture problems ...") part of your post (a few posts up) by the way? Or did I just somehow not read it when I wrote my last post in this thread?
Edit: and is it just me or did J13 curse his modification of aybabtu's code with ugliness?
Questions:
1) What's the texture inaccuracy being caused by that hack? (I can't see how that hack would cause texture problems ...)
2) Would anyone be interested in starting some form of allegro module collection (a set of easy to use files that would allow anyone to, say, have a raycaster, mode 7 gfx, etc, simply by downloading two files (and include and .c) and calling the appropriate functions? I think this raycaster thing would be a great first module, as there's so much community interest ...
1) If my hack screws up the accuracy of tan at angles close to factors of 90, then it would explain why the texture is a mite screwy when looking right down the side of a wall (only half of them; the one on your immediate left when you start, for example).
2) Love to. Someone else is working on Mode 7 (amarillion) and I guess I can make up a raytracer lib since I'm interested right now Right now I'm playing a Flash game, so maybe later
1) Did you try decreasing the amount of intentional error you create? Add .0001 instead of .01, for example?
2) Sweet! I can imagine this being a very cool resource to have. Would you like to host / do that stuff as well? Or should I get it started? I guess the grand opening would deserve a thread of its own ... and a news item, even.
1) The crashes come back ...
2) I think a higher level library for Allegro would rule. I've already begun one with my tilemap class, animation class, screen update API, etc. A raycasting API would be a good addition, as would a Mode 7 one. I can't host as I have no where to do it, but I would either contribute to one or just let one accumulate as I code and code and code Feel free to start a thread brainstorming as to what such a lib would require ...
23: The reason I don't want to just use your engine is because I don't understand it. I want to work through mine and learn it.
J13: Would clear() and blit() really slow it down that much?
EDIT: J13: No offense, but is that how you normally write your code? Or did you forget the ENTER key?::)
EDIT 2: J13: It appears that clear() does slow it down...I can now pump a whopping 11 fps out of it!
Eeeek. I need to get started on my own raycasting engine if I'm going to do this for the rpgdx compo this weekend. The last I checked there were only three entrants.
... Better chance of winning, I suppose.
- Ace
Would clear() and blit() really slow it down that much?
Evidently so; I lose a lot of FPS if I'm right up against a wall.
23: The reason I don't want to just use your engine is because I don't understand it. I want to work through mine and learn it.
My code is your code! I'm just using the math from that one site in place of your shoot-a-very-slow-ray-until-it-hits-something method.
If you take the calculations for x_inc and y_inc out of the loop, does that make a difference?
Pete
My code is your code! I'm just using the math from that one site in place of your shoot-a-very-slow-ray-until-it-hits-something method.
Okey...the raycasting part is the most important part. I'd much rather learn from my mistakes than just copy someones code. I'm starting to get the math behind the faster way, but I don't quite have it yet. Very seldomly do I just take some code that I don't understand. Plus, I'll learn so much more if I work through it. Thanks for the offer, but from now on, remember that I don't want someone doing it for me.
If you take the calculations for x_inc and y_inc out of the loop, does that make a difference?
Yeah, because then it would:
A) Be very slow if you used sin() and cos() in your casting loop.
B) Or just not work at all...:o
Are you using cos/sin lookup tables? I believe that might give you a big speed boost. I noticed in your last code post that you are calling cos/sin 320 times every frame which SURELY kills performance.
- Ace
How could I use lookup tables? Wouldn't it be hard with the angle variable being incremented by less than a whole number? And, the angle var is gotten by using the pa variable, which could have anything between 0-255! I think it'd be hard... I could have something like
sin_table[255];
cos_table[255];
but that would only be good for whole numbered angles! Could anyone help me with this?
aybabtu: Well, you change the angle by a fixed amount at a time, so there's only a fixed amount of entries you'd need in your sin or cos table. But since you're using allegro's builtin fixed point math it should matter -- allegro's fixed math sin and cos already use a lookup table.
I'm not sure if this will work, but it is worth the try. From your code it appears angle has a precision of two decimal places.
So maybe try:
sin[angle*100] = sin(angle);
For all possible angles. It'll produce a huge table, but I'm sure you'd makeup for it with the fps gains.
Edit: Eeeeek, well nevermind then.
- Ace
I tried some tables and such, but it didn't make a difference. It's because of allegro stepping in to help speed up sin&cos with it's tables. Very nice of it, really.:P
EDIT: Screw allegro's "help"!! I #included <math.h>, then made a macro to convert degrees (0-360) to radians, and used the sin & cos functions without allegro's help. I got the idea after I RTFM and it said that it's sin and cos tables weren't very accurate. Well, guess what? IT'S BEAUTIFUL!!!! Here's a screenie:
[url http://www.angelfire.com/empire/jonhome/wolf3.gif]Image...[/url]
Here's the latest code:
(I know I overuse the DEG_TO_RAD() macro, but it's easier this way. Also, notice the DrawSlice() function, made by 23, and it gives me an extra FPS. Also, I used those shift operators to do division, and that gave me another FPS! Also, you can now control it by the mouse...controls are shown...)
Pretty kewl...;D
I see no picture. Does this mean your columns are 1 pixel wide instead of 2 now?
I just uploaded the pic, so you should see it now...sorry!
Yup. 1 pixel! And the textures look way nicer, too! (Notice the left-most wall in this one, then compare it to the "NO DISTORTIONS" screenshot...pretty nice!)
It'll run a lot faster if you rotate your texture by 90 degrees so that you read the X coord instead of the Y coord.
Untested code:
1 | void DrawSlice(BITMAP*texture,BITMAP*buffer,int xoff,int x,int y,int tall) |
2 | { |
3 | int srcy=0, num=0; |
4 | for(int i=y;i<y+tall;i++) |
5 | { |
6 | num+=64; |
7 | while(num>=tall) |
8 | { |
9 | num-=tall; |
10 | srcy++; |
11 | } |
12 | if(i<0) continue; |
13 | if(srcy>=64||i>=buffer->h) return; |
14 | ((short*)buffer->line<i>)[x]=((short*)texture->line[xoff])[srcy]; |
15 | } |
16 | } |
Replacing that while loop by a bunch of adds will also make it run a lot faster. If possible, see if you can drop those if statements from the inner loop too.
Alright...I'll try that rotate 90 degrees thing.
It'll run a lot faster if you rotate your texture by 90 degrees so that you read the X coord instead of the Y coord.
Oooohh! Is that because of cache missing or whatever you call it? Okay, I understand now I'll have to play with this now ...
I'd still love to have my math problems magically disappear. Until then, I'm potentially optimising my errors, and that's no fun ...
Oooohh! Is that because of cache missing or whatever you call it?
Yep
Arg.
To make it look even nicer, I have to take out some code that makes it run faster. What it does is multiplies the x_inc and y_inc by 3, and adds 3 to distance every step. But, this of course makes it innaccurate, and produces larger (3-4) pixel blocks close to the player. I've gotta learn how that thing on page 7 of that tutorial works. I understand it...sorta. I have two problems:
1) How do I get that first intersection?
2) Why does it separate the horizontal and vertical intersections?
Could someone help me understand it?:-/
1) Basically, by rounding to the nearest 64 for one coordinate, then by using the tangent of the angle to get the other coordinate.
2) Because stepping through them seperately is a lot easier than going through them together. Look at how easy it is to just add twice every loop. Can only do that if they're checked seperately.
I still don't quite understand tan() enough to explain it. If it's not in amarillion's tutorial, it should be
I'm okay with tan:
Tangent(angle)=opposite side/adjacent side
I also think I'm getting this thing...I'm going to go work on it now...
Going slightly off topic into the 'not raycasting' ideas I posted earlier, I've come up with a knockup of such an implementation (see attachment for code & screenshot) and am convinced that it is a 'better than raycasting' implementation. The technique uses a combination of the following:
- front to back (zero overdraw) portal based rendering
- one divide per column texture mapping (same as normal ray casting)
The FPS doesn't seem to be the astronomic figure I'd expected, but to put things in perspective, here are the top four time consuming functions from a profile build:
Func Func+Child Hit Time % Time % Count Function --------------------------------------------------------- 2471.277 33.0 2471.277 33.0 686 _rectfill (alld40.dll) 944.485 12.6 1258.176 16.8 109713 DrawSlither(struct BITMAP *,struct Texture *,int,int,int,int) (main.obj) 774.079 10.3 1192.259 15.9 589954 _fmul (main.obj) 740.479 9.9 2330.742 31.1 1904 AddWall(struct Wall *,unsigned int,long,long,int,int) (main.obj)
So, the bottleneck is clearly video memory access (i.e. drawing, not calculating), which is exactly the same cost as raycasting. Therefore I am confident that with properly optimised drawing code the same sorts of frame rates as ray casting could be seen.
With respect to fmul versus AddWall, fmul is used four times per wall (unless the wall is a reverse face - then just twice) and a further three times per slither if that slither actually appears on the screen. For slithers which do not appear on screen due to being obscured, no fmuls will usually be performed, however for areas where the wall would appear on screen were it longer (and retained the same orientation), one fmul will be performed per slither.
I know that the display sometimes flickers. I'm not sure if this is a buffer bug I've added or the usual array of incompatibilities between Allegro and Windows 2000.
AddWall becomes substantially faster as the screen fills up. Many more sectors would not substantially increase calculation costs.
A screenshot is only attached as I couldn't find anywhere to upload it in order to show it inline. Similarly Allegro.cc would not let me post the code inline due to length.
That's neat...seems more complex than raycasting, though...:)
EDIT:
New stuff for the raycaster:
Multiple textures. (I've got three coded in...I need to make a level editor!)
Sped it up and kept it nice looking...I'm getting 9-12 FPS walking...
Looking up and down, looking and rotating is really nice when controled by the mouse (i.e. smooth/cool!)
Heh...I've even got a gun in there now (I'm going to make it a fps.) It's the PPK from GoldenEye. The "Z" Key switches modes for the gun. This gun switches between silenced and not silenced. It's pretty cool.
EDIT 2: I just downloaded that FAT engine SDK...you must get it! What I think's cool is that it's got ALL the textures from Wolfenstein! Plus, they're 64x64 px, all aranged on a 640x384 sheet. Its cool!
Yeah, textures are pretty easy. Just use different numbers in the array
I'm going to add that other stuff too, but not until I've gotten rid of those texture problems. I'll keep that in the other thread though. Until then, it's back to my game ...
Guess I'll check out TH's code too
23 (and anyone else who's interested): I'm attaching a sheet with all the Wolfenstein textures!
I just downloaded that FAT engine SDK
What's this now? What does it do that that site doesn't teach?
That's neat...seems more complex than raycasting, though...
Not really - the guts of the thing are within AddWall, and that is really nothing more than a ray caster which only considers one wall (and makes a few arrangements prior to drawing in order to pretend that the wall is axis aligned).
What's this now? What does it do that that site doesn't teach?
It's that TICL thing that Carrus posted. It doesn't really teach anything new. But, it's got the wolfenstein textures, and the brown-suited enemies from wolfenstein.
I tried my hand at getting that better way to cast the rays to work...of course it didn't...oh well. I just can't get it to work. I don't even know if I want to get it to work. To be honest, 23, that bug in yours, which is probly caused by the tangent or something, makes that method very unappealing. Maybe I can be enlightened from that java source code...idunno. But, for now, I can make the detail level adjustable. Basically, I can multiply teh x_inc and y_inc, and distance by a number, which makes it go much faster. Of course, you lose accuracy, but even when you multiply it by 5, you can get more than 10 more fps, and the output is still acceptable. Some of the smaller lines and details are lost somewhat, but it's not a problem. I'll just have the detail level adjustable.
TH: Could you compile it into a binary? My DJGPP complains about MANY "errors". I tried fixing some, but I just gave up. How smooth is it? Would you think that you were in a raycaster when you're running it? What is a 2D portal engine?
The TICT FAT engine doesn't really assist you in doing things directly. However, it might prove more useful to look at the forum under FAT SDK. The optimizations neccesarry to get a Raycaster to work on a graphing calculator are drastic, maybe you could implement some of them.
The current engine also has built in support for sprites (similar to wolf3d, blake stone, etc.), Moving walls for those secret passages, doors, etc.
However, once again I must reiterate the fact that it will only prove useful for opimizition, if at all.
Hey...I think I should put depth shading in...how should I do it? Use a blending function or something? If so, I've never used one before...are they slow?
aybabtu: On TH's: my compiler and updated allegro only gave one error, which was fixed by renaming the variable "Texture Texture" in the wall struct to "Texture texture" then searching through the file to find all instances in which (somewallvariable)->Texture was being used, and fixed the variable name. For your outdated as anything setup, however, I'm afraid there might be more errors, which is entirely your fault. Update your software.
TH, I think the word you're looking for is sliver, not slither.
aybabtu: nice textures! The art for Wolfenstein is a lot nicer than I had thought it was... as for lighting, it depends on whether you're using 8-bit colour or hi-colour/true-colour modes. If you're using 8-bit colour, you have to use palette tricks of some sort, and the fastest way is to use a lookup table. If you're using a higher colour depth, you use a lighting function... which are you using?
The Wolfenstein textures are as ugly as I remember Anyway, I think I may have licked the crashing someone else was getting, and I changed my DrawSlice() function so now it doesn't dip down to 14 fps if you're up against a wall. Now it stays over 90 at all times, but the texture is a little messed up (I think my math needs a little work). At least the texture doesn't reverse anymore (aybabtu, use a texture with a word on it and see if any of your walls come out with the word backwards). And I can still see slivers of perpendicular walls sticking through some other walls. I would post this in my thread, but it's a day old with the last post being mine, so no one would see it So, here's my latest:
I made a little test for shading the walls and it barely affected the FPS, so I'll probably give depth shading a shot soon ...
23:'for'loop initial declaration used outside C99 mode
// check for the walls
while(((int)X_Xcheck >> 6 < MAPWIDTH) &&
((int)X_Ycheck >> 6 < MAPHEIGHT)
you still checking the level[] boundary? use getpixel in Mypost!:P
and plz shorten your code or Attach it! your codebox is now 9.9k! sp4mm3r!;)
and test the FPS yourself:
23yrold3yrold: It's not crashing for me anymore (yay!), but it does drop below 90 when I'm next to a wall. To about 60 fps. Maybe my computer is being weird.
- Ace
23:'for'loop initial declaration used outside C99 mode
C++ coders care.
you still checking the level[] boundary?
Hell yes! Much faster than getpixel(). What would using getpixel() get me anyway?
and plz shorten your code or Attach it! your codebox is now 9.9k!
Is that why your code has almost 0 whitespace?
And I am testing the fps myself (or was that directed at aybabtu?) ...
Ace: Cool
I've attached a .zip with a DOS binary, the source code, and my little texture set. Could you guys test it out and tell me what kind of FPS you get with the "Ray Skip" at 1, and at 5? Use the -/+ butons to change it. It makes it run a lot faster on 5, but you lose detail...I wanna see how fast it runs on ppl's computers.
Goodbytes: I'm using 16bit color. Down with palettes!
J13: That screenshot looks nifty...I kinda like it with the lines on top of the walls!
For your outdated as anything setup, however, I'm afraid there might be more errors, which is entirely your fault. Update your software.
Fine...I'll download allegro 4 or the newest WIP or something...
Anywayz, here's the latest code:
(Control view with mouse/arrow keys, move with WASD)
[WHOOPS! The post's too big with it...just download the attachment]
As you can see, I've eliminated another call to the cos() function, by using a table to fix the distortion!
EDIT: 23: Words are backwards on my textures only on certain sides of a cube. 2 sides are right, 2 sides are backwards. I tested it with the all powerful NYAH.
YaY!!! I was waiting for source and some textures
edit:
Ok, I get about 30fps normally in XWIN mode, not too shabby. XWIN is like GDI mode in windows.. SLOW. and If I look directly at a wall I get 40fps I'd try it in DGA2 mode, but my monitor doesn't do anything less than 640x480.
Are you going to FPS-test mine?
heh, edited..
Goodbytes: I'm using 16bit color. Down with palettes!
Good. Then lighting is easy enough... sorta. I guess. Or something. Yeah.
Anyways, if you want to shade based on depth, as if the player has a light on his/her head or whatever, you'll probably want to shade the ceiling and floor as well as the walls. So, you need to darken your floor/ceiling gradient a little as it reaches the horizon.
Then, as for shading the walls, you can probably just subtract a little from the colour components of each pixel based on how far away it is. Allow me to rip some code from FBlend for a minute.
Okay, here's a sort of example program for ya:
1 | #include <allegro.h> |
2 | |
3 | int main() |
4 | { |
5 | allegro_init(); |
6 | set_color_depth(16); |
7 | set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0); |
8 | install_keyboard(); |
9 | |
10 | BITMAP* src = load_bitmap("src.bmp", 0); |
11 | BITMAP* dst = create_bitmap(src->w, src->h); |
12 | |
13 | // Important part #1: light levels |
14 | unsigned long lightlevel[32]; |
15 | for(int i = 0; i < 32; ++i) |
16 | { |
17 | lightlevel<i> = (i | (i << 11) | (i << 22)); |
18 | } |
19 | |
20 | int distance = 0; |
21 | |
22 | while(1) |
23 | { |
24 | // Important part #2: subtractive lighting |
25 | for(int y = 0; y < src->h; ++y) |
26 | { |
27 | for(int x = 0; x < src->w; ++x) |
28 | { |
29 | unsigned long c = _getpixel16(src, x, y); |
30 | unsigned long res; |
31 | c = ((c << 16) | c) & 0x7C0F81F; |
32 | c -= lightlevel[distance]; |
33 | res = c & 0x8010020; |
34 | res -= (res >> 5); |
35 | c &= ~res; |
36 | c &= 0x7C0F81F; |
37 | c |= (c >> 16); |
38 | c &= 0xFFFF; |
39 | _putpixel16(dst, x, y, c); |
40 | } |
41 | } |
42 | |
43 | textprintf(screen, font, 0, 0, makecol(255, 255, 255), "Distance: %2d. [Q] to quit, [A] & [Z] to control distance. %d", distance, lightlevel[31]); |
44 | draw_sprite(screen, dst, 0, 10); |
45 | |
46 | clear_keybuf(); |
47 | int k = readkey() & 0xff; |
48 | if(k == 'q') |
49 | break; |
50 | else if(k == 'a') |
51 | ++distance; |
52 | else if(k == 'z') |
53 | --distance; |
54 | distance = MID(0, distance, 31); |
55 | } |
56 | } |
57 | END_OF_MAIN(); |
This is C++ code, so compile it as so.
The two parts to note are the creation of the light table, and the actual subtractive blending parts, which are commented as important parts numbers one and two, respectively.
Now, credit must go to Bob for actually writing the subtractive blending part in FBlend, which I modified a teensy bit. The rest of the program is my fault.
Actually, I think it works well. Make a small bitmap called src.bmp, compile this, and put them in the same directory, and try it out yourself.
Anyhow, now on to how it works. It's kind of hard to explain... basically, in the subtractive blending part, the color is grabbed from the source bitmap(you should use a faster method than _getpixel16 if possible, btw) and then the individual colour components are separated apart, so where originally the binary representation of the colour looks like this:
OOOOOOOOOOOOOOOORRRRRGGGGGGBBBBB // 16-bit colour on a 32-bit int, 5 bits for red, 6 for green, 5 for blue
it gets transformed to look like this:
OOOOOGGGGGOOOOOORRRRROOOOOOBBBBB // 16-bit colour on a 32-bit int, but spread apart, and 5r5g5b instead of 5r6g5b
The lightlevel[] array is set up to match the above representation for 32 levels of light... for example, lightlevel[31] looks like this:
OOOOO11111OOOOOO11111OOOOOO11111 // 11111 = decimal 31 - for each colour component
Then, the light level is subtracted from the source colour, put back together into the packed 565 format, and drawn onto the destination bitmap. Again, use direct bitmap access instead of _putpixel16.
This is basically the same thing as doing
unsigned int r, g, b, c; c = _getpixel16(src, x, y); r = getr16(c); g = getg16(c); b = getb16(c); r -= distance; g -= distance; b -= distance; c = makecol16(r, g, b); _putpixel16(dst, x, y, c);
But hopefully faster.
Finally, remember that since you only have 32 distance values, try to spread them out a little - i.e. you would probably want a slice that's 32 'pixels' away to actually be quite bright. In fact, you can probably map distance in 'tile units' - i.e. with a slice that is 1 tile length away being a distance value of 1.
Hope this helps.
EDIT: 23: Words are backwards on my textures only on certain sides of a cube. 2 sides are right, 2 sides are backwards. I tested it with the all powerful NYAH.
That's what happened to me. My latest code up there corrects this.
YaY!!! I was waiting for source and some textures
Dude, that's all we've been posting
Sweet mother, aybabtu, why does your code run at 0 FPS?!? Seriously, I thought it froze my computer. I haven't looked at your code yet to see what's wrong, but it's literally unplayable. Exited via crash. BTW, have you tried my latest? I get 150 FPS, dunno what that comes to on your box ...
BTW, if you aren't texturing the floor or roof, gradient shading becomes very easy
Dude, that's all we've been posting
Sory, didnt notice any textures. I compiled and ran a previous version of somebodys and it crashed I was about to report it, but realize that it was because using a NULL bitmap doesn't work to well.
I've done some bugfixes and other stuff.
I don't know how things are with the M_PI define you complained about, it compiles fine here.
Have a look and do as you wish.
I used my own texture. Just throw a 64x64 truecolor texture in with the executable and you're good to go. Try it with mine; how's it work for you?
Textures? I don't have no stinkin textures. Really. (um.. you want me to try yours now? demands... sheesh )
I don't have any texture either; I just drew some scribbles in MSPaint
. Ok, I forgot to mention, my fps that I reported was totally unoptimized, and unaccelerated, windowedmode... I'll try that other one again, just optomized...
edit: ok that wolf one is like 50-60 fps now...
edit: ok, yours chris 300fps. in 320x240. in XWIN mode.. I'll see what DGA mode does.. (cause aparently, my monitor can do 320x240... I thought it couldn't)
edit: more updates... with DGA it hits 300-370 (400 at max) fps, and at 640x480 in DGA mode, I get 80-100fps.
BTW, can anyone help with this little bit of math? The if statement is to skip the offscreen pixels (HUGE fps boost) but it messes up the texture a bit. I've attached a screenshot which demonstrates this ...
1 | |
2 | void DrawSlice(BITMAP* buffer, BITMAP* texture, int xoff, int x, int y, int tall) |
3 | { |
4 | int srcy = 0, num = 0; |
5 | |
6 | // skip down until we hit the top of the screen (for very close walls) |
7 | // this bit is currently a little buggy ... |
8 | if(tall > SCREENHEIGHT) |
9 | { |
10 | num = 64 * ((tall - SCREENHEIGHT) / 2); |
11 | |
12 | if(num >= tall) |
13 | { |
14 | srcy = num / tall; |
15 | num = num % 64; |
16 | } |
17 | |
18 | y = 0; |
19 | } |
20 | |
21 | // draw that slice! |
22 | for(int i = y ; i < y + tall ; ++i) |
23 | { |
24 | num += 64; |
25 | |
26 | while(num >= tall) |
27 | { |
28 | num -= tall; |
29 | ++srcy; |
30 | } |
31 | |
32 | if(srcy >= 64 || i >= buffer->h) return; |
33 | |
34 | // this assumes the texture is rotated (otherwise it's slower due to cache missing) |
35 | // ((short *)buffer->line<i>)[x] = (((short*)texture->line[xoff])[srcy] >> 1 & 0x7BEF); |
36 | ((short *)buffer->line<i>)[x] = ((short*)texture->line[xoff])[srcy]; |
37 | } |
38 | } |
See anything wrong with that first half of the function?
TH: Could you compile it into a binary? My DJGPP complains about MANY "errors". I tried fixing some, but I just gave up. How smooth is it? Would you think that you were in a raycaster when you're running it? What is a 2D portal engine?
Portal engines work on the principle of sectors. For my engine, a sector is a convex room. Some of the walls are thought to be portals. In ray casting terms of thought, when a ray hits a portal, it carries on through the portal into another sector rather than deciding that it has hit a wall.
To do all this, the engine knows which sector the player is currently in, and works on the following (pseudo-code) draw loop:
function DrawSector(s) { for(all non-portal walls in sector) draw walls to the display for(all portals) { calculate area of screen that portal would cover if it were a solid wall call DrawSector with the connected sector, specifying to restrict to that area of wall } }
Because the sectors are convex, if we know the start sector, this gives a no-overdraw front to back draw.
I shall try to dust off a copy of DJGPP and compile my code. It was developed on MSVC so I may have inadvertently used some Microsoft extensions (e.g. unnamed structs) without realising...
TH, I think the word you're looking for is sliver, not slither.
Nah, its all to do with the first tutorial I read about Wolfenstein type things, many years ago, which compared them to snakes (don't ask).
Chris: Where's yours? I don't have a compiler here at work (and don't feel like setting one up here)
Okay, I'm attaching a zip with makefile, source, Win32 binary and texture file (32KB).
23: Your code stays over 150 on my computer even near walls, and tends to be >200 ... very nice! I can't really see the glitches you're talking about, though, so I can't really comment on what might be causing them. Perhaps you could post a screenie of a pronounced glitch? (Heh, you could even check miran's screenie module in the early modules submission thread to do it ...). This test result is from using your latest code posted in this thread, which is quite a few posts up, actually, so I guess you may have made progress since then.
EDIT: Gah! I idle a bit and look what happens ... takes me 20 minutes to write this post and to test 23's code and sooo many people have already posted. It's not being beaten ... it's being completely demolished
I did post a screenshot (5 posts up) as an attachment. Guess you missed it, dawdle-boy . It will show up on any wall that is a) taller than the screen, and b) at an angle (you won't see it if you're standing facing straight at the wall).
For reference, to document the worst beating I've seen yet: the last post in this thread when I wrote the above response was TF saying:
Textures? I don't have no stinkin textures. Really. (um.. you want me to try yours now? demands... sheesh )
Actually if you face the wall right up close, the ray infront gets quite abit bigger than it should, if you take a look at the size of the rays around it..
23: Stays over 260 with this 1.70GHz machine I have here at work. When not close to a wall, I get around 313.
aybabtu: 2000 don't like DOS apps , so I couldn't test it here.
Will do a test at home on my 600 MHz system (which, I assume because of RAM and other network stuff, run laps around this computer )
TF: If that's directed at me, I'm not seeing it ...
If Matthew closes this thread for length, I'd like to start another one, because I'd really like some help with those texture errors EDIT: nm, I just did
edit: ok that wolf one is like 50-60 fps now...
Yay! What's your system?
Goodbytes: Thanks...I'll try that stuff out tonight or something...
23: Your new code skips off screen pixels! Yay! Maybe even more FPS!
Miquel: Sorry it's DOS! I don't have MinGW or anything, but it shouldn't be hard to compile...you should just have to stick END_OF_MAIN in there.
On a 600Mhz system...you should get nice numbers. On my 233, I get ~20 frames...and with TF getting 50-60! Ah...life is good.:P
Ahem I went over this already.
23: darned. well, it seems that must have been a fluke, or only happens in very rare circumstances... but I did see something else thats a bit odd.. hard to explain, just that I found if you're standing right up to a wall, and turn back and forth a bit the colors on the texture move In the attached image, notice the green, almost teal color, now If I use the arrow keys to look side to side, that teal grows and shrinks in size, after a bit of looking left, it totally disapears and the colors underneath takeover. weird.
Yay! What's your system?
Athlon 900.
You forgot the image TF
crap
sory
and by green, almost teal, the two blocks, one at the top, and one at the bottom...
Yikes O_o I see nothing like that when I'm playing ... just nice gray walls ...
Sweet mother, aybabtu, why does your code run at 0 FPS?!?
Um...did you run the binary or compile the code? The binary is a DOS binary (DJGPP, remember?), so if you have winXP or 2000 or something, it probly won't work.:P
I ran the binary, and use Win98.
Chris, Can you explain what you're doing in DrawSlice? I can't figure it out (my fault )
I got similar fps on my system with just
void DrawSlice(BITMAP* buffer, BITMAP* texture, int xoff, int x, int y, int tall) { stretch_blit(texture, buffer, xoff, 0, 1, 64, x, y, 1,tall); }
but I may not have compiled yours with the correct optimisation. Also it rotated the textures by 90 compared to yours.
Pete
I compared your raycaster with mine.
Wolf, 64 fps max
Me, 400 fps max
I'm a little happy here.
BTW, the binary worked fine here, XP.
And my engine has quite a bit of usefull features you might want to have a closer look at.
I get better fps my way over using stretch_blit() (about 50% better). The rotation is because I treat rows like columns in DrawSlice() due to Bob's suggestion. Otherwise I'm doing evil cache missing or something
Anyway, DrawSlice is based off my Bresenham line drawing code to step through the column pixels and get the right texture pixel to draw. It's not too difficult to figure out, methinks. If it still confuses you, post in my "Math help, please" thread since this one's getting a little long
Trezker: Yes, I should look at your code closer; thanks for reminding me
So, have you figured out whats up with the funky colors? Since you say its supposed to be shaded?
I took the shading out of that code, TF. I don't see that for the life of me when I run it. Can't duplicate the error at all. Are you using my gray stone texture?
Nice, the stretch_blit is tha main bottleneck in my code. That's why I don't let the player closer than 2 units, each block is 64, from the walls. If I use a better method, it'll do alot.
what grey stone texture?
The one in my last zip (at miran's request)?
I compared your raycaster with mine.
Wolf, 64 fps max
Me, 400 fps max
I'm a little happy here.
Hey...so am I! I know my casting method is far from the best, but 50-64 FPS is fine by me. Wow...400? You're useing the "right" method, huh? I'm gonna check out your source...
I ran the binary, and use Win98.
Geez! That's crazy! It runs on both my old 586 (Win95) and my family's computer (Win98SE). Crazy...
hey just tested it, looks great, but you may want to actually put a fps limiter on there to make it always run at 60fps or something, because when I'm moving around you don't go forward smoothly, for instance you go around a corner where the fps goes from 60 to 90, you run into it because a sudden change in velocity. Anyway nice little demo
Holy CRAP! A 30 frame jump! Wow...didn't think I'd see the day...well okay! If it's becomming a problem, I'll put a limit...! YAY!
I think I've coaxed another frame per second out of it. In my raycasting while loop, I had it testing to see if the_x and the_y were hitting a tile, but, if they do, the inside of the loop catches it and breaks out of it. So, I eliminated 2 divisions (well, shifts), and a usless conditional...it runs a little bit faster...I think.
EDIT:
How accurate should PI be? I mean, after the first 10 decimal places...it should be pretty usless, right? Or should I have as many as I can?
Pfffft, PI.... round that thing down to 3 and call it a day.
Seriously though, using a long PI will definately give you more precision, there's only show much precision that's going to show up. Ten decimals should be plenty, probably overkill.
Edit: Dear lord... I'm going to have nightmares about pi tonight. In it, I'm going to be traveling through a raycasted dungeon and I'll run into various 2^1/2 enemies and when I finally get to the boss room, PI will be standing in the middle of it, carrying a missle launcher. Too bad I'm out of bullets and I'm done to 5% health.
- Ace
Pfffft, PI.... round that thing down to 3 and call it a day.
Heheheheh.
3*10=30
3.14*10=31.4-OR-
3*100=300
3.14*100=314
Quite a difference...;):P
EDIT: How long with Matthew let a thread go to...I see that Bruce Perry's "My avatar" thread is well over 200...
EDIT2:
Dear lord... I'm going to have nightmares about pi tonight.
Yes! How dare you insult teh almighty PI!? Round down to 3...bah!
Well, this thread is still on-topic I'm moving on to Tetris Attack for a while myself; I'll come back to this later ...
Anyway, DrawSlice is based off my Bresenham line drawing code
Wolfenstein uses the broad equivalent of compiled sprites for every possible sized slice of every possible texture. But I'd dare imagine it uses some super-clever assumptions about run length stuff which Allegro does not with its compiled sprites - or else it couldn't possibly fit all that code into memory.
How many different textures does the average Wolfenstein level have?
And has this thread yet discussed the ways of inserting sprites and doors into the world?
And has this thread yet discussed the ways of inserting sprites and doors into the world?
Unfortunately, no.
Well, a very primative technique of drawing a baddie would be to:
1) keep an array of distances to walls
2) Use the x/z, y/z formula to determine the sprite's midpoint location, and the size/z formula to determine how much to scale it -- to do this, of course, you need to rotate the x,y,z so z+ or z- is away from you (pending left or right hand coordinate system)
3) Draw the sprite slice by slice, comparing its z to the wall array's z for each slice
4) For each slice you draw, fill in the corresponding spot in the wall array with the sprite's z so that later sprite draws layer correctly.
Of course, this technique is designed with same-height enemies in mind, and cannot have a taller enemy peeking over a wall or another baddie. But then, it's probably good enough for your average wolfenstein clone.
Actually, I would just draw the enemies back to front, since you can see one enemy behind another because of transparency. You're keeping an array of distances to walls anyway, just grab all the enemies in your line of sight, get distances, draw back-to-front slice by slice, and I guess you got it.
I get around 70 fps with 1 frameskip and over 200 fps with 5 frameskips, with and without vync. 'Course, I'm on an Athlon 1800+
23: Oh, right. Heh, silly me and my rectangular enemies.
Just populate your dungeon with giant cube slimes, Z
I'm gonna work on drawing objects soon. I can't get the engine to run much faster, so I'm gonna move on. Should be pretty easy. Just use the distances in the view[] variable, and compare it's distance...I think.
Yeah. And in my portal engine you only need consider objects which overlap with sectors that were actually visited, so you usually end up considering less objects than a ray caster!
Thomas Harte: Can you attach a binary of your portal engine? I can't get it to compile with my version of allegro (3.11...bleh!) and DJGPP...
Oh, yeah, I'm going to get on top of that as soon as I get a working DJGPP install. Or are you happy with a Windows binary?
A windows binary is fine...but I'd prefer a DOS one, because sometimes I can't get the windows ones to work even on 98SE!
Aren't you taking my quote a bit out of context aybabtu?
Now, what framerate did you get with mine?
It says ~480 fps .
This is a really cool engine, if you'd improve it maybe you could make a Build-like engine, that would be awesome. Build > *.
23: did you ever fix your rendering error? It really looks great though, and yeah I get excellent fps with your demo I hope you can work out the kinks, but another great idea is to have the player move slowly if up against the wall proportional to his angle. For instance instead of stopping once you hit a wall at 10 degrees offcenter then move slowly in that direction instead of a dead stop. Of course if the player hits perpendicular to a wall then his velocity will by 0.
I already fixed the hit-wall-and-stop-dead thing in my other thread (which anyone is welcome to bump ). I detailed the finalest of final texture errors there, but I can't lick it. I put it away for now; I'm finishing my Tetris Attack clone right now, then I'm going back to The Mighty Stoopid. If someone wants to help me, I won't turn them down
See fourth last post here for my latest progress. And thanks for the compliments
I got MinGW up and running, and today I'm getting Allegro going...so I won't be the only person that can play my binaries!
Karel: Well...um...heheheh...no.
Attached is a compiled and slightly fixed version of my 'not really raycasting' engine, along with source, alleg40.dll and everything else you need to use it. It runs at 800x600, keeping up with the frame rate of 60fps on my little celeron 533Mhz.
I'm aware that in a few cases (most notably on the very right edge of walls) slivers do not appear. I'm sure I could fix that. Also I'm aware that I don't really need cos and tan tables across an entire circle because cos(a) = -cos(-a) and tan(a+ half circle) = tan(a). That wouldn't take terribly long to fix.
Cool, Thomas! I only get about 10-16 fps depending on how close I am to a wall (the closer, the slower).
How does 10-16fps compare with the other alternatives when running at 800x600? In any case, the good news is that I've fixed all rendering errors this afternoon and sped things up about 10% by replacing every instance of 'fmul' with 'fixmul'.
EDIT: to put things in perspective, 800x600 (8bit) is 468.75kb. 320x200 (15/16bit) is just 125kb. And, comparitively, you get half the byte computations for free.
10-16 fps is pretty good because I get 11-18 on my 320x200 raycaster!
23's seems to only get 45 fps when I make it 800x600, and has a minor graphical glitch in the background. Then again, he is drawing some pretty sky gradient or something in the top, so maybe that's just really really slow ... but I think we can probably say that yours is faster, TH. Especially because 60 is the refresh rate of my monitor
Those occassionally invisible slices of wall are kinda annoying though. Fixing that would be cool.
23's ... has a minor graphical glitch in the background.
Yes, and I hate it with all my being. It doesn't show up in nice low resolutions though
I hate to be satins advocate, but shouldn't you guys start up another thread? This one is getting REAL crowded, it is kinda nice not having to sift through all of the pages.
Whenever Matthew closes it, we'll all pile into Aybabtu's Depot thread
Use the lightbulb, Luke
(or change My Profile->Forums->General->Page Splits to a setting that you like)
I hate to be satins advocate, but shouldn't you guys start up another thread? This one is getting REAL crowded, it is kinda nice not having to sift through all of the pages.
I have my pages set to 25ppp, and I like it. I don't think it's crowded...pages are such a nice feature for dial up users who's modems are crapping out and only connect them at 21.6 kbps...bleh.
Whenever Matthew closes it, we'll all pile into Aybabtu's Depot thread
Well, you're all very welcome! How long is the longest thread ever? >225 posts, I think.