42_cub3d/algo.c
mcolonna 8c0d63300d s
2024-10-14 18:35:50 +02:00

329 lines
9.8 KiB
C

/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* algo.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: mc <mc@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/10/01 16:24:58 by grobledo #+# #+# */
/* Updated: 2024/10/14 18:35:03 by mc ### ########.fr */
/* */
/* ************************************************************************** */
#include "algo.h"
#include <math.h>
#include <stdio.h>
#include <X11/X.h>
#include <stdbool.h>
static void *g_mlx = NULL;
static void *g_win = NULL;
static t_ray g_ray;
// int worldMap[mapWidth][mapHeight]=
// {
// {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,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,1},
// {1,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,1,1,1,1,1,0,0,0,0,1,0,1,0,1,0,0,0,1},
// {1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1},
// {1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1},
// {1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1},
// {1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,1,0,1,0,1,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,1},
// {1,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,1},
// {1,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,1},
// {1,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,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,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
// {1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
// {1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
// {1,1,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,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,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
// };
int worldMap[mapWidth][mapHeight]=
{
{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,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,1,1,0,0,1,1,1,1,1,0,0,0,0,1,0,1,0,1,0,0,0,1},
{1,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,1,1,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1},
{1,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,1,1,0,0,1,1,0,1,1,0,0,0,0,1,0,1,0,1,0,0,0,1},
{1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,1,1,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,1},
{1,0,0,0,0,0,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,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,1},
{1,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,1,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,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,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};
static void vector_from_rotation(
double *vecX, double *vecY, double angle, double norm)
{
*vecX = -cos(angle) * norm;
*vecY = sin(angle) * norm;
}
static int initalgo()
{
// TODO from map
g_ray.posX = 14;
g_ray.posY = 6.5;
g_ray.rot = 0;
return (0);
}
static int render(u_int32_t *img_data)
{
int x; // px
// clear image data
ft_bzero(img_data, SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(int)); // TODO why
// 1
double dirX, dirY, planeX, planeY;
vector_from_rotation(&dirX, &dirY, g_ray.rot, 1);
vector_from_rotation(&planeX, &planeY, g_ray.rot + PI/2, FOV);
x = 0;
while (x < SCREEN_WIDTH)
{
// case player on map (cases)
int mapX = (int)g_ray.posX;
// case player on map (cases)
int mapY = (int)g_ray.posY;
// vector ray direction (1)
double raydirX, raydirY;
{
// ray direction in [-1;+1]. the highter the more to the right (1)
double ray_direction = 2 * x / (double)SCREEN_WIDTH - 1;
raydirX = dirX + planeX * ray_direction;
raydirY = dirY + planeY * ray_direction;
}
// longueur du rayon dans une même colonne de cases (cases)
double deltadistX = (raydirX == 0) ? 1e30 : fabs(1 / raydirX);
// longueur du rayon dans une même ligne de cases (cases)
double deltadistY = (raydirY == 0) ? 1e30 : fabs(1 / raydirY);
// longueur du rayon entre pos actuelle et pos du prochain coté ouest ou est d'une box
double sidedistX;
// longueur du rayon entre pos actuelle et pos du prochain coté nord ou sub d'une box
double sidedistY;
// direction du rayon sur x (+1 or -1)
int stepX;
// direction du rayon sur y (+1 or -1)
int stepY;
if (raydirX < 0)
{
stepX = -1;
sidedistX = (g_ray.posX - mapX) * deltadistX;
}
else
{
stepX = 1;
sidedistX = (mapX + 1.0 - g_ray.posX) * deltadistX;
}
if (raydirY < 0)
{
stepY = -1;
sidedistY = (g_ray.posY - mapY) * deltadistY;
}
else
{
stepY = 1;
sidedistY = (mapY + 1.0 - g_ray.posY) * deltadistY;
}
bool hit = false;
bool side;
while(!hit)
{
// jump to next map square, either in x-direction, or in y-direction
if(sidedistX < sidedistY)
{
sidedistX += deltadistX;
mapX += stepX;
side = false;
}
else
{
sidedistY += deltadistY;
mapY += stepY;
side = true;
}
// check if ray has hit a wall
if(worldMap[mapX][mapY] == 1)
hit = true;
}
// calcul lenght of ray (shortest perpendicular distance between wall and camera plane) (1)
double perpwalldist;
if(!side)
perpwalldist = (sidedistX - deltadistX);
else
perpwalldist = (sidedistY - deltadistY);
// calculate height of line to draw on screen (px)
int lineHeight = (int)(SCREEN_HEIGHT / perpwalldist);
// calculate lowest and highest pixel to fill in current stripe
int drawStart = -lineHeight / 2 + SCREEN_HEIGHT / 2;
if(drawStart < 0)
drawStart = 0;
int drawEnd = lineHeight / 2 + SCREEN_HEIGHT / 2;
if(drawEnd >= SCREEN_HEIGHT)
drawEnd = SCREEN_HEIGHT - 1;
// draw vertical line
int y = 0;
while (y < drawStart)
{
img_data[y * SCREEN_WIDTH + x] = COLOR_CEILING;
y++;
}
while (y < drawEnd)
{
img_data[y * SCREEN_WIDTH + x] = COLOR_WALL;
y++;
}
while (y < SCREEN_HEIGHT)
{
img_data[y * SCREEN_WIDTH + x] = COLOR_FLOOR;
y++;
}
x++;
}
return (0);
}
static void draw_screen() {
int bpp;
int size_line;
int endian;
void *img_ptr = mlx_new_image(g_mlx, SCREEN_WIDTH, SCREEN_HEIGHT);
u_int32_t *img_data = (u_int32_t *)mlx_get_data_addr(img_ptr, &bpp, &size_line, &endian);
if (bpp != 32 || endian != 0)
{
// TODO manage error better
printf("image format error (got bpp == %i, endian == %i)\n", bpp, endian);
exit(1);
}
render(img_data);
mlx_put_image_to_window(g_mlx, g_win, img_ptr, 0, 0);
}
static int keypress(int keycode)
{
double dirX, dirY, planeX, planeY;
vector_from_rotation(&dirX, &dirY, g_ray.rot, 1);
vector_from_rotation(&planeX, &planeY, g_ray.rot + PI/2, FOV);
//move forward if no wall in front of you
if (keycode == XK_Up || keycode == XK_z || keycode == XK_w)
{
if (worldMap[(int)((g_ray.posX + dirX * MOVE_SPEED))][(int)(g_ray.posY)] != 1)
g_ray.posX += dirX * MOVE_SPEED;
if (worldMap[(int)(g_ray.posX)][(int)(g_ray.posY + dirY * MOVE_SPEED)] != 1)
g_ray.posY += dirY * MOVE_SPEED;
}
//move backwards if no wall behind you
if (keycode == XK_Down || keycode == XK_s)
{
if (worldMap[(int)(g_ray.posX - dirX * MOVE_SPEED)][(int)(g_ray.posY)] != 1)
g_ray.posX -= dirX * MOVE_SPEED;
if (worldMap[(int)(g_ray.posX)][(int)(g_ray.posY - dirY * MOVE_SPEED)] != 1)
g_ray.posY -= dirY * MOVE_SPEED;
}
//rotate to the right
if (keycode == XK_Right || keycode == XK_d)
{
g_ray.rot += ROT_SPEED;
// double oldDirX;
// double oldPlaneX;
// //both camera direction and camera plane must be rotated
// oldDirX = dirX;
// dirX = dirX * cos(-ROT_SPEED) - dirY * sin(-ROT_SPEED);
// dirY = oldDirX * sin(-ROT_SPEED) + dirY * cos(-ROT_SPEED);
// oldPlaneX = planeX;
// planeX = planeX * cos(-ROT_SPEED) - planeY * sin(-ROT_SPEED);
// planeY = oldPlaneX * sin(-ROT_SPEED) + planeY * cos(-ROT_SPEED);
}
//rotate to the left
if (keycode == XK_Left || keycode == XK_q || keycode == XK_a)
{
g_ray.rot -= ROT_SPEED;
// double oldDirX;
// double oldPlaneX;
// //both camera direction and camera plane must be rotated
// oldDirX = dirX;
// dirX = dirX * cos(ROT_SPEED) - dirY * sin(ROT_SPEED);
// dirY = oldDirX * sin(ROT_SPEED) + dirY * cos(ROT_SPEED);
// oldPlaneX = planeX;
// planeX = planeX * cos(ROT_SPEED) - planeY * sin(ROT_SPEED);
// planeY = oldPlaneX * sin(ROT_SPEED) + planeY * cos(ROT_SPEED);
}
if (keycode == XK_Escape)
exit(0);
printf("x:%f y:%f\n", g_ray.posX, g_ray.posY);
// render the updated frame after key press
draw_screen();
return (0);
}
int main(void)
{
// Initialisation
g_mlx = mlx_init();
g_win = mlx_new_window(g_mlx, SCREEN_WIDTH, SCREEN_HEIGHT, "cub3d");
initalgo();
// hook keypress
mlx_hook(g_win, 2, 1L<<0, keypress, NULL);
// render the initial frame
draw_screen();
// start the mlx loop
mlx_loop(g_mlx);
return (0);
}
// https://github.com/iciamyplant/Cub3d-Linux
// https://lodev.org/cgtutor/raycasting.html
// https://www.youtube.com/watch?v=js7HW65MmNw&list=PL0H9-oZl_QOHM34HvD3DiGmwmj5X7GvTW