FORTRAN Computer Games


Ray Casting

Ray casting is a technique in computer graphics to render a 3-D environment from 2-D map data in software, based on perspective projection. Early computer and video games, like Catacomb 3-D, Wolfenstein 3-D, or Ken’s Labyrinth, use ray casting to create a first-person view. Due to the limitations of the approach, the rendered world is not truely in 3-D, but often seen as 2.5-D instead.

ray casting in FORTRAN 77
Fig. 1: Ray casting in FORTRAN 77 on X11

In FORTRAN, an additional library is required to render to the screen. The EGGX/ProCALL library provides basic drawing routines for X11. The following example program allows the player to move freely around in a room made of blocks (fig. 1), with simple collision detection applied. The map array MAPY of size MW, MH stores the position and colour of each block. The shade of the colour is chosen depending on the orientation of a particular wall.

Functions & Subroutines

The FORTRAN program calls six subroutines for input control, movement, and rendering:

CALL INPUT(MAPY, MW, MH, POSX, POSY, DX, DY, PLANEX, PLANEY, IKEY)
Moves or rotates the view point of the player on keyboard input. The code of the pressed key is returned in IKEY.
CALL MOVE(MAPY, MW, MH, X, Y, DX, DY, SPEED)
Moves the player on the map in direction DX, DY by SPEED.
CALL RAYCST(MAPY, MW, MH, IWIN, IW, IH, IX, POSX, POSY, DX, DY, PLANEX, PLANEY)
Casts a ray in IX on the camera plane and renders a single column of pixels in the colour of the wall hit.
CALL RENDER(MAPY, MW, MH, IWIN, IW, IH, POSX, POSY, DX, DY, PLANEX, PLANEY)
Renders the scene by drawing the floor and casting 640 rays from the player’s view point, then copies the double buffer to screen.
CALL ROTATE(X, Y, A)
Rotates a vector by angle A.
CALL XWIN(IWIN, IW, IH)
Opens an X11 windows using the EGGX/ProCALL library.

Program Listing

We can alter the parameters IW and IH to change the size of the X11 window. Use the arrows key for movement. Copy and save the program as file raycast.f locally.

C     ******************************************************************
C
C     SIMPLE 2.5-D RAY CASTING ENGINE FOR X11 IN FORTRAN 77, USING THE
C     EGGX/PROCALL LIBRARY.
C
C     ******************************************************************
      PROGRAM RAYCAST
      EXTERNAL GCLOSE, INPUT, MSLEEP, RENDER, XWIN

      INTEGER IDELAY, IESC, IW, IH, MW, MH
      PARAMETER (IDELAY=20, IESC=27, IW=640, IH=480, MW=12, MH=12)

      INTEGER IKEY, IWIN
      INTEGER MAPY(MW, MH)
      REAL    DX, DY, PLANEX, PLANEY, POSX, POSY
C
C     SET INITIAL POSITION AND DIRECTION.
C
      POSX   =  10.5
      POSY   =  10.5
      DX     = -1.0
      DY     =  0.0
      PLANEX =  0.0
      PLANEY =  0.66
C
C     OPEN NEW X11 WINDOW.
C
      CALL XWIN(IWIN, IW, IH)
C
C     MAIN LOOP, RUNS UNTIL USER HITS ESCAPE.
C
   10 CONTINUE
      CALL INPUT(MAPY, MW, MH, POSX, POSY, DX, DY, PLANEX, PLANEY, IKEY)
      CALL RENDER(MAPY, MW, MH, IWIN, IW, IH, POSX, POSY, DX, DY,
     &            PLANEX, PLANEY)
      CALL MSLEEP(IDELAY)
      IF (IKEY .NE. IESC) GOTO 10

      CALL GCLOSE(IWIN)
C
C     MAP DATA.
C
      DATA MAPY /02,07,07,03,07,03,07,03,07,03,07,01,
     &           02,00,00,00,00,00,00,00,00,00,00,01,
     &           02,00,00,00,00,00,00,00,00,00,00,02,
     &           04,00,07,02,00,00,00,00,00,00,00,01,
     &           02,00,07,00,00,00,00,00,00,00,00,02,
     &           04,00,00,00,02,02,02,00,00,00,00,01,
     &           02,00,00,00,02,03,03,00,00,00,00,02,
     &           02,00,00,00,00,00,00,00,00,00,00,01,
     &           02,00,00,00,00,00,00,00,00,05,00,02,
     &           02,00,04,04,04,00,00,00,00,05,00,01,
     &           02,00,00,00,00,00,00,00,00,00,00,02,
     &           02,03,03,03,03,03,03,03,03,03,03,01/
      END
