TIL
20210319(금) : cub 진행 상황 본문
해야 할 것
- BMP파일 생성.
- parse_main.c 의 parse_type의
N
에 맞춰 25줄로 줄이기
오늘 할 일
- BMP파일 생성.
- 스프라이트의 이해
- 스프라이트 구현
- N 맞추기,
gnl, libft
N 맞추기 - Error 처리 :
printf("Error\n")
띄우고 죽게하기.
내일 할 일
- N 맞추기,
gnl, libft
N 맞추기 - Error 처리 :
printf("Error\n")
띄우고 죽게하기. - 줄 줄이기 (parse_type)
요구사항 분석
- 스프라이트의 좌표 리스트들.
- 스프라이트들의 각기 다른 텍스쳐 그림을 위해서 스프라이트 구조체 존재하는 정보 :
텍스쳐 디렉토리, 스프라이트 위치 : 실 사용때는 스프라이트 구조체의 배열로 만들어 사용할것.
- 스프라이트들의 각기 다른 텍스쳐 그림을 위해서 스프라이트 구조체 존재하는 정보 :
- 스프라이트의 텍스쳐를 받을 수 있도록 Parsing부에 parsing 추가가 필요해보인다.
- 아마 load_texture()함수도 조작해주어야 할것같다.
진행 로그
비트와 비트맵



--save option 들어왔을 시, 비트맵 파일로 저장.
bitmap과 관련된 헤더 생성
struct s_bmpihdr
struct s_bmpfhdr
bitmap과 관련된 함수 생성, 그리고 오류
f->file_type = 0x4d42;
위와 같이 저장하고, stdout으로 뽑으면, 0x4d42
의 integer값인 19778
이 문자로 에디터에 입력된다.
즉, 1은 0x31
, 9는 0x39
, 7은 0x37
로.. 들어가게 된다.
따라서 이렇게 초기화 시 값을 숫자로 넣는 방법은 개선이 필요할 듯 싶다.
static void write_data_2_bmp(t_cub *cub, t_bmpfhdr *f, t_bmpihdr *i, int fd)
{
ft_putnbr_fd(f->file_type, fd);
ft_putstr_fd(ft_itoa(f->file_type), fd);
ft_putnbr_fd(f->file_size, fd);
ft_putnbr_fd(f->reserved_1, fd);
ft_putnbr_fd(f->reserved_2, fd);
ft_putnbr_fd(f->pixel_data_offset, fd);
ft_putnbr_fd(i->header_size, fd);
ft_putnbr_fd(i->image_width, fd);
ft_putnbr_fd(i->image_height, fd);
ft_putnbr_fd(i->planes, fd);
ft_putnbr_fd(i->bpp, fd);
ft_putnbr_fd(i->compression, fd);
ft_putnbr_fd(i->image_size, fd);
ft_putnbr_fd(i->xpixels_per_meter, fd);
ft_putnbr_fd(i->ypixels_per_meter, fd);
ft_putnbr_fd(i->total_colors, fd);
ft_putnbr_fd(i->important_colors, fd);
}
static void init_bmp_header(t_cub *cub, t_bmpfhdr *f, t_bmpihdr *i)
{
ft_memset(f, 0, sizeof(t_bmpfhdr));
ft_memset(i, 0, sizeof(t_bmpihdr));
f->file_type = 0x4d42;
f->pixel_data_offset = 0x36;
i->header_size = 0x28;
i->image_width = cub->screen_x;
i->image_height = cub->screen_y;
i->planes = 0x01;
i->bpp = 0x20;
}
비트맵 구조체 저장, 파일 출력 문제 해결
write_data_2_bmp
에는 큰 실수가 있었다.
데이터를 가공해서 char형으로 만들면 안되고 데이터 그 자체를 bmp파일로 보내야 했다.
그렇게 하기 위해서,
static void write_data_2_bmp(t_cub *cub, t_bmpfhdr *f, t_bmpihdr *i, int fd)
{
int write_val;
write_val = write(fd, f, 14);
write_val += write(fd, i, 40);
}
아예 바이너리 형태로 뽑히도록 구조체 자체를 뽑아줬다.
이렇게 만들었을 경우, static void write_data_2_bmp(t_cub *cub, t_bmpfhdr *f, t_bmpihdr *i, int fd)
위와 같이 만들어서 구조체를 바이너리로 출력할 경우, 원하는 대로 bmp파일에 바이너리가 저장이 되질 않는다.
이는 컴파일러가 데이터 최적화를 위해서 구조체 정렬(padding)을 하기 때문에, 문제가 생기는데, 이를 해결해주기 위해서
#pragma pack(push, 1)
, #pragma pack(pop)
를 사용해야 했다.
위의 pragma pack과 pop은, 컴파일 시 데이터를 n바이트 단위로 정렬한다.
출력
메타데이터를 알맞게 설정해 준 뒤에, image 배열 안에 존재하는 데이터까지 bmp파일로 옮겼을 경우,
bmp파일로 제대로 옮겨지는 것을 확인 할 수 있었다.


