Aerosol, on 21 May 2014 - 02:54 PM, said:

I saw that page a long time ago. I was a little disappointed that he had removed the source code.

Cannonball run is open source, and I would expect it behaves similar to the methods he describes.

Regardless, here's some allegro code that works very similarly:

#include "allegro.h"
#include <math.h>
void DrawPerspective(BITMAP *t, BITMAP *tex, int y1, float z1, int y2, float z2, float x, float xmul, float ymul, float texoff)
{
float curvdiv = z1/(3.141592654f*0.5f);
float zpos = 1.0f /z2;
z1 = 1.0f / z1;
/* sample 16 times for each scanline */
y1 <<= 4;
y2 <<= 4;
ymul *= 16.0f;
int BottomY = y2;
float zadd = (z1 - zpos) / (float)(y2 - y1);
while(y2 > y1)
{
float z = 1.0f / zpos;
/*
NOTE: z is the actual depth of this scanline, zpos is 1/z. The most common
perspective projection formula is used, I.e.
transformed_x = (centre of screen) - (half width of screen)*(x / z)
But instead of dividing by z we can multiply by zpos if we like.
*/
float centx = x + xmul*(cos(z/curvdiv) - 1.0f);
int topy = y2 + (SCREEN_H >> 1)*(ymul*(cos(z/curvdiv) - 1.0f) / z);
if((topy >> 4) < (BottomY >> 4)) /* question is: are we at least one pixel line above what was last drawn ? */
{
stretch_blit(tex, t, 0, (int)((z+texoff)*4.0f)&(tex->h-1), tex->w, 1, (SCREEN_W >> 1) + (SCREEN_W >> 1)*(centx-1.0f)*zpos, (topy) >> 4, (SCREEN_W >> 1)*(2.0f)*zpos, (BottomY >> 4) -(topy >> 4));
BottomY = topy;
}
zpos += zadd;
y2--;
}
}
int main(int argc, const char *argv[])
{
allegro_init();
install_keyboard();
if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0)) {
allegro_message("Error setting 640x480 gfx mode:\n%s\n", allegro_error);
return -1;
}
BITMAP *back = create_bitmap(SCREEN_W, SCREEN_H);
/* obviously it would be better to load a road texture here, but we'll just generate a chequered
pattern ... */
BITMAP *tex = create_bitmap(8, 2);
int xp, yp;
for(xp = 0; xp < tex->w; xp++)
for(yp = 0; yp < tex->h; yp++)
putpixel(tex, xp, yp, (xp+yp)&1 ? makecol(255, 255, 255) : makecol(0, 0, 0));
/* seed some variables */
float x = 0, xmul = 0, ymul = 0, z = 0;
while(!key[KEY_ESC])
{
clear_to_color(back, makecol(0, 0, 0));
DrawPerspective(back, tex, SCREEN_H >> 1, 20, SCREEN_H, 1, x, xmul, ymul, z);
blit(back, screen, 0, 0, 0, 0, back->w, back->h);
if(key[KEY_LEFT]) x+= 0.05f;
if(key[KEY_RIGHT]) x-= 0.05f;
if(key[KEY_UP]) z+= 0.05f;
if(key[KEY_DOWN]) z-= 0.05f;
if(key[KEY_P]) xmul-= 0.1f;
if(key[KEY_O]) xmul+= 0.1f;
if(key[KEY_A]) ymul-= 0.1f;
if(key[KEY_Q]) ymul+= 0.1f;
}
destroy_bitmap(back);
destroy_bitmap(tex);
return 0;
}
END_OF_MAIN()

produces this:

Courtesy of Thomas Harte over at gamedev.net