Files
raycaster/raycaster.c
T
hans 3f6caff7e6 Fix texture map
Add door to map
Added ability to "open" a door by pressing e

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
2026-09-02 18:53:11 +02:00

1223 lines
35 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <GLUT/glut.h>
#include <math.h>
#define PI 3.1415926535f
#define PI2 PI / 2
#define PI3 3 * PI / 2
#define DR 0.0174533f // One degree in radians
#define gameWidth 320
#define gameHeight 256
#define GAP 20
#define MAP_WIDTH (mapX * mapSize)
#define MAP_HEIGHT (mapY * mapSize)
#define TOTAL_WIDTH (MAP_WIDTH + GAP + gameWidth)
#define TOTAL_HEIGHT (MAP_HEIGHT > gameHeight ? MAP_HEIGHT : gameHeight)
// 0° => 0 radians (RIGHT)
// 90° => π/2 radians (TOP)
// 180° => π radians (LEFT)
// 270° => 3π/2 radians (BOTTOM)
// 360° => 2π radians (RIGHT)
// *MARK: Define the world
int mapX = 8, mapY = 8, mapSize = 32;
float movementRestriction;
// clang-format off
int mapWalls[] =
{
1,1,1,1,1,1,1,1,
1,0,0,0,0,0,0,1,
1,0,1,0,0,0,0,1,
1,0,4,0,0,0,0,1,
1,0,0,0,0,0,0,1,
1,0,0,0,0,2,0,1,
1,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,
};
// clang-format on
// clang-format off
int All_Textures[] = // All 32x32 textures
{
// Checkboard
0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
0,0,0,0,0,1,1,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
0,0,1,1,1,1,1,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
0,0,1,1,1,1,1,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
0,0,0,0,0,1,1,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,
// Brick
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
//Window
1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
//Door
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,1,1,1,1,1, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 1,1,1,1,0,0,0,0,
0,0,0,1,0,0,0,1, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 1,0,0,1,0,0,0,0,
0,0,0,1,0,0,0,1, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 1,0,0,1,0,0,0,0,
0,0,0,1,0,0,0,1, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 1,0,0,1,0,0,0,0,
0,0,0,1,0,0,0,1, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 1,0,0,1,0,0,0,0,
0,0,0,1,0,0,0,1, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 1,0,0,1,0,0,0,0,
0,0,0,1,0,0,0,1, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 1,0,0,1,0,0,0,0,
0,0,0,1,0,0,0,1, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 1,0,0,1,0,0,0,0,
0,0,0,1,0,0,0,1, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 1,0,0,1,0,0,0,0,
0,0,0,1,0,0,0,1, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 1,0,0,1,0,0,0,0,
0,0,0,1,0,0,0,1, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 1,0,0,1,0,0,0,0,
0,0,0,1,1,1,1,1, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 1,1,1,1,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,1,0,0,1, 1,0,1,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,1,1,1,0,0,1, 1,0,1,1,1,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,0,
};
// clang-format on
typedef struct
{
int w, a, d, s; // Button state on/off
} ButtonKeys;
ButtonKeys Keys;
// *MARK: HELPERS
// Function to check if a number is a power of 2
int isPowerOfTwo(int n)
{
return (n > 0) && ((n & (n - 1)) == 0);
}
float degToRad(float a) { return a * M_PI / 180.0; }
float FixAng(float a)
{
if (a > 359)
{
a -= 360;
}
if (a < 0)
{
a += 360;
}
return a;
}
// * Returns the distance between the player and the ray's endpoint
// That's the hypotenuse, so we can use the Pythagorean theorem.
float distance(float ax, float ay, float bx, float by, float ang)
{
// SOH - CAH - TOA
return (sqrt((bx - ax) * (bx - ax) + (by - ay) * (by - ay)));
}
// *MARK: PLAYER
// Player position (playerX, playerY), delta (playerDeltaX, playerDeltaY), and angle (pa)
float playerX, playerY, playerDeltaX, playerDeltaY, playerAngle;
// Draw the player on the map
void drawPlayer2D()
{
// Draws the player square
glColor3f(1, 1, 0);
glPointSize(8);
glBegin(GL_POINTS);
glVertex2i(playerX, playerY);
glEnd();
// Draws the player direction
glLineWidth(4);
glBegin(GL_LINES);
glVertex2i(playerX, playerY);
glVertex2i(playerX + playerDeltaX * 5, playerY + playerDeltaY * 5);
glEnd();
}
// *MARK: Draw Rays and Walls
void drawRays2D()
{
int r, mx, my, mapPoint, dof;
// Ensure mapSize is a power of 2, otherwise default or handle error
if (!isPowerOfTwo(mapSize))
{
fprintf(stderr, "Error: mapSize must be a power of 2 (e.g., 16, 32, 64, 128).\n");
exit(1);
}
int shiftSize = (int)(log2f((float)mapSize));
// Ray x, y, angle
float rayX, rayY, rayAngle;
float xo, yo;
// Final distance
float disT;
// Let's initialize the ray angle n degrees from the player's facing position
// ra = ray angle; pa = player angle; DR = degree radian; n = degrees to use
rayAngle = playerAngle - DR * 30;
// rayAngle = FixAng(playerAngle+30);
// Make sure to limit the next angle
if (rayAngle < 0)
{
rayAngle += 2 * PI;
}
if (rayAngle > 2 * PI)
{
rayAngle -= 2 * PI;
}
// * Cast n rays
for (r = 0; r < 60; r++)
{
// * --- Check Horizontal Lines ---
dof = 0;
// We need to get the shortest ray cast in either the vertical or horizontal
// distance. So, we'll set a default that's way higher than we'll ever
// calculate, and save the horizontal ray's X and Y position.
float distanceHor = 1000000, hx = playerX, hy = playerY;
// Negative inverse of tangent
float aTan = -1.0f / tanf(rayAngle);
// Check if the ray is looking up or down.
// If it's less than π (e.g., 180°), we're looking down.
// ? Looking up (ray angle > π)
if (rayAngle > PI)
{
// Round the ray's Y position to the nearest 64th value.
// We can do this by bit-shifting down by `shiftSize`, then bit-shifting back up by
// `shiftSize`. Then, we subtract a small amount for accuracy.
rayY = (((int)playerY >> shiftSize) << shiftSize) - 0.0001f;
// The ray's X value is the distance between the player and the ray's Y
// position, times the inverse of tangent, plus the player's X position.
rayX = (playerY - rayY) * aTan + playerX;
// Now that we have the ray's first grid intersection position, we need
// the next X and Y offset for the second grid intersection point.
yo = -mapSize;
xo = -yo * aTan;
}
// ? Looking down
if (rayAngle < PI)
{
// This is almost exactly the same as looking up, but we add one grid
// unit, instead of subtracting a tiny amount.
rayY = (((int)playerY >> shiftSize) << shiftSize) + mapSize;
rayX = (playerY - rayY) * aTan + playerX;
// This is almost exactly the same as looking up, but we add instead of
// subtract.
yo = mapSize;
xo = -yo * aTan;
}
// ? Looking straight left or right
if (rayAngle == 0.0f || rayAngle == PI)
{
// It's impossible to ever hit a horizontal line
rayX = playerX;
rayY = playerY;
}
int mv = 0, mh = 0;
// ? We don't want to get caught in an infinite loop, so use the
// depth-of-field to limit our check to a max of 8.
while (dof < 8)
{
// We know the coordinates where the ray will hit the wall, but we need
// to know where that is in the mapWalls array.
// Let's take the ray hit position (rx,ry) and divide it by 64 (bit-shift
// 6 places), and use that to find the position in the mapWalls's array.
mx = (int)(rayX) >> shiftSize;
my = (int)(rayY) >> shiftSize;
mapPoint = my * mapX + mx;
// If the mapWalls position is less than the array's size, then we can check it
// inside the mapWalls. If the value is 1, there's a wall. If we hit a wall,
// we're done!
if (mapPoint > 0 && mapPoint < mapX * mapY && mapWalls[mapPoint] > 0)
{
// We hit a wall!
mh = mapWalls[mapPoint];
// Save the values and calculate the distance from the player
hx = rayX;
hy = rayY;
distanceHor = distance(playerX, playerY, hx, hy, rayAngle);
dof = 8; // Break the loop - no need to calculate more.
}
else
{
// If we didn't hit a wall, we check the next horizontal line by adding
// the x and y offset.
rayX += xo;
rayY += yo;
dof += 1;
}
}
// * --- Check Vertical Lines ---
dof = 0;
float distanceVert = 1000000, vx = playerX, vy = playerY;
// Negative of tangent
float nTan = -tanf(rayAngle);
// ? Looking left
if (rayAngle > PI2 && rayAngle < PI3)
{
rayX = (((int)playerX >> shiftSize) << shiftSize) - 0.0001f;
rayY = (playerX - rayX) * nTan + playerY;
xo = -mapSize;
yo = -xo * nTan;
}
// ? Looking right
if (rayAngle < PI2 || rayAngle > PI3)
{
rayX = (((int)playerX >> shiftSize) << shiftSize) + mapSize;
rayY = (playerX - rayX) * nTan + playerY;
xo = mapSize;
yo = -xo * nTan;
}
// ? Looking straight up or down
if (rayAngle == 0.0f || rayAngle == PI)
{
rayX = playerX;
rayY = playerY;
}
while (dof < 8)
{
// We know the coordinates where the ray will hit the wall, but we need
// to know where that is in the mapWalls array.
// Let's take the ray hit position (rx,ry) and divide it by 64 (bit-shift
// 6 places), and use that to find the position in the mapWalls's array.
mx = (int)(rayX) >> shiftSize;
my = (int)(rayY) >> shiftSize;
mapPoint = my * mapX + mx;
// If the mapWalls position is less than the array's size, then we can check it
// inside the mapWalls. If the value is 1, there's a wall. If we hit a wall,
// we're done!
if (mapPoint > 0 && mapPoint < mapX * mapY && mapWalls[mapPoint] > 0)
{
// We hit a wall!
mv = mapWalls[mapPoint];
// Save the values and calculate the distance from the player
vx = rayX;
vy = rayY;
distanceVert = distance(playerX, playerY, vx, vy, rayAngle);
dof = 8; // Break the loop - no need to calculate more.
}
else
{
// If we didn't hit a wall, we check the next horizontal line by adding
// the x and y offset.
rayX += xo;
rayY += yo;
dof += 1;
}
}
// * Compare shortest horizontal and vertical line, then use the shortest
// This is what actually determines our ray that hits a wall!
// Vertical wall hit
if (distanceVert < distanceHor)
{
rayX = vx;
rayY = vy;
disT = distanceVert;
}
// Horizontal wall hit
if (distanceHor < distanceVert)
{
rayX = hx;
rayY = hy;
disT = distanceHor;
}
// * Draw the ray
glColor3f(0, 1, 1);
glLineWidth(3);
glBegin(GL_LINES);
glVertex2i(playerX, playerY);
glVertex2i(rayX, rayY);
glEnd();
// * Increase the ray angle to draw the next ray
rayAngle += DR;
if (rayAngle < 0)
{
rayAngle += 2 * PI;
}
if (rayAngle > 2 * PI)
{
rayAngle -= 2 * PI;
}
}
}
// * Draws the top-down map with white alls and black floors
void drawMap2D()
{
// glColor3f(0, 1, 1);
// glBegin(GL_QUADS);
// glVertex2i(0, 0);
// glVertex2i(TOTAL_WIDTH, 0);
// glVertex2i(TOTAL_WIDTH, (int)(TOTAL_HEIGHT / 2));
// glVertex2i(0, (int)(TOTAL_HEIGHT / 2));
// glEnd();
// glColor3f(0, 0, 1);
// glBegin(GL_QUADS);
// glVertex2i(0, (int)(TOTAL_HEIGHT / 2));
// glVertex2i(TOTAL_WIDTH, (int)(TOTAL_HEIGHT / 2));
// glVertex2i(TOTAL_WIDTH, TOTAL_HEIGHT);
// glVertex2i(0, TOTAL_HEIGHT);
// glEnd();
int gridMapSize = mapSize;
int x, y, xo, yo;
for (y = 0; y < mapY; y++)
{
for (x = 0; x < mapX; x++)
{
// Check if it's a wall
if (mapWalls[y * mapX + x] > 0)
{
// Color walls (white)
glColor3f(1, 1, 1);
}
else
{
// Color floors (black)
glColor3f(0, 0, 0);
}
xo = x * gridMapSize;
yo = y * gridMapSize;
glBegin(GL_QUADS);
// Draw each block; the +-1 creates the outline
glVertex2i(xo + 1, yo + 1);
glVertex2i(xo + 1, yo + gridMapSize - 1);
glVertex2i(xo + gridMapSize - 1, yo + gridMapSize - 1);
glVertex2i(xo + gridMapSize - 1, yo + 1);
glEnd();
}
}
}
// * The actual raycaster
void drawRays3D()
{
int r, mx, my, mapPoint, dof;
int gridMapSize = mapSize;
// Ensure mapSize is a power of 2, otherwise default or handle error
if (!isPowerOfTwo(mapSize))
{
fprintf(stderr, "Error: mapSize must be a power of 2 (e.g., 16, 32, 64, 128).\n");
exit(1);
}
int shiftSize = (int)(log2f((float)mapSize));
// Ray x, y, angle
float rayX, rayY, rayAngle;
float xo, yo;
// Final distance
float disT;
// Let's initialize the ray angle n degrees from the player's facing position
// ra = ray angle; pa = player angle; DR = degree radian; n = degrees to use
rayAngle = playerAngle - DR * 30;
// Make sure to limit the next angle
if (rayAngle < 0)
{
rayAngle += 2 * PI;
}
if (rayAngle > 2 * PI)
{
rayAngle -= 2 * PI;
}
glPointSize(2.0f);
glBegin(GL_POINTS);
// * Cast n rays
for (r = 0; r < 60; r++)
{
int verticalMapTexture = 0;
int horizontalMapTexture = 0;
// * --- Check Horizontal Lines ---
dof = 0;
// We need to get the shortest ray cast in either the vertical or horizontal
// distance. So, we'll set a default that's way higher than we'll ever
// calculate, and save the horizontal ray's X and Y position.
float distanceHor = 1000000, hx = playerX, hy = playerY;
// Negative inverse of tangent
float aTan = -1.0f / tanf(rayAngle);
// Check if the ray is looking up or down.
// If it's less than π (e.g., 180°), we're looking down.
// ? Looking up (ray angle > π)
if (rayAngle > PI)
{
// Round the ray's Y position to the nearest 64th value.
// We can do this by bit-shifting down by `shiftSize`, then bit-shifting back up by
// `shiftSize`. Then, we subtract a small amount for accuracy.
rayY = (((int)playerY >> shiftSize) << shiftSize) - 0.0001f;
// The ray's X value is the distance between the player and the ray's Y
// position, times the inverse of tangent, plus the player's X position.
rayX = (playerY - rayY) * aTan + playerX;
// Now that we have the ray's first grid intersection position, we need
// the next X and Y offset for the second grid intersection point.
yo = -gridMapSize;
xo = -yo * aTan;
}
// ? Looking down
if (rayAngle < PI)
{
// This is almost exactly the same as looking up, but we add one grid
// unit, instead of subtracting a tiny amount.
rayY = (((int)playerY >> shiftSize) << shiftSize) + gridMapSize;
rayX = (playerY - rayY) * aTan + playerX;
// This is almost exactly the same as looking up, but we add instead of
// subtract.
yo = gridMapSize;
xo = -yo * aTan;
}
// ? Looking straight left or right
if (rayAngle == 0.0f || rayAngle == PI)
{
// It's impossible to ever hit a horizontal line
rayX = playerX;
rayY = playerY;
}
int mv = 0, mh = 0;
// ? We don't want to get caught in an infinite loop, so use the
// depth-of-field to limit our check to a max of 8.
while (dof < 8)
{
// We know the coordinates where the ray will hit the wall, but we need
// to know where that is in the mapWalls array.
// Let's take the ray hit position (rx,ry) and divide it by 64 (bit-shift
// 6 places), and use that to find the position in the mapWalls's array.
mx = (int)(rayX) >> shiftSize;
my = (int)(rayY) >> shiftSize;
mapPoint = my * mapX + mx;
// If the mapWalls position is less than the array's size, then we can check it
// inside the mapWalls. If the value is 1, there's a wall. If we hit a wall,
// we're done!
if (mapPoint > 0 && mapPoint < mapX * mapY && mapWalls[mapPoint] > 0)
{
// We hit a wall!
mh = mapWalls[mapPoint];
// Save the values and calculate the distance from the player
hx = rayX;
hy = rayY;
distanceHor = distance(playerX, playerY, hx, hy, rayAngle);
verticalMapTexture = mapWalls[mapPoint] - 1;
dof = 8; // Break the loop - no need to calculate more.
}
else
{
// If we didn't hit a wall, we check the next horizontal line by adding
// the x and y offset.
rayX += xo;
rayY += yo;
dof += 1;
}
}
// * --- Check Vertical Lines ---
dof = 0;
float distanceVert = 1000000, vx = playerX, vy = playerY;
// Negative of tangent
float nTan = -tanf(rayAngle);
// ? Looking left
if (rayAngle > PI2 && rayAngle < PI3)
{
rayX = (((int)playerX >> shiftSize) << shiftSize) - 0.0001f;
rayY = (playerX - rayX) * nTan + playerY;
xo = -gridMapSize;
yo = -xo * nTan;
}
// ? Looking right
if (rayAngle < PI2 || rayAngle > PI3)
{
rayX = (((int)playerX >> shiftSize) << shiftSize) + gridMapSize;
rayY = (playerX - rayX) * nTan + playerY;
xo = gridMapSize;
yo = -xo * nTan;
}
// ? Looking straight up or down
if (rayAngle == 0.0f || rayAngle == PI)
{
rayX = playerX;
rayY = playerY;
}
while (dof < 8)
{
// We know the coordinates where the ray will hit the wall, but we need
// to know where that is in the mapWalls array.
// Let's take the ray hit position (rx,ry) and divide it by 64 (bit-shift
// 6 places), and use that to find the position in the mapWalls's array.
mx = (int)(rayX) >> shiftSize;
my = (int)(rayY) >> shiftSize;
mapPoint = my * mapX + mx;
// If the mapWalls position is less than the array's size, then we can check it
// inside the mapWalls. If the value is 1, there's a wall. If we hit a wall,
// we're done!
if (mapPoint > 0 && mapPoint < mapX * mapY && mapWalls[mapPoint] > 0)
{
// We hit a wall!
mv = mapWalls[mapPoint];
// Save the values and calculate the distance from the player
vx = rayX;
vy = rayY;
distanceVert = distance(playerX, playerY, vx, vy, rayAngle);
verticalMapTexture = mapWalls[mapPoint] - 1;
dof = 8; // Break the loop - no need to calculate more.
}
else
{
// If we didn't hit a wall, we check the next horizontal line by adding
// the x and y offset.
rayX += xo;
rayY += yo;
dof += 1;
}
}
// * Compare shortest horizontal and vertical line, then use the shortest
// This is what actually determines our ray that hits a wall!
// Vertical wall hit
if (distanceVert < distanceHor)
{
horizontalMapTexture = verticalMapTexture;
rayX = vx;
rayY = vy;
disT = distanceVert;
// Vertical walls are drawn a little brighter than horizontal walls
glColor3f(0.9, 0, 0);
if (mv == 2)
{
glColor3f(0, 0, 0.7);
}
}
// Horizontal wall hit
if (distanceHor < distanceVert)
{
horizontalMapTexture = mh - 1;
rayX = hx;
rayY = hy;
disT = distanceHor;
// Horizontal walls are drawn a little darker than vertical walls
glColor3f(0.7, 0, 0);
if (mh == 2)
{
glColor3f(0, 0, 0.4);
}
}
// * --- Draw 3D Walls ---
// Correct the fish-eye effect
float correctedAngle = playerAngle - rayAngle;
if (correctedAngle < 0)
{
correctedAngle += 2 * PI;
}
if (correctedAngle > 2 * PI)
{
correctedAngle -= 2 * PI;
}
disT = disT * cos(correctedAngle);
// The formula for each line height will be the cube size (64) times our
// screen height, divided by the ray distance (disT).
// The further away, the larger the distance and the shorter the final line
// height will be.
float lineHeight = (mapSize * gameHeight) / disT;
float textureYstep = mapSize / (float)lineHeight;
float textureYoffset = 0;
if (lineHeight > gameHeight)
{
textureYoffset = (lineHeight - gameHeight) / 2.0;
lineHeight = gameHeight;
}
// Let's make sure the line doesn't exceed our vertical space, so we cap it
if (lineHeight > gameWidth)
{
lineHeight = gameWidth;
}
// We need to offset the line to the middle of the screen
// That's the full window height, minus half the line height
float lineOffset = (gameHeight / 2.0f) - (lineHeight / 2.0f);
// Calculate dynamic column width and horizontal starting offset
float rayWidth = (float)gameWidth / 60.0f;
int xStart = (int)floorf(r * rayWidth);
int xEnd = (int)floorf((r + 1) * rayWidth);
float rayX3D = MAP_WIDTH + GAP + (r * rayWidth);
float shade = 1;
glColor3f(0, 0.8, 0);
if (distanceVert < distanceHor)
{
shade = 0.5;
rayX = vx;
rayY = vy;
distanceHor = distanceVert;
glColor3f(0, 0.6, 0);
}
// * Draw the "wall" line
int x, y;
int textureX;
if (shade == 1) // North-south walls (PI -> (2 * PI))
{
textureX = (int)(rayX / 2.0) % 32;
if (rayAngle < PI) // South walls
{
textureX = 31 - textureX;
}
}
else // East-west walls (PI2 -> PI3)
{
textureX = (int)(rayY / 2.0) % 32;
if (rayAngle > PI2 || rayAngle < PI3) // West
{
textureX = 31 - textureX;
}
}
for (x = xStart; x < xEnd; x++)
{
float textureY = textureYoffset * textureYstep + (horizontalMapTexture * 32);
for (y = 0; y < lineHeight; y++)
{
float c = All_Textures[((int)textureY * mapSize) + textureX] * shade;
glColor3f(c, c, c);
glVertex2i(MAP_WIDTH + GAP + x, (int)(y + lineOffset));
textureY += textureYstep;
}
}
// * Increase the ray angle to draw the next ray
rayAngle += DR;
if (rayAngle < 0)
{
rayAngle += 2 * PI;
}
if (rayAngle > 2 * PI)
{
rayAngle -= 2 * PI;
}
}
glEnd();
}
// * Draw ceiling
void drawCeiling()
{
float halfHeight = (float)(gameHeight / 2);
glBegin(GL_QUADS);
for (float distance = 0; distance < halfHeight; distance += 1.0f)
{
// Normalize color from 0.0 to 1.0 based on distance, multiplied by relative intensity
float colorIntensity = 0.5f + (distance / halfHeight);
glColor3f(0.25 * colorIntensity, 0.25 * colorIntensity, 0.25 * colorIntensity);
// Draw a horizontal strip at the current distance
glVertex2f(MAP_WIDTH + GAP, distance);
glVertex2f(MAP_WIDTH + GAP, distance + 1.0f);
glVertex2f(TOTAL_WIDTH, distance + 1.0f);
glVertex2f(TOTAL_WIDTH, distance);
}
glEnd();
}
// * Draw floor
void drawFloor()
{
float halfHeight = (float)(gameHeight / 2);
glBegin(GL_QUADS);
for (float distance = 0; distance < halfHeight; distance += 1.0f)
{
float colorIntensity = 0.5f + (distance / halfHeight);
glColor3f(0.25 * colorIntensity, 0.25 * colorIntensity, 0.0f);
float y = halfHeight + distance;
glVertex2f(MAP_WIDTH + GAP, y);
glVertex2f(MAP_WIDTH + GAP, y + 1.0f);
glVertex2f(TOTAL_WIDTH, y + 1.0f);
glVertex2f(TOTAL_WIDTH, y);
}
glEnd();
}
// *MARK: MAIN
float frame1, frame2, fps;
// *MARK: Main rendering
void display()
{
// Frames per second
frame2 = glutGet(GLUT_ELAPSED_TIME);
if (frame1 < 0)
{
frame1 = 10;
}
fps = (frame2 - frame1);
frame1 = glutGet(GLUT_ELAPSED_TIME);
float movementMultiplier = 0.05 * fps;
// * Handle keyboard inputs
// ? Move left
if (Keys.a == 1)
{
// We change the angle by a small value because we're not working with
// degrees. In C, we instead use radians, which is 0 -> 2π.
//
// 0° -> 360° ≈ 0 -> ~6.28
// Change player angle slightly (using radians)
playerAngle -= 0.1 * movementMultiplier;
// If player angle drops below 0, let's reset back to 2π
if (playerAngle < 0)
{
playerAngle += 2 * PI;
}
// The values returned from sin() and cos() are very small, so we'll just
// artificialy inflate them by multilying by 5. This is our actual movement.
playerDeltaX = cos(playerAngle) * 5.0f;
playerDeltaY = sin(playerAngle) * 5.0f;
}
// ? Move right
if (Keys.d == 1)
{
playerAngle += 0.1 * movementMultiplier;
if (playerAngle > 2 * PI)
{
playerAngle -= 2 * PI;
}
playerDeltaX = cos(playerAngle) * 5.0f;
playerDeltaY = sin(playerAngle) * 5.0f;
}
// We need to check if we're going to bump into a wall and prevent that from happening.
int xOffset = 0;
if (playerDeltaX < 0)
{
xOffset = -movementRestriction;
}
else
{
xOffset = movementRestriction;
}
int yOffset = 0;
if (playerDeltaY < 0)
{
yOffset = -movementRestriction;
}
else
{
yOffset = movementRestriction;
}
// Convert pixel positions to map grid coordinates dynamically using mapSize
int iPlayerX = (int)playerX / mapSize;
int iPlayerY = (int)playerY / mapSize;
int iPlayerX_add_xOffset = (int)(playerX + xOffset) / mapSize;
int iPlayerY_add_yOffset = (int)(playerY + yOffset) / mapSize;
int iPlayerX_sub_xOffset = (int)(playerX - xOffset) / mapSize;
int iPlayerY_sub_yOffset = (int)(playerY - yOffset) / mapSize;
// ? Move forward
if (Keys.w == 1)
{
// We change the X and Y values based on our delta, which is calculated
// during our rotation.
if (mapWalls[iPlayerY * mapX + iPlayerX_add_xOffset] == 0)
{
playerX += playerDeltaX * movementMultiplier;
}
if (mapWalls[iPlayerY_add_yOffset * mapX + iPlayerX] == 0)
{
playerY += playerDeltaY * movementMultiplier;
}
}
// ? Move backwards
if (Keys.s == 1)
{
if (mapWalls[iPlayerY * mapX + iPlayerX_sub_xOffset] == 0)
{
playerX -= playerDeltaX * movementMultiplier;
}
if (mapWalls[iPlayerY_sub_yOffset * mapX + iPlayerX] == 0)
{
playerY -= playerDeltaY * movementMultiplier;
}
}
glutPostRedisplay();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawMap2D();
drawRays2D();
drawPlayer2D();
drawCeiling();
drawFloor();
drawRays3D();
glutSwapBuffers();
}
// *MARK: Key input handling
void ButtonDown(unsigned char key, int x, int y)
{
if (key == 'a')
{
Keys.a = 1;
}
if (key == 'd')
{
Keys.d = 1;
}
if (key == 'w')
{
Keys.w = 1;
}
if (key == 's')
{
Keys.s = 1;
}
if (key == 'e')
{
int xOffset = (playerDeltaX < 0) ? -25 : 25;
int yOffset = (playerDeltaY < 0) ? -25 : 25;
int iPlayerX = playerX / mapSize, iPlayerX_add_xOffset = (playerX + xOffset) / mapSize;
int iPlayerY = playerY / mapSize, iPlayerY_add_yOffset = (playerY + yOffset) / mapSize;
if (mapWalls[iPlayerY * mapX + iPlayerX_add_xOffset] == 4)
mapWalls[iPlayerY * mapX + iPlayerX_add_xOffset] = 0;
if (mapWalls[iPlayerY_add_yOffset * mapX + iPlayerX] == 4)
mapWalls[iPlayerY_add_yOffset * mapX + iPlayerX] = 0;
}
glutPostRedisplay();
}
void ButtonUp(unsigned char key, int x, int y)
{
if (key == 'a')
{
Keys.a = 0;
}
if (key == 'd')
{
Keys.d = 0;
}
if (key == 'w')
{
Keys.w = 0;
}
if (key == 's')
{
Keys.s = 0;
}
glutPostRedisplay();
}
// * Window resizing
void resize(int w, int h)
{
// Prevent division by zero
if (h == 0)
h = 1;
// Map the orthographic scene bounds to fill the entire window frame
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Set logical coordinate space: (left, right, bottom, top)
gluOrtho2D(0, TOTAL_WIDTH, TOTAL_HEIGHT, 0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// *MARK: Init and main
void init()
{
// Sets the screen to the given color (grey)
glClearColor(0.3, 0.3, 0.3, 0);
// Sets the window to be 2D at a given size
// gluOrtho2D(0, 1024, 512, 0);
// gluOrtho2D(0, TOTAL_WIDTH, TOTAL_HEIGHT, 0);
// Spawn player centered in grid tile (1, 1)
playerX = 1.5f * mapSize;
playerY = 1.5f * mapSize;
movementRestriction = mapSize / 4;
playerAngle = 0;
// Calculate the initial player deltaX and deltaY
playerDeltaX = cos(playerAngle) * 5.0f;
playerDeltaY = sin(playerAngle) * 5.0f;
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
// glutInitWindowSize(TOTAL_WIDTH, TOTAL_HEIGHT);
glutInitWindowSize(1024, 512);
// Initialize the window to the center of the screen
glutInitWindowPosition(200, 200);
glutCreateWindow("Raycaster");
init();
// Send display parameters to the display function
glutDisplayFunc(display);
// Handle window resizes
glutReshapeFunc(resize);
// Send key presses to the keyboard function
glutKeyboardFunc(ButtonDown);
glutKeyboardUpFunc(ButtonUp);
glutMainLoop();
}