C     ******************************************************************
      SUBROUTINE INPUT(MAPY, MW, MH, POSX, POSY, DX, DY,
     &                 PLANEX, PLANEY, IKEY)
C
C     PROCESS KEYBOARD INPUT AND MOVES/ROTATES PLAYER.
C
      EXTERNAL GGETCH, MOVE, ROTATE

      INTEGER IDOWN, ILEFT, IRIGHT, IUP
      REAL    ALPHA, SPEED
      PARAMETER (IDOWN=31, ILEFT=29, IRIGHT=28, IUP=30)
      PARAMETER (ALPHA=0.1, SPEED=0.15)

      INTEGER MW, MH, MAPY(MW, MH), IKEY
      REAL    POSX, POSY, DX, DY, PLANEX, PLANEY

      CALL GGETCH(IKEY)

      IF (IKEY .EQ. IUP) THEN
C
C       MOVE FORWARD.
C
        CALL MOVE(MAPY, MW, MH, POSX, POSY, DX, DY, SPEED)
      ELSE IF (IKEY .EQ. IDOWN) THEN
C
C       MOVE BACKWARD.
C
        CALL MOVE(MAPY, MW, MH, POSX, POSY, DX, DY, -SPEED)
      ELSE IF (IKEY .EQ. ILEFT) THEN
C
C       TURN LEFT.
C
        CALL ROTATE(DX, DY, ALPHA)
        CALL ROTATE(PLANEX, PLANEY, ALPHA)
      ELSE IF (IKEY .EQ. IRIGHT) THEN
C
C       TURN RIGHT.
C
        CALL ROTATE(DX, DY, -ALPHA)
        CALL ROTATE(PLANEX, PLANEY, -ALPHA)
      END IF
      END
C     ******************************************************************
      SUBROUTINE MOVE(MAPY, MW, MH, X, Y, DX, DY, SPEED)
C
C     MOVES PLAYER IN DX, DY.
C
      INTEGER MW, MH, MAPY(MW, MH)
      REAL    X, Y, DX, DY, SPEED
      REAL    NX, NY

      NX = X + DX * SPEED
      NY = Y + DY * SPEED

      IF (NX .LE. 1 .OR. NX .GE. MW .OR.
     &    NY .LE. 1 .OR. NY .GE. MH) RETURN

      IF (MAPY(INT(NX) + 1, INT(Y) + 1) .EQ. 0) X = NX
      IF (MAPY(INT(X) + 1, INT(NY) + 1) .EQ. 0) Y = NY
      END
C     ******************************************************************
      SUBROUTINE RAYCST(MAPY, MW, MH, IWIN, IW, IH, IX, POSX, POSY,
     &                  DX, DY, PLANEX, PLANEY)
