• 창 관리는 매끄럽게 유지되어야합니다 : 다른 창으로 변경, 최소화 등.
• 벽에 따라 달라지는 다양한 벽 텍스처를 표시합니다 (선택은 귀하의 것입니다).
벽이 향하는 쪽 (북, 남, 동, 서).
• 프로그램은 벽 대신 항목 (스프라이트)을 표시 할 수 있어야합니다.
• 프로그램은 바닥 및 천장 색상을 서로 다른 두 가지 색상으로 설정할 수 있어야합니다.
• Deepthought가 언젠가 귀하의 프로젝트를 평가할 눈이 있다면 귀하의 프로그램은
두 번째 인수가 다음과 같을 때 첫 번째 렌더링 된 이미지를 bmp 형식으로 저장해야합니다.
"--저장".
• 두 번째 인수가 제공되지 않으면 프로그램은 창에 이미지를 표시하고
다음 규칙을 준수합니다.
◦ 키보드의 왼쪽 및 오른쪽 화살표 키를 사용하여 왼쪽과 오른쪽을 볼 수 있어야합니다.
미로에서 바로.
◦ W, A, S 및 D 키를 사용하여 시점을 이동할 수 있어야합니다.
미로.
◦ ESC 키를 누르면 창을 닫고 프로그램을 깨끗하게 종료해야합니다.
◦ 창의 프레임에있는 빨간색 십자가를 클릭하면 창을 닫고
프로그램을 깨끗하게 종료하십시오.
◦지도에 선언 된 화면 크기가 디스플레이 해상도보다 큰 경우
창 크기는 현재 디스플레이 해상도에 따라 설정됩니다.
◦ minilibX의 이미지 사용을 적극 권장합니다.
• 프로그램은 .cub가있는 장면 설명 파일을 첫 번째 인수로 취해야합니다.
보너스 파트
보너스는 필수 부분이 PERFECT 인 경우에만 평가됩니다.
PERFECT 란 당연히 완전해야 함을 의미합니다.
잘못된 사용과 같은 심한 실수의 경우에도 실패 할 수 없습니다.
기본적으로 필수 부분이 ALL을 얻지 못하면
점수를 매기는 동안 귀하의 보너스는 완전히 무시됩니다.
보너스 목록 :
• 벽 충돌.
• 스카이 박스.
• 바닥 및 / 또는 천장 텍스처.
• HUD.
• 위아래를 보는 능력.
• 점프하거나 웅 크리십시오.
• 거리 관련 그림자 효과.
• 라이프 바.
• 미로에 더 많은 항목.
• 개체 충돌.
• 물건 / 덫을 주워 포인트를 획득하거나 생명을 잃습니다.
• 열고 닫을 수있는 문.
• 비밀 문.
• 총알 애니메이션 또는 애니메이션 스프라이트.
• 여러 수준.
• 소리와 음악.
• 마우스로 시점 회전
DISPLAY 환경 변수 설정.
WSL1의 경우 : export DISPLAY=localhost:0.0
WSL2의 경우 : export DISPLAY="`grep nameserver /etc/resolv.conf | sed 's/nameserver // '`:0"
/etc/resolve.conf
의 nameserver 주소를 가져와 끝에 :0
을 추가한 것이다. (cat /etc/resolve.conf
)export DISPLAY=172.26.128.1:0
명령어로 환경 변수를 설정할 수 있다.vcxsrv
. (xming으로도 시도해봤는데 전혀 되지 않았다.)xming
이 좋다.)VcXsrv windows xserver
선택 후 개인, 공용 둘 다 허용해준다. (참고로 나는 vcxsrv 항목이 두 개 여서 둘다 허용했다.)Additional parameters for VcXsrv
항목에 -ac
를 추가한다.RDP 설치
1 apt install -y xrdp
2 apt install -y xfce4
3 apt install -y xfce4-goodies
4 sudo cp /etc/xrdp/xrdp.ini /etc/xrdp/xrdp.ini.bak
5 sudo sed -i 's/3389/3390/g' /etc/xrdp/xrdp.ini
6 sudo sed -i 's/max_bpp=32/#max_bpp=32\nmax=bpp=128/g' /etc/xrdp/xrdp.ini
7 sudo sed -i 's/xserverbpp=24/#xserverbpp=24\nxserverbpp=128/g' /etc/xrdp/xrdp.ini
8 sudo /etc/init.d/xrdp start
원격 데스크톱 연결
에 들어간다.localhost:3390
입력후 접속.sudo apt-get install gedit
으로 gedit을 설치하고 명령어 gedit
으로 실행. (이미 설치되었으면 스킵.)sudo /etc/init.d/xrdp restart
# include "../minilibx_opengl_20191021/mlx.h"
로 헤더 파일을 불러와 사용하면 된다.# include "../minilibx_opengl_20191021/mlx.h"
int main(void)
{
void *mlx;
mlx = mlx_init();
mlx_win = mlx_new_window(mlx, 800, 600, "Hello 42!");
mlx_loop(mlx);
return (0);
}
apt install libx11-dev
apt install libxext-dev
apt install libbsd-dev
apt install clang
gcc main.c -lmlx -lX11 -lXext -lm -lbsd
(win)
gcc main.c -lmlx -lm -framework OpenGL -framework AppKit
(Mac)
for (x = 0; x < 220; x++)
{
for (y = 0; y < 220; y++)
{
mlx_pixel_put(mlx_ptr, win_ptr, x, y, 0xFFFFFF);
}
}
void ft_putchar(char c)
{
write(1, &c, 1);
}
int deal_key(int key, void *param)
{
ft_putchar('X');
return (0);
}
int main(void)
{
void *mlx_ptr;
void *win_ptr;
mlx_ptr = mlx_init();
win_ptr = mlx_new_window(mlx_ptr, 300, 300, "Hello, World!");
mlx_key_hook(win_ptr, deal_key, (void *)0);
mlx_loop(mlx_ptr);
return (0);
}
*
param);mlx_hook(win_ptr, KeyPress, KeyPressMask, deal_key, (void *)0);
mlx_hook(win_ptr, 2, 1L<<0, deal_key, (void *)0);
(1L<<0)
로 입력해야 하지만, 내 경우엔 따로 매크로가 있는 헤더 파일을 사용했다.#include "X11/X.h"
(mlx 이벤트 변수가 정의된 헤더파일)*
win_ptr, int (*
funct_ptr)(), void *
param );int deal_key(int key, void *param)
{
ft_putchar('X');
return (0);
}
.
.
.
mlx_mouse_hook(win_ptr, deal_key, (void *)0);;
void *mlx_xpm_file_to_image(void *mlx_ptr, char *filename, int *width, int *height)
int mlx_put_image_to_window(void *mlx_ptr, void *win_ptr, void *img_ptr, int x, int y)
int key_press(int keycode, t_param *param)
{
static int a = 0;
if (keycode == KEY_W)//Action when W key pressed
param->x++;
else if (keycode == KEY_S) //Action when S key pressed
param->x--;
else if (keycode == KEY_ESC) //Quit the program when ESC key pressed
exit(0);
printf("x: %d\n", param->x);
return (0);
}
.
.
mlx_hook(win, KeyPress, KeyPressMask, key_press, ¶m);
이름 | x좌표 | y좌표 | 판별식(F) |
---|---|---|---|
판별식(F) < 0 | x + 1 | y | F + 2H |
판별식(F) >= 0 | x + 1 | y + 1 | F + 2(H-W) |
즉, F < 0 이면 F + 2H = F + 16
F >= 0 이면 F + 2(H-W) = F + 2(-2) = F - 4
단계 1 (21, 11), F = 2
단계 2 (22, 12), F = -2
단계 3 (23, 12), F = 14
단계 4 (24, 13), F = 10
단계 5 (25, 14), F = 6
단계 6 (26, 15), F = 2
단계 7 (27, 16), F = -2
단계 8 (28, 16), F = 14
단계 9 (29, 17), F = 10
단계 10 (30, 18), F = 6
레이캐스팅 기본 원리
단계에서 광선(Ray)의 필요성에 대해 설명했다.typedef struct s_info
{
void *mlx;
void *win;
float player_x;
float player_y;
int pixel_size;
int window_x;
int window_y;
void *img;
char *addr;
int bits_per_pixel;
int line_length;
int endian;
} t_info;
int mlx_loop_hook(void *mlx_ptr, int (*funct_ptr)(), void *param)
mlx_loop_hook(info.mlx, &main_loop, &info);
nt main_loop(t_info *info)
{
//draw player
for(int x = 0; x < info->pixel_size; x++)
{
for(int y = 0; y < info->pixel_size; y++)
{
mlx_pixel_put(info->mlx, info->win, info->player_x + x, info->player_y + y, 0x00FFFF00);
}
}
}
int main_loop(t_info *info)
{
//맵 전체를 검은색으로 초기화
for(int x = 0; x < info->window_x; x++)
{
for(int y = 0; y < info->window_y; y++)
{
my_mlx_pixel_put(info, x, y, 0x00000000);
}
}
mlx_put_image_to_window(info->mlx, info->win, info->img, 0, 0);
//플레이어를 픽셀로 찍어낸다.
for(int x = 0; x < info->pixel_size; x++)
{
for(int y = 0; y < info->pixel_size; y++)
{
mlx_pixel_put(info->mlx, info->win, info->px + x, info->py + y, 0x00FFFF00);
}
}
}
int mapX=8, mapY=8, mapSize=64;
int map[]=
{
1, 1, 1, 1, 1, 1, 1, 1,
1, 0, 1, 0, 0, 0, 0, 1,
1, 0, 1, 0, 0, 1, 0, 1,
1, 0, 1, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 1, 1,
1, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1,
};
void drawMap2D(t_info *info)
{
int x, y, xo, yo, color;
for(y = 0; y < mapY; y++) //0, 1, 2, 3, 4, 5, 6, 7, 8, 9
{
for (x = 0; x < mapX; x++) //0, 1, 2, 3, 4, 5, 6, 7, 8, 9
{
if (map[y*mapX + x] == 1)
color = 0x00FFFFFF;
else
color = 0x00000000;
xo = x * mapSize; //0, 64 ,128 ,192, 256, 320
yo = y * mapSize; //0, 64 ,128 ,192, 256, 320
for (int i = 0; i < mapSize; i++)
{
for (int j = 0; j < mapSize; j++)
{
my_mlx_pixel_put(info, xo + i, yo + j, color);
}
}
}
}
}
w
와 s
로 전진, 후진을 하고, a
와 d
로 움직일 방향(각도)를 조절할 수 있다.math.h
라이브러리가 필요하다.#include <math.h>
float pdx, pdy, pa;
int key_press(int key, t_info *info)
{
if (key == KEY_W)
{
info->player_x += pdx;
info->player_y += pdy;
}
if (key == KEY_S)
{
info->player_x -= pdx;
info->player_y -= pdy;
}
if (key == KEY_A)
{
pa -= 0.1;
if (pa < 0)
pa += 2 * PI;
pdx = cos(pa) * 5;
pdy = sin(pa) * 5;
}
if (key == KEY_D)
{
pa += 0.1;
if (pa > 2 * PI)
pa -= 2 * PI;
pdx = cos(pa) * 5;
pdy = sin(pa) * 5;
}
return (0);
}
a
와 d
를 통한 각도 변경이 가능해졌다.glBegin(GL_LINES)
함수를 이용했다. (https://www.youtube.com/watch?v=gYRrGTC7GtA&t=313s) (6:21)void bresenham(t_info *info, int startX, int startY, int finishX, int finishY)
{
int x = startX;
int y = startY;
int Xfactor = finishX < startX ? -1 : 1;
int Yfactor = finishY < startY ? -1 : 1;
int w = abs(finishX - startX); // -300
int h = abs(finishY - startY); // -200
int f;
if (w > h)
{
f = (2 * h) - w;
for (x = startX; x != finishX; x += Xfactor)
{
if (f < 0)
{
f += 2 * h;
}
else
{
y += Yfactor;
f += 2 * (h - w);
}
my_mlx_pixel_put(info, x, y, 0x00FFFF00);
}
}
else
{
f = (2 * w) - h;
for (y = startY; y != finishY; y += Yfactor)
{
if (f < 0)
{
f += 2 * w;
}
else
{
x += Xfactor;
f += 2 * (w - h);
}
my_mlx_pixel_put(info, x, y, 0x00FFFF00);
}
}
}
bresenham
함수는 시작 좌표 (x, y)와 도착 좌표(x, y)를 입력받으면 두 좌표 사이에 선을 그려주는 함수다.void drawRays3D(t_info *info)
{
int r, mx, my, mp, dof;
float rx,ry, ra, xo, yo;
int color;
color = 0x00FFFF00;
ra=pa;
// 수평선(horizontal)
for(r = 0; r < 1; r++)
{
dof = 0;
float aTan = -1/tan(ra);
if (ra > PI)
{
ry = (((int)info->player_y >> 6)<<6) - 0.0001;
rx = (info->player_y - ry) * aTan + info->player_x;
yo = -64;
xo = -yo * aTan;
}
if (ra < PI)
{
ry = (((int)info->player_y >> 6) << 6) + 64;
rx = (info->player_y - ry) * aTan + info->player_x;
yo = 64;
xo = -yo * aTan;
}
while (dof < 8)
{
mx = (int)(rx) >> 6;
my = (int) (ry)>>6;
mp = my * mapX + mx;
if (mp < mapX * mapY && map[mp] == 1)
dof = 8;
else
{
rx += xo;
ry += yo;
dof += 1;
}
}
bresenham(info, info->player_x, info->player_y, rx, ry, color);
}
}
segmentation fault
가 뜨는 경우가 있다.bresenham
이므로 이걸 수정하면 된다.if (w > h)
{
f = (2 * h) - w;
for (x = startX; x != finishX; x += Xfactor)
{
if (f < 0)
{
f += 2 * h;
}
else
{
y += Yfactor;
f += 2 * (h - w);
}
if (x >= 0 && x < info->window_x)
my_mlx_pixel_put(info, x, y, color);
}
}
int key_check[65536];
if (info->key_check[KEY_W] == 1 && keycode != KEY_ESC)
{
info->player_x += pdx;
info->player_y += pdy;
}
if (info->key_check[KEY_S] == 1 && keycode != KEY_ESC)
{
info->player_x -= pdx;
info->player_y -= pdy;
}
int ButtonDown(int keycode, t_info *info)
{
if (keycode >= 300)
return (-1);
info->key_check[keycode] = 1;
return (0);
}
int Button_Release(int keycode, t_info *info)
{
if (keycode >= 300)
return (-1);
info->key_check[keycode] = 0;
return (0);
}
ButtonRelease
로 하면 기존 헤더 함수와 이름이 겹쳐 에러 발생.mlx_loop_hook(info.mlx, &main_loop, &info);
mlx_hook(info.win, KeyPress, KeyPressMask, ButtonDown, &info);
mlx_hook(info.win, KeyRelease, KeyReleaseMask, Button_Release, &info);
KeyPressMask
말고도 KeyReleaseMask
를 사용하는 mlx_hook을 새로 만든다KeyPressMask
는 버튼을 누르면 인식되고, KeyReleaseMask
는 버튼을 때는 순간 인식된다.앞으로 나아갈 수 없다
는 조건을 주면 된다. int xo=0; if (pdx<0) { xo=-20; } else { xo=20;}
int yo=0; if (pdy<0) { yo=-20; } else { yo=20;}
//ipx와 ipy, add, sub 변수 생성.
int ipx=info->player_x/64.0, ipx_add_xo=(info->player_x+xo)/64.0, ipx_sub_xo=(info->player_x-xo)/64.0;
int ipy=info->player_y/64.0, ipy_add_yo=(info->player_y+yo)/64.0, ipy_sub_yo=(info->player_y-yo)/64.0;
if (info->key_check[KEY_W] == 1) //키를 감지하면.
{
//플레이어의 앞(현재 위치)에 있는 게 벽이 아니면.
if (map[ipy*mapX + ipx_add_xo] != 1) {info->player_x += pdx;}
if (map[ipy_add_yo*mapX + ipx] != 1) {info->player_y += pdy;}
}
if (info->key_check[KEY_S] == 1)
{
if (map[ipy*mapX + ipx_sub_xo] != 1) {info->player_x -= pdx;}
if (map[ipy_sub_yo*mapX + ipx] != 1) {info->player_y -= pdy;}
}
플레이어가 바라보는 시야
의 앞에 무엇이 있는지 알수 있다.// 벽을 칠한다.
bresenham(info, (r * 8+530), lineO, (r * 8+530), lineH + lineO, color, 8);
천장
은 시작 Y지점이 0(화면의 시작)
부분이고, 끝나는 y지점은 벽의 제일 윗 부분이다.바닥
은 시작 Y지점이 벽의 제일 아래
이고, 끝나는 y지점은 화면의 끝(window_y)
부분이다.//천장
bresenham(info, (r * 8+530), 0, (r * 8+530), lineO, 0x0040E0D0, 8);
// 바닥
bresenham(info, (r * 8+530), lineH + lineO, (r * 8+530), 512, 0x00FF0000, 8);
int mapX=10, mapY=10, mapSize=64;
int map[]=
{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 0, 3, 0, 0, 0, 0, 0, 0, 1, <= (2, 1)위치에 3이 있다.
1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 1, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};
0
은 공간, 1
은 벽, 2
는 아이템(아직 미구현)이다.전체 코드
void player_init(t_info *info)
{
int x;
int y;
for (int i = 0; i < (mapX * mapY); i++)
{
if (map[i] == 3 || map[i] == 4 || map[i] == 5 || map[i] == 6)
{
//맵 기준 x, y 구하기.
x = i % mapX;
y = i / mapX;
info->player_x = x * 64 + (mapSize / 2);
info->player_y = y * 64 + (mapSize / 2);
if (map[i] == 3) //동
pa = 0 + 0.25;
else if (map[i] == 4) //서
pa = 3 + 0.25;
else if (map[i] == 5) //남
pa = 1.5 + 0.25;
else if (map[i] == 6) //북
pa = 4.5 + 0.25;
break;
}
}
}
x = [현재 배열 위치] % 배열의 x크기.
y = [현재 패열 위치] / 배열의 x크기.
info->player_x = x * 64 + (mapSize / 2);
info->player_y = y * 64 + (mapSize / 2);
if (map[i] == 3) //동
pa = 0 + 0.25;
else if (map[i] == 4) //서
pa = 3 + 0.25;
else if (map[i] == 5) //남
pa = 1.5 + 0.25;
else if (map[i] == 6) //북
pa = 4.5 + 0.25;
int All_Textures[]=
{
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,
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,
};
int rayHitVertical = 0;
//vertical wall hit
if (disV < disH) { rx = vx; ry = vy; disT = disV; color = 0x00FFAA00; rayHitVertical = 1; }
//horizontal wall hit
if (disH < disV) { rx = hx; ry = hy; disT = disH; color = 0x00FFCC00;}
bresenham(info, info->player_x, info->player_y, rx, ry, color, 1);
//----3D 그리기-----
float ca = pa - ra; if (ca < 0) { ca+= 2* PI; } if(ca > 2*PI) { ca-= 2 * PI; } disT = disT * cos(ca);
float lineH = (mapSize * 320)/disT;
if (lineH > 320) { lineH = 320; }
float lineO = 160 - lineH/2;
float ty_step = 32.0 / (float)lineH;
float ty_off=0;
if (lineH > 320) { ty_off=(lineH-320)/2.0; lineH = 320;}
float ty = ty_off*ty_step;
float tx;
if (disV > disH) { tx=(int)(rx / 4.0) % 16;}
else { tx=(int)(ry / 4.0) % 16;}
//천장
bresenham(info, (r * 8+530), 0, (r * 8+530), lineO, 0x0040E0D0, 8);
// 벽 (텍스쳐 적용)
for (int y = 0; y < lineH; y++)
{
int c;
if ((int)ty + (int)tx * 32 < 512)
c = All_Textures[(int)ty + (int)tx * 32];
ty += ty_step;
if (rayHitVertical == 1) //동서
{
if (c == 0) //텍스쳐 배열의 0
{
if (info->player_x > rx) //동쪽
color = 0x000000000; //검정
else //서쪽
color = 0x000FFFF00; //노랑
}
else if (c == 1)//텍스쳐 배열의 1
{
if (info->player_x > rx) //동쪽
color = 0x000FFFFFF; //하양
else
color = 0x000FFFFFF; //하양
}
}
else //남북
{
if (c == 0)
{
if (info->player_y > ry) //남쪽
color = 0x00066ff33;//연두
else //북쪽
color = 0x00000FFFF;//하늘색
}
else if (c == 1)
{
color = 0x000ff99ff; //밝은 보라
}
}
// 벽 그리기
for(int aa = 0; aa < 8; aa++)
my_mlx_pixel_put(info, r * 8 +530 + aa, y+lineO, color);
}
// 바닥
bresenham(info, (r * 8+530), lineH + lineO, (r * 8+530), 512, 0x00FF0000, 8);
ra += DR; if (ra < 0) { ra+= 2* PI; } if(ra > 2*PI) { ra-= 2 * PI; }
}
}
if (disV < disH) //동서
{
if (info->player_x < rx)
texNum = 0; //동
else
texNum = 1; //서
}
else
{
if (info->player_y < ry)
texNum = 2; //남
else
texNum = 3; //북
}
void load_image(t_info *info, int *texture, char *path)
{
info->img = mlx_xpm_file_to_image(info->mlx, path, &info->img_width, &info->img_height);
info->addr = mlx_get_data_addr(info->img, &info->bits_per_pixel, &info->size_l, &info->endian);
for (int y = 0; y < info->img_height; y++)
{
for (int x = 0; x < info->img_width; x++)
{
texture[info->img_width * y + x] = info->addr[info->img_width * y + x];
}
}
mlx_destroy_image(info->mlx, info->img);
}
void load_texture(t_info *info)
{
load_image(info, info->texture[0], "textures/wall_e.xpm");
load_image(info, info->texture[1], "textures/wall_n.xpm");
load_image(info, info->texture[2], "textures/wall_s.xpm");
load_image(info, info->texture[3], "textures/wall_w.xpm");
}
int **texture;
형식의 변수를 구조체에 추가해야 한다.int main(void)
{
t_info info;
info.mlx = mlx_init();
info.player_x = 300;
info.player_y = 300;
info.pixel_size = 8;
info.window_x = 1024;
info.window_y = 512;
player_init();
//텍스쳐 초기화.
if (!(info.texture = (int **)malloc(sizeof(int *) * 8)))
return (-1);
for (int i = 0; i < 8; i++)
if (!(info.texture[i] = (int *)malloc(sizeof(int) * (texHeight * texWidth))))
return (-1);
for (int i = 0; i < 8; i++)
for (int j = 0; j < texHeight * texWidth; j++)
info.texture[i][j] = 0;
info.key_check[KEY_ESC] = 0;
//텍스쳐 초기화시 keycode가 멋대로 지정되는 오류가 있어서 넣음.
load_texture(&info);
info.win = mlx_new_window(info.mlx, info.window_x, info.window_y, "Hello world!");
info.img = mlx_new_image(info.mlx, info.window_x, info.window_y);
info.addr = mlx_get_data_addr(info.img, &info.bits_per_pixel, &info.size_l,
&info.endian);
mlx_loop_hook(info.mlx, &main_loop, &info);
mlx_hook(info.win, KeyPress, KeyPressMask, Button_down, &info);
mlx_hook(info.win, KeyRelease, KeyReleaseMask, Button_release, &info);
mlx_loop(info.mlx);
}
texture
변수를 사용하기 전에 알맞은 크기의 xpm파일인지 검사하고, 메모리 할당, 초기화 한다.load_image
라는 텍스쳐의 색 정보를 추출하는 함수를 제작.load_texture
라는 함수를 이용해 우리가 가져온 텍스트 파일의 색 정보를 Texture
변수에 저장.yechoi
님의 설명에 나와있다.float ca = pa - ra;
if (ca < 0) { ca+= 2* PI; }
if (ca > 2*PI) { ca-= 2 * PI; }
disT = disT * cos(ca);
float lineH = (mapSize * 320)/disT;
if (lineH > 320) { lineH = 320; }
float lineO = 160 - lineH/2;
float ty_step = 64.0 / (float)lineH; //원래 32, 지금은 64X64라 64.0으로 변경
float ty_off=0;
if (lineH > 320) { ty_off=(lineH-320)/2.0; lineH = 320;}
float ty = ty_off*ty_step;
float tx;
//32X16사이즈의 경우: (4, 16)
//32X32사이즈의 경우: (2, 32)
//64X64사이즈의 경우: (1, 64)
if (disV > disH) { tx=(int)(rx / 1.0) % 64;}
else { tx=(int)(ry / 1.0) % 64;}
//천장
bresenham(info, (r * 8+530), 0, (r * 8+530), lineO, 0x0040E0D0, 8);
int texNum;
//벽 (텍스쳐 적용)
for (int y = 0; y < lineH; y++)
{
if (rayHitVertical == 1) //동서
{
if (info->player_x < rx)
texNum = 0; //동
else
texNum = 1; //서
}
else
{
if (info->player_y < ry)
texNum = 2; //남
else
texNum = 3; //북
}
color = info->texture[texNum][(int)ty * 64 + (int)tx];
ty += ty_step;
for(int aa = 0; aa < 8; aa++)
my_mlx_pixel_put(info, r * 8 +530 + aa, y+lineO, color);
}
(ty * Texture_size) + tx)
if (rayHitVertical == 1) //동서
{ // 여기 중괄호(Braces)로 묶어야 한다. 안 묶으면 else를 인식 못함.
if (info->player_x > rx) //서 (플레이어가 벽보다 x좌표가 클때.)
tx= 63-tx;
}
else
if (info->player_y < ry) //남 (플레이어가 벽보다 y좌표가 작을 때.)
tx= 63-tx;
(ty * Texture_size) + tx)
void bresenham(t_info *info, int startX, int startY, int finishX, int finishY, int color, int width)
{
int x = startX;
int y = startY;
int Xfactor = finishX < startX ? -1 : 1;
int Yfactor = finishY < startY ? -1 : 1;
int w = abs(finishX - startX); // -300
int h = abs(finishY - startY); // -200
int f;
for (int i = 0; i < width; i++)
{
if (w > h)
{
f = (2 * h) - w;
for (x = startX; x != finishX; x += Xfactor)
{
if (f < 0)
{
f += 2 * h;
}
else
{
y += Yfactor;
f += 2 * (h - w);
}
if (x >= 0 && x < info->window_x)
my_mlx_pixel_put(info, x + i, y, color);
}
}
else
{
f = (2 * w) - h;
for (y = startY; y != finishY; y += Yfactor)
{
if (f < 0)
{
f += 2 * w;
}
else
{
x += Xfactor;
f += 2 * (w - h);
}
if (y >= 0 && y < info->window_y)
my_mlx_pixel_put(info, x + i, y, color);
}
}
}
}
t_pos bresen(t_info *info, float startX, float startY, float pdx, float pdy)
{
t_pos pos;
float x = startX;
float y = startY;
float finishX = startX + pdx * 100;
float finishY = startY + pdy * 100;
float Xfactor = finishX < startX ? -1 : 1;
float Yfactor = finishY < startY ? -1 : 1;
float w = abs(finishX - startX); // -300
float h = abs(finishY - startY); // -200
float f;
if (w > h)
{
f = (2 * h) - w;
for (x = startX; x != finishX; x += Xfactor)
{
if (f < 0)
{
f += 2 * h;
}
else
{
y += Yfactor;
f += 2 * (h - w);
}
if (map[((int)y / mapSize)*mapX + ((int)x / mapSize)] == 1)
{
pos.x = x;
pos.y = y;
return (pos);
}
}
}
else
{
f = (2 * w) - h;
for (y = startY; y != finishY; y += Yfactor)
{
if (f < 0)
{
f += 2 * w;
}
else
{
x += Xfactor;
f += 2 * (w - h);
}
if (map[((int)y / mapSize)*mapX + ((int)x / mapSize)] == 1)
{
pos.x = x;
pos.y = y;
return (pos);
}
}
}
}
기울기
이다. pdx = cos(pa) * 5; // cos각도 * 변의 길이 = x증가량.
pdy = sin(pa) * 5; // sin각도 * 변의 길이 = y증가량.
void drawRays2D(t_info *info)
{
float ra = pa;
int color, color2;
color = 0x00FF0000; //빨간색
color2 = 0x00FFFF00; //노랑색
t_pos pos;
//시야가 60이니까, 반시계 방향으로 30각도 만큼만 빼서 START!, 그래야 광선의 중심이 시야의 중심이 되니까.
ra -= 30 * DR;
for (int i = 0; i < 60; i++)
{
//px, py : 지도상에서 플레이어의 위치.
//pdx pdy : x, y 증가값. (증가 위치는 임시로 5라고 했음. (딱히 상관 없다.))
// ra(Radians)값이 6.28을 넘어가면 초기화.
if (ra < 0) { ra+= 2* PI; }
if(ra > 2*PI) { ra-= 2 * PI; }
//바뀐 ra에 따라서 pdx, pdy 값을 변경.
float pdx_a = cos(ra) * 5;
float pdy_a = sin(ra) * 5;
//bresen 함수를 이용해, 광선이 벽에 닿은 위치를 계산, 밎 pos 변수에 저장.
pos = bresen(info, info->player_x, info->player_y, pdx_a, pdy_a);
//pos의 x, y 값을 이용해 선을 그린다.
bresenham(info, info->player_x, info->player_y, pos.x, pos.y, color2, 1);
//현재 Radians(파이각)이 6.28이므로 파이/360의 값인 DR을 1도로 가정하고 계산. DR = 0.017
ra += DR;
}
}
void drawRays2D(t_info *info)
{
float ra = pa;
int color, color2, color3;
color = 0x00FF0000;
color2 = 0x00FFFF00;
color3 = 0x00FFFFFF;
t_pos pos;
float tempX;
float tempY;
float disV;
float disH;
//시야가 60이니까, 반시계 방향으로 30각도 만큼만 빼서 START!
ra -= 30 * DR;
for (int i = 0; i < 60; i++)
{
//px, py : 지도상에서 플레이어의 위치.
//pdx pdy : 플레이어 앵글의 각 x, y 증가값. (증가 위치는 임시로 +5라고 했음. (딱히 상관 없다.))
//이거 초기화 해야함.
float pdx_a = cos(ra) * 5;
float pdy_a = sin(ra) * 5;
pos = bresen(info, info->player_x, info->player_y, pdx_a, pdy_a);
if (pos.y < info->player_y) //벽의 y위치가 플레이어보다 높이 있을 시.
pos.y += 1;
if (pos.x < info->player_x)
pos.x += 1;
if ((int)pos.x % mapSize == 0) //수직선 hit!
{
//bresenham(info, info->player_x, info->player_y, pos.x, pos.y, color, 1);
disV = dist(info->player_x, info->player_y, pos.x, pos.y);
disH = 0;
}
else if ((int)pos.y % mapSize == 0) //수평선 hit!
{
//bresenham(info, info->player_x, info->player_y, pos.x, pos.y, color2, 1);
disH = dist(info->player_x, info->player_y, pos.x, pos.y);
disV = 0;
}
//가끔 두 조건을 충족하는 경우 때문에 에러 발생.
//그래서 이전에 사용한 좌표(tempX, tempY)와 비교해서 에러를 없앰.
//다만 이 경우 i = 0, 즉 가장 첫번째는 비교할 temp가 존재하지 않아 에러 발생.
//그래서 시야각을 61로 주고, 1부분의 시야각을 삭제하면 될 듯.
if ((int)pos.x % mapSize == 0 && (int)pos.y % mapSize == 0)
{
if ((int)tempX % mapSize == 0)
{
//bresenham(info, info->player_x, info->player_y, pos.x, pos.y, color, 1);
disV = dist(info->player_x, info->player_y, pos.x, pos.y);
disH = 0;
}
else if((int)tempY % mapSize == 0)
{
//bresenham(info, info->player_x, info->player_y, pos.x, pos.y, color2, 1);
disH = dist(info->player_x, info->player_y, pos.x, pos.y);
disV = 0;
}
}
tempX = pos.x;
tempY = pos.y;
//3D 구현.
//위에서 구한 광선이 벽에 닿은 위치(pos.x, pos.y)를 이용해 3D 벽을 구현.
//광선이 벽의 수직선(Vertical) 수평선(Horizontal)에 맞았는지 구분해야 한다.
//disV, disH 중에 더 큰 것이 진짜 거리이므로 disT에 값을 입력.
int rayHitVertical = 0;
float disT;
if (disV > disH)
{
disT = disV;
rayHitVertical = 1;
}
else if (disH > disV)
{
disT = disH;
}
// 벽의 뭉툭함을 없애줌.
float ca = pa - ra;
if (ca < 0) { ca+= 2* PI; }
if (ca > 2*PI) { ca-= 2 * PI; }
disT = disT * cos(ca);
//벽의 높이, 오프셋 계산.
float lineH = (mapSize * info->window_y)/disT; //원래 640, 지금은 window_y
//동서남북 구분하고 그에 따른 텍스트 넘버 부여.
int texNum = 0;
if (rayHitVertical == 1)
{
if (info->player_x < pos.x)
texNum = 0; //동
else
texNum = 1; //서
}
else
{
if (info->player_y < pos.y)
texNum = 2; //남
else
texNum = 3; //북
}
float tx, ty;
float ty_off = 0;
float ty_step = 64.0 / (float)lineH;
//구현해야 할 벽의 크기가 윈도우 화면보다 클 시.
//즉, 플레이어가 벽에 딱 붙었을 시.
//너무 가까이 붙으면 벽의 윗부분과 아래는 시야에 보이지 않는다.
//그래서 벽의 윗부분과 아랫부분을 버리는 작업.
if (lineH >= info->window_y)
{
//lineH - info->window_y로 불필요한 벽의 윗부분을 버린다.
//이후 2로 나눠서 불필요한 벽의 아래부분 마저 버린다.
//결과적으로 필요한 벽의 중앙 부분만 남음.
ty_off=(lineH-info->window_y) / 2;
//구현해야 할 벽의 높이가 윈도우 화면보다 크면 안되므로 고정해준다.
lineH = info->window_y;
}
float lineO = (info->window_y / 2) - lineH / 2;
//ty는 텍스쳐 배열에서 y배열을 얼만큼 아래로 옳길지 결정.
ty = ty_off*ty_step;
//tx는 텍스쳐 배열에서 x배열을 얼만큼 아래로 옳길지 결정.
if (rayHitVertical == 0) //광선이 수평선(Horizontal) 벽에 맞았으니, x좌표가 필요.
tx = (int)pos.x % 64;
else //광선이 수평선(Vertical) 벽에 맞았으니, y좌표가 필요.
tx = (int)pos.y % 64;
//벽을 구현.
//i(시야 60) 만큼
int xx = info->window_x / 60 - 1;
for (int aa = 0; aa < lineH; aa++)
{
color = info->texture[texNum][(int)ty * texWidth + (int)tx];
ty += ty_step;
for (int bb = 0; bb < 16; bb++)
{
my_mlx_pixel_put(info, i * xx + bb , lineO + aa, color);
}
}
//풀해상도 천장 바닥.
bresenham(info, (i * 16), 0, (i * 16), lineO, 0x0040E0D0, 16);
bresenham(info, (i * 16), lineH + lineO, (i * 16), info->window_y, 0x00FF0000, 16);
ra += DR; //현재 Radians(파이각)이 6.28이므로 파이/360의 값인 DR을 1도로 가정하고 계산. DR = 0.017
if (ra < 0) { ra+= 2* PI; } if(ra > 2*PI) { ra-= 2 * PI; }
}
}