├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── fonts └── FreeSans.ttf ├── images ├── texture0.bmp ├── texture1.bmp ├── texture2.bmp └── texture3.bmp └── src ├── draw.c ├── fps.c ├── include ├── draw.h ├── fps.h ├── init.h ├── input.h ├── mapgen.h ├── raycastEngine.h ├── render.h └── variables.h ├── init.c ├── input.c ├── mapgen.c ├── raycastEngine.c └── render.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Outputted executable 32 | run 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Will Enright 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc -std=c99 -o run src/*.c -lSDL2 -lSDL2_ttf -lm -g 3 | clean: 4 | rm run -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Raycast-Engine 2 | ============== 3 | 4 | A simple raycast engine written in C using [SDL2.0](http://libsdl.org/) 5 | 6 | Screenshots 7 | = 8 | ![image](http://i.imgur.com/wZOwbaA.png) 9 | 10 | Building 11 | = 12 | ####Linux 13 | Install SDL2 14 | ``` 15 | sudo apt-get install libsdl2-dev libsdl2-ttf-dev 16 | ``` 17 | Then, clone this repository, or download the .zip file on the right 18 | ``` 19 | git clone https://github.com/wenright/Raycast-Engine.git 20 | ``` 21 | cd into the directory, and compile with 22 | ``` 23 | make 24 | ``` 25 | This will create an executable called run, which can be opened by typing 26 | ``` 27 | ./run 28 | ``` 29 | You can close out of the window with escape 30 | -------------------------------------------------------------------------------- /fonts/FreeSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenright/Raycast-Engine/8c5403f00ce2b7d84e84676c8df97f89962c7c35/fonts/FreeSans.ttf -------------------------------------------------------------------------------- /images/texture0.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenright/Raycast-Engine/8c5403f00ce2b7d84e84676c8df97f89962c7c35/images/texture0.bmp -------------------------------------------------------------------------------- /images/texture1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenright/Raycast-Engine/8c5403f00ce2b7d84e84676c8df97f89962c7c35/images/texture1.bmp -------------------------------------------------------------------------------- /images/texture2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenright/Raycast-Engine/8c5403f00ce2b7d84e84676c8df97f89962c7c35/images/texture2.bmp -------------------------------------------------------------------------------- /images/texture3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenright/Raycast-Engine/8c5403f00ce2b7d84e84676c8df97f89962c7c35/images/texture3.bmp -------------------------------------------------------------------------------- /src/draw.c: -------------------------------------------------------------------------------- 1 | #include "include/draw.h" 2 | 3 | void drawPoint (Uint32 x, Uint32 y, Uint32 color) { 4 | if (x > SCREEN_WIDTH | y > SCREEN_HEIGHT) return; 5 | pixelBuffer[y][x] = color; 6 | } 7 | 8 | void drawLine (Uint32 x, Uint32 y1, Uint32 y2, Uint32 color) { 9 | for (int i = y1; i < y2; i++) 10 | pixelBuffer[i][x] = color; 11 | } 12 | 13 | void drawRect (Uint32 x, Uint32 y, Uint32 w, Uint32 h, Uint32 color) { 14 | for (int i = x; i < x + w; i++) 15 | for (int j = y; j < y + h; j++) 16 | pixelBuffer[j][i] = color; 17 | } 18 | 19 | Uint32 getPixel(SDL_Surface *surface, int x, int y) { 20 | int bpp = surface->format->BytesPerPixel; 21 | Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; 22 | 23 | switch(bpp) { 24 | case 1: 25 | return *p; 26 | break; 27 | case 2: 28 | return *(Uint16 *)p; 29 | break; 30 | case 3: 31 | if(SDL_BYTEORDER == SDL_BIG_ENDIAN) 32 | return p[0] << 16 | p[1] << 8 | p[2]; 33 | else 34 | return p[0] | p[1] << 8 | p[2] << 16; 35 | break; 36 | case 4: 37 | return *(Uint32 *)p; 38 | break; 39 | default: 40 | return 0; 41 | } 42 | } -------------------------------------------------------------------------------- /src/fps.c: -------------------------------------------------------------------------------- 1 | #include "include/fps.h" 2 | 3 | //Current and previous frame time 4 | double curTime = 0, prevTime = 0; 5 | int accumulatedFPS = 0, numFrames = 0; 6 | char currentFPS[5]; 7 | 8 | double calcFPS () { 9 | prevTime = curTime; 10 | curTime = clock(); 11 | double deltaTime = (curTime - prevTime) / CLOCKS_PER_SEC; 12 | int intFPS = 1 / deltaTime; 13 | accumulatedFPS += intFPS; 14 | numFrames++; 15 | 16 | if (numFrames > 120) { 17 | sprintf(currentFPS, "%d", accumulatedFPS / numFrames); 18 | numFrames = 0; 19 | accumulatedFPS = 0; 20 | } 21 | 22 | //Delay if we updated faster than maxFPS 23 | /* 24 | if (deltaTime < maxTicks) { 25 | SDL_Delay(maxTicks - deltaTime); 26 | } 27 | */ 28 | 29 | return deltaTime; 30 | } 31 | 32 | void drawFPSToRenderer () { 33 | SDL_Color White = {255, 255, 255}; 34 | 35 | SDL_Surface* framerateTextSurface = TTF_RenderText_Solid(font, currentFPS, White); 36 | SDL_Texture* framerateTexture = SDL_CreateTextureFromSurface(renderer, framerateTextSurface); 37 | 38 | SDL_Rect r; 39 | r.x = 0; 40 | r.y = 0; 41 | r.w = 50; 42 | r.h = 50; 43 | SDL_RenderCopy(renderer, framerateTexture, NULL, &r); 44 | } -------------------------------------------------------------------------------- /src/include/draw.h: -------------------------------------------------------------------------------- 1 | #ifndef DRAW_H_INCLUDED 2 | #define DRAW_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include "variables.h" 7 | 8 | //Draws a single point. There is probably a more efficient way of doing this. 9 | void drawPoint (Uint32 x, Uint32 y, Uint32 color); 10 | 11 | //Draws a vertical line 12 | void drawLine (Uint32 x, Uint32 y1, Uint32 y2, Uint32 color); 13 | 14 | //Draws a rectangle 15 | void drawRect (Uint32 x, Uint32 y, Uint32 w, Uint32 h, Uint32 color); 16 | 17 | //Performs simple conversions to prepare a surface for accessing pixels 18 | Uint32 getPixel(SDL_Surface* surface, int x, int y); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/include/fps.h: -------------------------------------------------------------------------------- 1 | #ifndef FPS_H_INCLUDED 2 | #define FPS_H_INCLUDED 3 | 4 | #include 5 | #include "variables.h" 6 | 7 | //Frames per second to be capped at 8 | #define framerate = 60; 9 | 10 | //Surface for text to be rendered to 11 | SDL_Surface* text; 12 | 13 | //Font to be used 14 | TTF_Font* font; 15 | 16 | double calcFPS (); 17 | 18 | void drawFPSToRenderer (); 19 | 20 | #endif -------------------------------------------------------------------------------- /src/include/init.h: -------------------------------------------------------------------------------- 1 | #ifndef INIT_H_INCLUDED 2 | #define INIT_H_INCLUDED 3 | 4 | #include 5 | #include "variables.h" 6 | #include "draw.h" 7 | 8 | //Starts up SDL and creates window 9 | int init_SDL(); 10 | 11 | //Loads media 12 | int loadMedia(); 13 | 14 | //Frees media and shuts down SDL 15 | void close_SDL(); 16 | 17 | #endif -------------------------------------------------------------------------------- /src/include/input.h: -------------------------------------------------------------------------------- 1 | #ifndef INPUT_H_INCLUDED 2 | #define INPUT_H_INCLUDED 3 | 4 | #include 5 | #include "variables.h" 6 | 7 | //Handles player input, returns false if player has requested quit 8 | int handleInput(); 9 | 10 | //Move the player based on values moveX and moveY 11 | void movePlayer(double deltaTime); 12 | 13 | #endif -------------------------------------------------------------------------------- /src/include/mapgen.h: -------------------------------------------------------------------------------- 1 | #ifndef MAPGEN_H_INCLUDED 2 | #define MAPGEN_H_INCLUDED 3 | 4 | #include 5 | #include "variables.h" 6 | 7 | void generateRandomMap (); 8 | 9 | #endif -------------------------------------------------------------------------------- /src/include/raycastEngine.h: -------------------------------------------------------------------------------- 1 | #ifndef RAYCASTENGINE_H_INCLUDED 2 | #define RAYCASTENGINE_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | #include "variables.h" 8 | #include "init.h" 9 | #include "input.h" 10 | #include "draw.h" 11 | #include "render.h" 12 | #include "fps.h" 13 | #include "mapgen.h" 14 | 15 | int worldMap[MAP_WIDTH][MAP_HEIGHT]; 16 | 17 | //Initial player position 18 | double posX = 22.5, posY = 12.5, posZ = 0; 19 | //Player movement direction vector 20 | int moveLeft = false, moveRight = false, moveFwd = false, moveBck = false; 21 | //Player vector variables 22 | double dirX = -1.0, dirY = 0.0, planeX = 0.0, planeY = 0.66; 23 | //Player movement speed 24 | double speed = 5.0, rotateSpeedX = 0.0045, rotateSpeedY = 1.5; 25 | 26 | //The window we'll be rendering to 27 | SDL_Window* window; 28 | 29 | //The surface contained by the window 30 | SDL_Renderer* renderer; 31 | 32 | //The texture that we will be streaming pixels to 33 | SDL_Texture* texture; 34 | 35 | //The pixel array that is used as a buffer for the texture 36 | Uint32 pixelBuffer[SCREEN_HEIGHT][SCREEN_WIDTH]; 37 | 38 | //The image we will load and show on the screen 39 | SDL_Surface* textureSource[NUM_TEXTURES]; 40 | 41 | //Te array of pixels for each of the textures, so they don't have to be calcuated each frame 42 | Uint32 pixelArray[NUM_TEXTURES][TEXTURE_SIZE][TEXTURE_SIZE]; 43 | 44 | #endif -------------------------------------------------------------------------------- /src/include/render.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDER_H_INCLUDED 2 | #define RENDER_H_INCLUDED 3 | 4 | #include 5 | #include "draw.h" 6 | #include "variables.h" 7 | #include "fps.h" 8 | 9 | //Renders sky by writing a single color over the whole screen 10 | void renderSky (); 11 | 12 | //Renders the floor (solid color only for now) 13 | void renderFloor (); 14 | 15 | //Renders the world's walls 16 | void renderWalls (); 17 | 18 | //Updates the texture by applying it to the screen 19 | void updateTexture (); 20 | 21 | #endif -------------------------------------------------------------------------------- /src/include/variables.h: -------------------------------------------------------------------------------- 1 | #ifndef VARIABLES_H_INCLUDED 2 | #define VARIABLES_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | #define RGB_Yellow 16120320 8 | #define RGB_Green 64000 9 | #define RGB_Blue 2800 10 | #define RGB_SkyBlue 11517695 11 | #define RGB_Red 16386560 12 | #define RGB_White 16775930 13 | #define RGB_Grey 7895160 14 | 15 | #define true 1 16 | #define false 0 17 | 18 | #define maxFPS 60 19 | #define maxTicks 1000 / maxFPS 20 | 21 | #define SCREEN_WIDTH 720 22 | #define SCREEN_HEIGHT 400 23 | 24 | #define MIDDLE_X SCREEN_WIDTH / 2 25 | #define MIDDLE_Y SCREEN_HEIGHT / 2 26 | 27 | #define MAP_WIDTH 24 28 | #define MAP_HEIGHT 24 29 | 30 | #define NUM_TEXTURES 4 31 | #define TEXTURE_SIZE 64 32 | #define TEXTURE_SIZE_BINARY (int)log2(TEXTURE_SIZE) 33 | 34 | //Whether or to use textures when rendering 35 | #define textured true 36 | 37 | //Creates a world map 38 | extern int worldMap[MAP_WIDTH][MAP_HEIGHT]; 39 | 40 | //Player position 41 | extern double posX, posY, posZ; 42 | 43 | //Player movement direction vector 44 | extern int moveLeft, moveRight, moveFwd, moveBck; 45 | 46 | //Player vector variables 47 | extern double dirX, dirY, planeX, planeY; 48 | 49 | //Player movement speed 50 | extern double speed, rotateSpeedX, rotateSpeedY; 51 | 52 | //The window we'll be rendering to 53 | extern SDL_Window* window; 54 | 55 | //The surface contained by the window 56 | extern SDL_Renderer* renderer; 57 | 58 | //The texture that we will be streaming pixels to 59 | extern SDL_Texture* texture; 60 | 61 | //The pixel array that is used as a buffer for the texture 62 | extern Uint32 pixelBuffer[SCREEN_HEIGHT][SCREEN_WIDTH]; 63 | 64 | //The image we will load and show on the screen 65 | extern SDL_Surface* textureSource[NUM_TEXTURES]; 66 | 67 | //The array of pixels for each of the textures, so they don't have to be calcuated each frame 68 | extern Uint32 pixelArray[NUM_TEXTURES][TEXTURE_SIZE][TEXTURE_SIZE]; 69 | 70 | //Font used for FPS Debug 71 | extern TTF_Font* font; 72 | 73 | //Surface that font will be rendered onto 74 | extern SDL_Surface* text; 75 | 76 | #endif -------------------------------------------------------------------------------- /src/init.c: -------------------------------------------------------------------------------- 1 | #include "include/init.h" 2 | 3 | int init_SDL() { 4 | if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 5 | printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); 6 | return false; 7 | } 8 | 9 | /* 10 | SDL_DisplayMode displayMode; 11 | if (SDL_GetCurrentDisplayMode(i, &displayMode) != 0) { 12 | printf("Failed to find current resolution\n"); 13 | return false; 14 | } 15 | SCREEN_WIDTH = displayMode.w; 16 | SCREEN_HEIGHT = displayMode.h; 17 | */ 18 | 19 | //Create window 20 | window = SDL_CreateWindow("Hello World", 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_RENDERER_PRESENTVSYNC | SDL_WINDOW_INPUT_GRABBED | SDL_WINDOW_FULLSCREEN_DESKTOP); 21 | 22 | renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); 23 | 24 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); // make the scaled rendering look smoother. 25 | SDL_RenderSetLogicalSize(renderer, SCREEN_WIDTH, SCREEN_HEIGHT); 26 | 27 | if(window == NULL) { 28 | printf("Window could not be created! SDL_Error: %s\n", SDL_GetError()); 29 | return false; 30 | } 31 | 32 | if (renderer == NULL) { 33 | printf("Failed to set screen. SDL error: %s\n", SDL_GetError()); 34 | return false; 35 | } 36 | 37 | texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT); 38 | if (texture == NULL) { 39 | printf("Texture failed to initialize. SDL error: %s\n", SDL_GetError()); 40 | return false; 41 | } 42 | 43 | if (TTF_Init() != 0) { 44 | printf("TTF could not load, %s\n", SDL_GetError()); 45 | return false; 46 | } 47 | 48 | font = TTF_OpenFont("fonts/FreeSans.ttf", 16); 49 | if (font == NULL) { 50 | printf("Failed to initialize font, Error: %s\n", SDL_GetError()); 51 | return false; 52 | } 53 | 54 | //Hide/lock cursor 55 | SDL_ShowCursor(false); 56 | SDL_SetRelativeMouseMode(true); 57 | return true; 58 | } 59 | 60 | int loadMedia() { 61 | //Load wall textures 62 | for (int i = 0; i < NUM_TEXTURES; ++i) { 63 | //Allocate enough space for file path (and then some, in case someone wants to use 99999 textures) 64 | char *imagePath = malloc(24); 65 | sprintf(imagePath, "images/texture%d.bmp", i); 66 | 67 | textureSource[i] = SDL_LoadBMP(imagePath); 68 | if(textureSource[i] == NULL) { 69 | printf("Unable to load images/texture%d.bmp, SDL Error: %s\n", i, SDL_GetError()); 70 | free(imagePath); 71 | return false; 72 | } 73 | 74 | free(imagePath); 75 | } 76 | 77 | //Load texture pixel colors into a pixel array so calculations don't need to be done per frame 78 | for (int t = 0; t < NUM_TEXTURES; ++t) 79 | for (int x = 0; x < TEXTURE_SIZE; ++x) 80 | for (int y = 0; y < TEXTURE_SIZE; ++y) 81 | pixelArray[t][x][y] = getPixel(textureSource[t], x, y); 82 | 83 | return true; 84 | } 85 | 86 | void close_SDL() { 87 | //Deallocate surfaces 88 | for (int i = 0; i < NUM_TEXTURES; ++i) { 89 | SDL_FreeSurface(textureSource[i]); 90 | textureSource[i] = NULL; 91 | } 92 | 93 | //Destroy window 94 | SDL_DestroyWindow(window); 95 | window = NULL; 96 | 97 | //Exit SDL_ttf 98 | TTF_Quit(); 99 | 100 | //Quit SDL subsystems 101 | SDL_Quit(); 102 | } -------------------------------------------------------------------------------- /src/input.c: -------------------------------------------------------------------------------- 1 | #include "include/input.h" 2 | 3 | int handleInput () { 4 | SDL_Event event; 5 | 6 | while (SDL_PollEvent(&event)) { 7 | switch (event.type) { 8 | case SDL_KEYDOWN: 9 | switch( event.key.keysym.sym ){ 10 | case SDLK_LEFT: 11 | case 'a': 12 | moveLeft = true; 13 | break; 14 | case 'd': 15 | case SDLK_RIGHT: 16 | moveRight = true; 17 | break; 18 | case 'w': 19 | case SDLK_UP: 20 | moveFwd = true; 21 | break; 22 | case 's': 23 | case SDLK_DOWN: 24 | moveBck = true; 25 | break; 26 | default: 27 | break; 28 | } 29 | break; 30 | case SDL_KEYUP: 31 | switch( event.key.keysym.sym ){ 32 | case SDLK_LEFT: 33 | case 'a': 34 | moveLeft = false; 35 | break; 36 | case 'd': 37 | case SDLK_RIGHT: 38 | moveRight = false; 39 | break; 40 | case 'w': 41 | case SDLK_UP: 42 | moveFwd = false; 43 | break; 44 | case 's': 45 | case SDLK_DOWN: 46 | moveBck = false; 47 | break; 48 | case SDLK_ESCAPE: 49 | return false; 50 | break; 51 | default: 52 | break; 53 | } 54 | break; 55 | case SDL_MOUSEMOTION: 56 | if (event.motion.xrel) { 57 | double newRotateSpeed = (double)event.motion.xrel * -rotateSpeedX; 58 | double oldDirX = dirX; 59 | dirX = dirX * cos(newRotateSpeed) - dirY * sin(newRotateSpeed); 60 | dirY = oldDirX * sin(newRotateSpeed) + dirY * cos(newRotateSpeed); 61 | double oldPlaneX = planeX; 62 | planeX = planeX * cos(newRotateSpeed) - planeY * sin(newRotateSpeed); 63 | planeY = oldPlaneX * sin(newRotateSpeed) + planeY * cos(newRotateSpeed); 64 | } 65 | 66 | if ((event.motion.yrel > 0 && posZ > -750) || (event.motion.yrel < 0 && posZ < 750)) 67 | posZ += event.motion.yrel * -rotateSpeedY; 68 | 69 | SDL_WarpMouseInWindow(window, MIDDLE_X, MIDDLE_Y); 70 | break; 71 | case SDL_QUIT: 72 | return false; 73 | break; 74 | default: 75 | break; 76 | } 77 | } 78 | 79 | return true; 80 | } 81 | 82 | void movePlayer (double deltaTime) { 83 | //Move forward/backward 84 | if (moveFwd) { 85 | double moveStepX = dirX * deltaTime * speed; 86 | double moveStepY = dirY * deltaTime * speed; 87 | 88 | if (!worldMap[(int)(posX + moveStepX)][(int)posY]) 89 | posX += moveStepX; 90 | if (!worldMap[(int)(posX)][(int)(posY + moveStepY)]) 91 | posY += moveStepY; 92 | } 93 | if (moveBck) { 94 | double moveStepX = -dirX * deltaTime * speed; 95 | double moveStepY = -dirY * deltaTime * speed; 96 | 97 | if (!worldMap[(int)(posX + moveStepX)][(int)posY]) 98 | posX += moveStepX; 99 | if (!worldMap[(int)(posX)][(int)(posY + moveStepY)]) 100 | posY += moveStepY; 101 | } 102 | //Strafe 103 | if (moveRight) { 104 | //Find perp. vector by switching x and y, and negating one 105 | double moveStepX = dirY * deltaTime * speed; 106 | double moveStepY = -dirX * deltaTime * speed; 107 | 108 | if (!worldMap[(int)(posX + moveStepX)][(int)posY]) 109 | posX += moveStepX; 110 | if (!worldMap[(int)(posX)][(int)(posY + moveStepY)]) 111 | posY += moveStepY; 112 | } 113 | if (moveLeft) { 114 | double moveStepX = -dirY * deltaTime * speed; 115 | double moveStepY = dirX * deltaTime * speed; 116 | 117 | if (!worldMap[(int)(posX + moveStepX)][(int)posY]) 118 | posX += moveStepX; 119 | if (!worldMap[(int)(posX)][(int)(posY + moveStepY)]) 120 | posY += moveStepY; 121 | } 122 | } -------------------------------------------------------------------------------- /src/mapgen.c: -------------------------------------------------------------------------------- 1 | #include "include/mapgen.h" 2 | 3 | int worldMap[MAP_WIDTH][MAP_HEIGHT] = { 4 | {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, 5 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 6 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 7 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 8 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 9 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 10 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 11 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 12 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 13 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 14 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 15 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 16 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 17 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 18 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 19 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 20 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 21 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 22 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 23 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 24 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 25 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 26 | {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 27 | {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} 28 | }; 29 | 30 | void generateRandomMap () { 31 | //Generate borders 32 | for (int i = 0; i < MAP_WIDTH; ++i) { 33 | worldMap[i][0] = 1; 34 | worldMap[i][MAP_HEIGHT - 1] = 1; 35 | } 36 | for (int i = 0; i < MAP_HEIGHT; ++i) { 37 | worldMap[0][i] = 1; 38 | worldMap[MAP_HEIGHT - 1][i] = 1; 39 | } 40 | 41 | //Generate random blocks with random textures 42 | for (int i = 1; i < MAP_WIDTH - 1; ++i) 43 | for (int j = 1; j < MAP_HEIGHT - 1; ++j) 44 | if (!(rand() % 5)) 45 | worldMap[i][j] = rand() % (NUM_TEXTURES + 1); 46 | 47 | //Make sure that the position the player is at is empty 48 | worldMap[(int)posX][(int)posY] = 0; 49 | } -------------------------------------------------------------------------------- /src/raycastEngine.c: -------------------------------------------------------------------------------- 1 | #include "include/raycastEngine.h" 2 | 3 | int main(int argc, char* args[]) { 4 | if (!init_SDL() || !loadMedia()) 5 | return 1; 6 | 7 | generateRandomMap(); 8 | 9 | while (handleInput ()) { 10 | renderSky (); 11 | 12 | renderFloor (); 13 | 14 | renderWalls (); 15 | 16 | updateTexture (); 17 | 18 | movePlayer (calcFPS ()); 19 | } 20 | 21 | close_SDL(); 22 | 23 | return 0; 24 | } -------------------------------------------------------------------------------- /src/render.c: -------------------------------------------------------------------------------- 1 | #include "include/render.h" 2 | 3 | void renderSky () { 4 | //Clear the screen 5 | drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, RGB_SkyBlue); 6 | } 7 | 8 | void renderFloor () { 9 | if (posZ + MIDDLE_Y < 0) 10 | drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, RGB_Grey); 11 | else 12 | drawRect(0, MIDDLE_Y + posZ, SCREEN_WIDTH, MIDDLE_Y - posZ, RGB_Grey); 13 | } 14 | 15 | // ~85% of execution time spent here 16 | void renderWalls () { 17 | //For each pixel on the x axis 18 | for (int x = 0; x < SCREEN_WIDTH; ++x) { 19 | double screenCoordinate = (x << 1) / (double)SCREEN_WIDTH - 1; 20 | double rayPosX = posX; 21 | double rayPosY = posY; 22 | double rayDirX = dirX + planeX * screenCoordinate; 23 | double rayDirY = dirY + planeY * screenCoordinate; 24 | 25 | //get X, Y coordinate of map by truncating current position 26 | int mapX = (int) rayPosX; 27 | int mapY = (int) rayPosY; 28 | 29 | //Distance from current pos to next x or y side 30 | double sideDistX; 31 | double sideDistY; 32 | 33 | //Length from one side to the other 34 | double deltaX = sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX)); 35 | double deltaY = sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY)); 36 | double wallDist; 37 | 38 | //Direction to step (+1 or -1) 39 | int stepX; 40 | int stepY; 41 | 42 | //north south, or east west? 43 | int nsWall = false; 44 | 45 | if (rayDirX < 0) { 46 | stepX = -1; 47 | sideDistX = (rayPosX - mapX) * deltaX; 48 | } 49 | else { 50 | stepX = 1; 51 | sideDistX = (mapX + 1.0 - rayPosX) * deltaX; 52 | } 53 | 54 | if (rayDirY < 0) { 55 | stepY = -1; 56 | sideDistY = (rayPosY - mapY) * deltaY; 57 | } 58 | else { 59 | stepY = 1; 60 | sideDistY = (mapY + 1.0 - rayPosY) * deltaY; 61 | } 62 | 63 | //DDA 64 | while (!worldMap[mapX][mapY]) { 65 | //Jump to next square 66 | if (sideDistX < sideDistY) { 67 | sideDistX += deltaX; 68 | mapX += stepX; 69 | nsWall = false; 70 | } 71 | else { 72 | sideDistY += deltaY; 73 | mapY += stepY; 74 | nsWall = true; 75 | } 76 | 77 | if (mapX > MAP_WIDTH || mapY > MAP_HEIGHT) 78 | return; 79 | } 80 | 81 | //Calculate distance to camera 82 | if (nsWall) 83 | wallDist = fabs ((mapY - rayPosY + (1 - stepY) / 2) / rayDirY); 84 | else 85 | wallDist = fabs ((mapX - rayPosX + (1 - stepX) / 2) / rayDirX); 86 | 87 | //Calculates height of wall to be drawn. Multiply by some value to create non-cube walls 88 | int wallHeight = abs ((int)(SCREEN_HEIGHT / wallDist)); 89 | 90 | //Calculate lowest and heighest points to be drawn 91 | int start = -wallHeight / 2 + SCREEN_HEIGHT / 2; 92 | if (start < -posZ) 93 | start = -posZ; 94 | 95 | int end = wallHeight / 2 + SCREEN_HEIGHT / 2; 96 | if (end > SCREEN_HEIGHT - posZ) 97 | end = SCREEN_HEIGHT - posZ; 98 | 99 | #if !textured 100 | //Renders single colors 101 | Uint32 color; 102 | switch(worldMap[mapX][mapY]) { 103 | case 1: color = RGB_Red; break; 104 | case 2: color = RGB_Yellow; break; 105 | case 3: color = RGB_Blue; break; 106 | case 4: color = RGB_Green; break; 107 | default:color = RGB_White; break; 108 | } 109 | 110 | //Half the color of some sides, looks better 111 | if (nsWall) 112 | color = color / 2; 113 | 114 | //Draw vertical line 115 | int y1 = start + posZ; 116 | if (y1 < 0) 117 | y1 = 0; 118 | 119 | int y2 = end + posZ; 120 | if (y2 < 0) 121 | y2 = 0; 122 | 123 | drawLine (x, y1, y2, color); 124 | #else 125 | //Calculates how far along the wall this ray has hit (for use w/ texture) 126 | double wallX = 0.0; 127 | if (nsWall) 128 | wallX = rayPosX + ((mapY - rayPosY + ((1 - stepY) >> 1)) / rayDirY) * rayDirX; 129 | else 130 | wallX = rayPosY + ((mapX - rayPosX + ((1 - stepX) >> 1)) / rayDirX) * rayDirY; 131 | wallX -= floor(wallX); 132 | 133 | //Calculates x coordinate of texture 134 | int textureX = (int) (wallX * (double)TEXTURE_SIZE); 135 | if ((!nsWall && rayDirX > 0) || (nsWall && rayDirY < 0)) 136 | textureX = TEXTURE_SIZE - textureX - 1; 137 | 138 | ///Select texture from zero indexed array 139 | int textureIndex = worldMap[mapX][mapY] - 1; 140 | 141 | //Calculate brightness of this strip of pixels 142 | double brightness = wallDist / 2; 143 | 144 | //For all of the pixels in the Y direction 145 | for (int y = start; y < end; ++y) { 146 | //Uses integers to avoid floating point arithmetic 147 | int textureY = ((((y << 1) - SCREEN_HEIGHT + wallHeight) << TEXTURE_SIZE_BINARY) / wallHeight) >> 1; 148 | 149 | //Pick color from pixel array 150 | Uint32 color = pixelArray[textureIndex][textureX][textureY]; 151 | 152 | //Seperate the colors 153 | int red = (color >> 16) & 0x0FF; 154 | int green = (color >> 8)& 0x0FF; 155 | int blue = (color) & 0x0FF; 156 | 157 | //Modify the colors 158 | if (brightness > 1) { 159 | red /= brightness; 160 | green /= brightness; 161 | blue /= brightness; 162 | } 163 | 164 | //Recombine the colors 165 | color = ((red & 0x0ff) << 16) | ((green & 0x0ff)<<8) | (blue & 0x0ff); 166 | 167 | //Draw the individual pixel. May be faster if a buffer is used 168 | drawPoint(x, y + posZ, color); 169 | } 170 | #endif 171 | } 172 | } 173 | 174 | void updateTexture () { 175 | SDL_UpdateTexture(texture, NULL, pixelBuffer, SCREEN_WIDTH * 4); 176 | SDL_RenderClear(renderer); 177 | SDL_RenderCopy(renderer, texture, NULL, NULL); 178 | drawFPSToRenderer(); 179 | SDL_RenderPresent(renderer); 180 | } --------------------------------------------------------------------------------