Notice
Recent Posts
Recent Comments
Link
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Archives
Today
Total
관리 메뉴

TIL

20210319(금) : cub 진행 상황 본문

2021/일일 기록

20210319(금) : cub 진행 상황

ililillllllliilli 2021. 3. 20. 01:23

해야 할 것

  • 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 메시지가 떴다.

원인 :

  1. 프로그램 종료 바로 이전 시점에서 parser에서 사용한 구조체 conf의 해제를 해주지 않아보임.
  2. 프로그램 종료 시점에서 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.cload_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




학습에 도움이 된 사이트

주제 사이트
BMP 파일 생성 https://stackoverflow.com/questions/50090500/create-simple-bitmap-in-c-without-external-libraries
비트와 비트맵 https://medium.com/sysf/bits-to-bitmaps-a-simple-walkthrough-of-bmp-image-format-765dc6857393
구조체 정렬과 cub3d https://dojang.io/mod/page/view.php?id=432
로데브's sprite raycasting https://lodev.org/cgtutor/raycasting3.html

'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
Comments