C
C     RENDERS SINGLE COLUMN OF PIXELS AT IX.
C
      EXTERNAL DRAWLINE, NEWPENCOLOR

      INTEGER MW, MH, MAPY(MW, MH), IWIN, IW, IH, IX
      REAL    POSX, POSY, DX, DY, PLANEX, PLANEY

      INTEGER ISIDE, ISTEPX, ISTEPY, IWALL, LENGTH, MX, MY, IH2, IW2
      LOGICAL DONE
      REAL    CAMX, DIST, DDISTX, DDISTY, RAYX, RAYY
      REAL    SIDEX, SIDEY, X, Y1, Y2

      IW2 = IW / 2
      IH2 = IH / 2

      CAMX = 2 * IX / REAL(IW) - 1
      RAYX = DX + PLANEX * CAMX
      RAYY = DY + PLANEY * CAMX

      MX = INT(POSX)
      MY = INT(POSY)

      DDISTX = ABS(1 / RAYX)
      DDISTY = ABS(1 / RAYY)

      IF (RAYX .LT. 0) THEN
        ISTEPX = -1
        SIDEX  = (POSX - MX) * DDISTX
      ELSE
        ISTEPX = 1
        SIDEX  = (MX + 1.0 - POSX) * DDISTX
      END IF

      IF (RAYY .LT. 0) THEN
        ISTEPY = -1
        SIDEY  = (POSY - MY) * DDISTY
      ELSE
        ISTEPY = 1
        SIDEY  = (MY + 1.0 - POSY) * DDISTY
      END IF

      IWALL = 0
      DONE  = .FALSE.

   10 CONTINUE
      IF (SIDEX .LT. SIDEY) THEN
        SIDEX = SIDEX + DDISTX
        MX    = MX + ISTEPX
        ISIDE = 0
      ELSE
        SIDEY = SIDEY + DDISTY
        MY    = MY + ISTEPY
        ISIDE = 1
      END IF

      IF (MAPY(MX + 1, MY + 1) .GT. 0) THEN
        IWALL = MAPY(MX + 1, MY + 1)
        DONE  = .TRUE.
      END IF
      IF (.NOT. DONE) GOTO 10

      IF (IWALL .EQ. 0) RETURN

      IF (ISIDE .EQ. 0) THEN
        DIST = SIDEX - DDISTX
      ELSE
        DIST = SIDEY - DDISTY
      END IF

      IF (ISIDE .EQ. 1) IWALL = IWALL + 8

      LENGTH = INT(IH / DIST) / 2

      X  = REAL(IX)
      Y1 = REAL(MAX(0, -LENGTH + IH2))
      Y2 = REAL(MIN(IH, LENGTH + IH2))

      CALL NEWPENCOLOR(IWIN, IWALL)
      CALL DRAWLINE(IWIN, X, Y1, X, Y2)
      END
C     ******************************************************************
      SUBROUTINE RENDER(MAPY, MW, MH, IWIN, IW, IH, POSX, POSY, DX, DY,
     &                  PLANEX, PLANEY)
C
C     RENDERS THE SCENE.
C
      EXTERNAL COPYLAYER, GCLR, FILLRECT, NEWPENCOLOR, RAYCST
      INTEGER MW, MH, MAPY(MW, MH), IWIN, IW, IH
      REAL    POSX, POSY, DX, DY, PLANEX, PLANEY
      INTEGER IX

      CALL GCLR(IWIN)
      CALL NEWPENCOLOR(IWIN, 8)
      CALL FILLRECT(IWIN, 0.0, 0.0, REAL(IW), REAL(IH / 2))

      DO 10 IX = 0, IW - 1
      CALL RAYCST(MAPY, MW, MH, IWIN, IW, IH, IX, POSX, POSY, DX, DY,
     &            PLANEX, PLANEY)
   10 CONTINUE

      CALL COPYLAYER(IWIN, 1, 0)
      END
C     ******************************************************************
      SUBROUTINE ROTATE(X, Y, A)
C
C     ROTATES X, Y BY ANGLE A [RAD].
C
      REAL X, Y, A
      REAL X0

      X0 = X
      X =  X * COS(A) - Y * SIN(A)
      Y = X0 * SIN(A) + Y * COS(A)
      END
C     ******************************************************************
      SUBROUTINE XWIN(IWIN, IW, IH)
C
C     OPENS X11 WINDOW USING EGGX/PROCALL.
C
      EXTERNAL GOPEN, GSETBGCOLOR, GSETNONBLOCK, LAYER, WINNAME
      INTEGER IWIN, IW, IH

      CALL GOPEN(IW, IH, IWIN)
      CALL WINNAME(IWIN, 'RAY CASTING' // CHAR(0))
      CALL GSETNONBLOCK(1)
      CALL GSETBGCOLOR(IWIN, 'BLACK' // CHAR(0))
      CALL LAYER(IWIN, 0, 1)
      END

Link the program against EGGX/ProCALL and X11:

$ gfortran -o raycast raycast.f libeggx.a -lX11 -lm
$ ./raycast

We can even compile the program with f2c:

$ f2c raycast.f
raycast.f:
   MAIN raycast:
   input:
   move:
   raycst:
   render:
   rotate:
   xwin:
$ cc -I/usr/local/include -L/usr/local/lib -o raycast \
  raycast.c libeggx.a -lX11 -lf2c -lm
$ ./raycast

References


Home