BMP 함수 Leak_check
bmp_maker를 실행시키면 아래와 같은 leak 메시지가 떴다.
원인 :
- 프로그램 종료 바로 이전 시점에서 parser에서 사용한 구조체
conf
의 해제를 해주지 않아보임. - 프로그램 종료 시점에서 mlx 포인터 해제를 안시켜준 것으로 보임.
따라서, 프로그램 종료 시점에서 custom 구조체와 mlx구조체의 완전한 해제를 시켜주면 해결될 문제로 보인다.
이를 위해 함수를 따로 만들어 관리하기로 한다.
==21047== HEAP SUMMARY:
==21047== in use at exit: 202,736 bytes in 103 blocks
==21047== total heap usage: 717 allocs, 614 frees, 495,898 bytes allocated
==21047==
==21047== 39 bytes in 2 blocks are definitely lost in loss record 17 of 52
==21047== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21047== by 0x116329: ft_strdup
==21047== by 0x10F176: identifier_ss
==21047== by 0x10F29C: parse_identifier
==21047== by 0x10EAD2: parse_type
==21047== by 0x10EB4D: parse_conf
==21047== by 0x10ED60: parse_conf_cub
==21047== by 0x1120EA: main
==21047==
==21047== 63 bytes in 3 blocks are definitely lost in loss record 21 of 52
==21047== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21047== by 0x116329: ft_strdup
==21047== by 0x10F045: identifier_nwe
==21047== by 0x10F275: parse_identifier
==21047== by 0x10EAD2: parse_type
==21047== by 0x10EB4D: parse_conf
==21047== by 0x10ED60: parse_conf_cub
==21047== by 0x1120EA: main
==21047==
==21047== 224 (88 direct, 136 indirect) bytes in 1 blocks are definitely lost in loss record 36 of 52
==21047== at 0x4C33B25: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21047== by 0x112B47: mlx_int_new_xshm_image
==21047== by 0x112E56: mlx_new_image
==21047== by 0x11093A: init_img
==21047== by 0x110BFF: init_cub
==21047== by 0x112103: main
==21047==
==21047== 712 (136 direct, 576 indirect) bytes in 1 blocks are definitely lost in loss record 40 of 52
==21047== at 0x4C33B25: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21047== by 0x10F382: resize_map
==21047== by 0x10F638: is_valid_map
==21047== by 0x10ED6C: parse_conf_cub
==21047== by 0x1120EA: main
==21047==
==21047== 1,224 (128 direct, 1,096 indirect) bytes in 1 blocks are definitely lost in loss record 43 of 52
==21047== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21047== by 0x11237A: mlx_init
==21047== by 0x11082C: init_mlx
==21047== by 0x110BF3: init_cub
==21047== by 0x112103: main
==21047==
==21047== 131,136 (64 direct, 131,072 indirect) bytes in 1 blocks are definitely lost in loss record 52 of 52
==21047== at 0x4C33B25: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21047== by 0x111B89: allocate_tex_arr
==21047== by 0x111DD4: load_texture
==21047== by 0x110C38: init_cub
==21047== by 0x112103: main
==21047==
==21047== LEAK SUMMARY:
==21047== definitely lost: 518 bytes in 9 blocks
==21047== indirectly lost: 132,880 bytes in 27 blocks
==21047== possibly lost: 0 bytes in 0 blocks
==21047== still reachable: 69,338 bytes in 67 blocks
==21047== suppressed: 0 bytes in 0 blocks
==21047== Reachable blocks (those to which a pointer was found) are not shown.
==21047== To see them, rerun with: --leak-check=full --show-leak-kinds=all
종료 시점에서 free해주어야 할 것들을 free해주는 함수void free_cub_struct(t_cub *cub)
를 만든 후 종료시켰다.
메모리 릭이 잡히지 않았다.
스프라이트 구현의 이해: Lodev 코드 이해
ZBuffer[x] = perpWallDist;
: 내 위치에서 POV_W 내에서 빛을 쐈을 때의 perpendicular Distance를 저장해야한다.
spriteOrder
: 스프라이트 배열에서의 스프라이트 위치
spriteDistance
: 스트라이트의 유클리드 거리
위 두 변수들은 sprite 구조체 안의 배열들인데, order는 distance를 기준으로 정렬된 배열에 맞춰 인덱스를 조정하기 위해 필요해보인다.
int spriteScreenX = int((w / 2) * (1 + transformX / transformY));
:
spritescreenX의 의미: (1 + transformX / transformY)
으로 tranform된 벡터공간을 screen으로 옮기기 위해 screenx에 맞게 비율 조정한 뒤, 계산된 screen의 sprite 픽셀 위치.
//SPRITE CASTING
//sort sprites from far to close
for(int i = 0; i < numSprites; i++)
{
spriteOrder[i] = i;
spriteDistance[i] = ((posX - sprite[i].x) * (posX - sprite[i].x) + (posY - sprite[i].y) * (posY - sprite[i].y)); //sqrt not taken, unneeded
}
sortSprites(spriteOrder, spriteDistance, numSprites);
//after sorting the sprites, do the projection and draw them
for(int i = 0; i < numSprites; i++)
{
//translate sprite position to relative to camera
double spriteX = sprite[spriteOrder[i]].x - posX;
double spriteY = sprite[spriteOrder[i]].y - posY;
//transform sprite with the inverse camera matrix
// [ planeX dirX ] -1 [ dirY -dirX ]
// [ ] = 1/(planeX*dirY-dirX*planeY) * [ ]
// [ planeY dirY ] [ -planeY planeX ]
double invDet = 1.0 / (planeX * dirY - dirX * planeY); //required for correct matrix multiplication
//transform된 벡터 공간의 spritex와 spritey의 좌표. == transform(sprite)x, transform(sprite)y.
double transformX = invDet * (dirY * spriteX - dirX * spriteY);
double transformY = invDet * (-planeY * spriteX + planeX * spriteY); //this is actually the depth inside the screen, that what Z is in 3D
//`(1 + transformX / transformY)`으로 tranform된 벡터공간을 screen으로 옮기기 위해 screenx에 맞게 비율 조정한 뒤, 계산된 screen의 sprite 픽셀 위치.
int spriteScreenX = int((w / 2) * (1 + transformX / transformY));
//calculate height of the sprite on screen
int spriteHeight = abs(int(h / (transformY))); //using 'transformY' instead of the real distance prevents fisheye
//calculate lowest and highest pixel to fill in current stripe
int drawStartY = -spriteHeight / 2 + h / 2;
if(drawStartY < 0) drawStartY = 0;
int drawEndY = spriteHeight / 2 + h / 2;
if(drawEndY >= h) drawEndY = h - 1;
//calculate width of the sprite
int spriteWidth = abs( int (h / (transformY)));
int drawStartX = -spriteWidth / 2 + spriteScreenX;
if(drawStartX < 0) drawStartX = 0;
int drawEndX = spriteWidth / 2 + spriteScreenX;
if(drawEndX >= w) drawEndX = w - 1;
//loop through every vertical stripe of the sprite on screen
for(int stripe = drawStartX; stripe < drawEndX; stripe++)
{
//가로 픽셀 좌표? * (스크린 -> 텍스쳐로의 비) == screen의 texture에서의 좌표
int texX = int(256 * (stripe - (-spriteWidth / 2 + spriteScreenX)) * texWidth / spriteWidth) / 256;
//the conditions in the if are:
//1) it's in front of camera plane so you don't see things behind you
//2) it's on the screen (left)
//3) it's on the screen (right)
//4) ZBuffer, with perpendicular distance
if(transformY > 0 && stripe > 0 && stripe < w && transformY < ZBuffer[stripe])
for(int y = drawStartY; y < drawEndY; y++) //for every pixel of the current stripe
{
//float로의 전환을 방지하기 위해 (y - h/2 + spriteheight) << 2^8
int d = (y) * 256 - h * 128 + spriteHeight * 128; //256 and 128 factors to avoid floats
int texY = ((d * texHeight) / spriteHeight) / 256;
Uint32 color = texture[sprite[spriteOrder[i]].texture][texWidth * texY + texX]; //get current color from the texture
if((color & 0x00FFFFFF) != 0) buffer[y][stripe] = color; //paint pixel if it isn't black, black is the invisible color
}
}
}
sprite 구현위한 기능
- parsing 할 때는, 기본 sprite만 받도록 한다.
- texture_num의 기본 texture 수 = 10으로 고정.
perp_buff
의 초기화는 init 시에 이루어져야한다.texture.c
의load_texture()
에 sprite를 위한 추가 spriteload_image_2_texture()
를 4개 더 만들도록 한다.load_img_2_texture(cub, TEXTURE_SP_1, "./ss/..")
- 이렇게 loading 된 texture들은 cub의 texture 포인터가 주소를 들고 있으며, sprite 텍스쳐들은 ,index가 6부터 시작한다.
- sprite 구조체의 초기화는
-
init_sprite
함수와 직접 값을 넣어줘서 위치를 초기화시켜주도록 한다.
-
- sprite 구조체의 생김새 :
struct Sprite { double x; double y; int texture; };
-
cub에perpwallBuff
, spriteorder, spritedistance 포인터, 배열을 만들어주며,perpwallbuff
는 screen의 width를 런타임에 받아야하기 때문에, 동적으로 할당해주도록 한다. - cub에 아예 sprite를 그리기 위한 sprite_op 구조체를 따로 만들어주도록한다.
sprite_op : double spritex; double spritey; double inv_det; double trans_x; double trans_y; int sprite_scx; int sprite_h; int sprite_w; int drawstart_x; int drawend_x; 로 초기화 시킨다.
- sprite들을 내 위치에 따라 sorting하는 함수 필요.
sortsprites(t_cub *, int *, int *)
정도 될것.
sprite 구현 로그
https://lodev.org/cgtutor/raycasting3.html <<
int init_perp_buff(t_cub *cub)
//in raycasting_1.c
cub->perp_buff[screen_x] = cub->ray.perpwalldist;
void sprite_rayc(t_cub *cub)
{
//init_sprite() : cub의 sprite 구조체 배열 생성, 구조체 배열의 내용물 추가, 공간할당 실패 시 정상종료.
//conf_struct_sprite() : 구조체 배열의 내용물 추가 인터페이스.
//sortsprites()
//init_sprite_op_data()
//calculate
//draw_sprite()
}
구현한 코드
init_sprite()
conf_struct_sprite()
sort_sprites()
수정한 코드
raycasting.h의 TEXTURE_SP_K
texture.c의 load_texture
void load_texture(t_cub *cub, t_conf *conf
수정 해야할 코드
draw_sprite.c에 draw_sprite(t_cub*, t_op *, int line)
으로 sprite_rayc에 있는 코드를 옮겨담기.
raycasting_sprite.c
draw_sprite.c
학습에 도움이 된 사이트
'2021 > 일일 기록' 카테고리의 다른 글
2021-03-30 : 데이터분석 (0) | 2021.03.30 |
---|---|
20210321(일) : cub 진행상황 (0) | 2021.03.22 |
20210318(목) : cub 진행상황 (0) | 2021.03.19 |
20210317(수) : cub 진행상황 (0) | 2021.03.18 |
20210312(일) : cub 진행상황 (0) | 2021.03.17 |