Skip to content

myungjinki/cub3d

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

목표

Raycasting 알고리즘을 이용해서 게임을 만들어라.


필요한 지식

  • C
  • Makefile
  • mlx 사용법
  • 벡터
  • 삼각함수
  • 회전행렬
  • Raycasting

개발 일지

시작

  • 문제 분석하기

    • 문제를 분석하고 내가 무엇을 어떻게 만들 것인지 전략을 짜봅시다.

    우선 문제부터 볼까요?

    • 그 다음 자료부터 수집해야겠죠? 과제 페이지에 있는 minilibx로 뭔가를 하는 것 같은데...
    • mlx 라이브러리안의 함수들이 각각 어떤 역할을 하는지 이것저것 해봅시다.
    • https://github.com/taelee42/mlx_example를 따라해보면 도움이 됩니다.
    • mlx를 이용해서 차근차근 과제를 진행해 나가봅시다.
    • 플레이어 만드는 함수 구현하기
    • 플레이어 키보드 입력 받기
    • 동시 입력 구현하기
    • 격자 만드는 함수 구현하기
    • 벽 만드는 함수 구현하기
    • Makefile, header, main만들기
    • 함수는 하나에 한 가지 기능만! 언제든지 불러와서 쓸 수 있게 범용성 있는 함수를 만들어보아요.
  • 자료 조사

    • 음... mlx는 약간 익숙해진 것 같은데... 이제 뭘 해야 하죠? 도저히 혼자서는 못하겠어요. 친구들아 부탁해!
    • 자료만 훑어보는 걸로 하루를 보내봐요.
    • https://lodev.org/cgtutor/raycasting.html 약칭 '로데브'라고 하는 이 사이트에는 cub3d의 정답이 들어있습니다. 하지만 영어이기 때문에, 영어가 익숙치 않은 사람들에게는 적합하지 않은 자료입니다.
    • 그래서 https://github.com/365kim/raycasting_tutorial에 번역을 해 주신 cadet분이 계십니다. 이것을 보고 차근차근 이해하면 될 것 같지만 여전히 모르겠습니다.
    • minckim님이 정리해 놓은 pdf 파일과 github도 있습니다. https://github.com/minckim0/cub3d_lect
    • sucho님이 정리해 놓은 자료도 있습니다. https://github.com/sungyongcho/ii2r
    • https://42kchoi.tistory.com/category/Code 42/miniLibX%2C 3서클 이 자료도 도움이 될 겁니다.
    • 참고자료를 이용해서 벽을 세우는 방법을 이해하고 구현해야 합니다.
    • 그래서... 벽을 도대체 어떻게 세우죠?
    • 광선을 쏘고, DDA 알고리즘을 이용해서 벽을 그리라는데... 정말 모르겠어요...
    • 자 그래서... 블로그를 봐도 하나도 모르겠으니... 하나씩 격파를 해보죠.
  • 문제 분할하기

  • Makefile 만들기

    문제: mlx라이브러리 libmlx.a와 libft라이브러리 libft.a 두 개를 합치고 싶은데 ar 명령어로는 동작하지 않습니다.

    해결: libtool 명령어를 이용합시다.

    libtool -static libft.a libmlx.a -o cub3d.a

    참고: https://kwonsaw.tistory.com/42

2D로 그려보기

  • mlx 튜토리얼 따라해보기

    • mlx manpage

      • mlx

        MiniLibX(3)                                                                                       MiniLibX(3)
        
        NAME
               MiniLibX - Simple Graphical Interface Library for students
        
        SYNOPSYS
               #include <mlx.h>
        
               void *
               mlx_init ();
        
        DESCRIPTION
               MiniLibX  is  an  easy way to create graphical software, without any X-Window/Cocoa programming knowl-
               edge. It provides simple window creation, a drawing tool, image and basic events management.
        
        BSD/LINUX X-WINDOW CONCEPT
               X-Window is a network-oriented graphical system for Unix.  It is based on two main parts:
               On one side, your software wants to draw something on the screen and/or get keyboard & mouse  entries.
               On  the  other  side, the X-Server manages the screen, keyboard and mouse (It is often refered to as a
               "display").
               A network connection must be established between these two entities to send drawing orders  (from  the
               software to the X-Server), and keyboard/mouse events (from the X-Server to the software).
        
        MACOSX CONCEPT
               The MacOSX operating system handle graphical access to the screen (or "display").
               On  one side, your software wants to draw something on the screen and/or get keyboard & mouse entries.
               On the other side, the underlying MacOSX graphical framework that handles the  screen,  the  windowing
               system, keyboard and mouse.
               A connection between these two entities must be established.
        
        INCLUDE FILE
               mlx.h should be included for a correct use of the MiniLibX API.  It only contains function prototypes,
               no structure is needed.
        
        LIBRARY FUNCTIONS
               First of all, you need to initialize the connection between your software and the display.  Once  this
               connection  is  established,  you'll  be  able  to  use other MiniLibX functions to send the graphical
               orders, like "I want to draw a yellow pixel in this window" or "did the user hit a key?".
        
               The mlx_init function will create this connection. No parameters are needed, ant it will return a void
               * identifier, used for further calls to the library routines.
        
               All other MiniLibX functions are described in the following man pages:
        
               mlx_new_window      : manage windows
        
               mlx_pixel_put       : draw inside window
        
               mlx_new_image       : manipulate images
        
               mlx_loop            : handle keyboard or mouse events
        
        LINKING MiniLibX on BSD/Linux and X-Window
               To  use  MiniLibX  functions,  you'll need to link your software with several libraries, including the
               MiniLibX library itself.  To do this, simply add the following arguments at linking time:
        
               -lmlx -lXext -lX11
        
               You may also need to specify the path to these libraries, using the -L flag.
        
        LINKING MiniLibX on MACOSX
               To use MiniLibX functions, you'll need to link your software with the MiniLibX  library,  and  several
               system frameworks:
        
               -lmlx -framework OpenGL -framework AppKit
        
               You may also need to specify the path to the MiniLibX library, using the -L flag.
        
        RETURN VALUES
               If mlx_init() fails to set up the connection to the graphical system, it will return NULL, otherwise a
               non-null pointer is returned as a connection identifier.
        
        SEE ALSO
               mlx_new_window(3), mlx_pixel_put(3), mlx_new_image(3), mlx_loop(3)
        
        AUTHOR
               Copyright ol@ - 2002-2015 - Olivier Crouzet
        
                                                      September 19, 2002                                  MiniLibX(3)
        
      • mlx_new_window

        MiniLibX(3)                                                                                       MiniLibX(3)
        
        NAME
               MiniLibX - Managing windows
        
        SYNOPSYS
               void *
               mlx_new_window ( void *mlx_ptr, int size_x, int size_y, char *title );
        
               int
               mlx_clear_window ( void *mlx_ptr, void *win_ptr );
        
               int
               mlx_destroy_window ( void *mlx_ptr, void *win_ptr );
        
        DESCRIPTION
               The mlx_new_window () function creates a new window on the screen, using the size_x and size_y parame-
               ters to determine its size, and title as the text that should be displayed in the window's title  bar.
               The  mlx_ptr  parameter  is  the connection identifier returned by mlx_init () (see the mlx man page).
               mlx_new_window () returns a void * window identifier that can be used by other MiniLibX  calls.   Note
               that the MiniLibX can handle an arbitrary number of separate windows.
        
               mlx_clear_window () and mlx_destroy_window () respectively clear (in black) and destroy the given win-
               dow. They both have the same parameters: mlx_ptr is the screen connection identifier, and win_ptr is a
               window identifier.
        
        RETURN VALUES
               If mlx_new_window() fails to create a new window (for wathever reason), it will return NULL, otherwise
               a non-null pointer is returned as a window identifier.  mlx_clear_window and mlx_destroy_window  right
               now return nothing.
        
        SEE ALSO
               mlx(3), mlx_pixel_put(3), mlx_new_image(3), mlx_loop(3)
        
        AUTHOR
               Copyright ol@ - 2002-2015 - Olivier Crouzet
        
                                                      September 19, 2002                                  MiniLibX(3)
        
      • mlx_pixel_put

        MiniLibX(3)                                                                                       MiniLibX(3)
        
        NAME
               MiniLibX - Drawing inside windows
        
        SYNOPSYS
               int
               mlx_pixel_put ( void *mlx_ptr, void *win_ptr, int x, int y, int color );
        
               int
               mlx_string_put ( void *mlx_ptr, void *win_ptr, int x, int y, int color, char *string );
        
        DESCRIPTION
               The  mlx_pixel_put () function draws a defined pixel in the window win_ptr using the ( x , y ) coordi-
               nates, and the specified color . The origin (0,0) is the upper left corner of the window, the x and  y
               axis respectively pointing right and down. The connection identifier, mlx_ptr , is needed (see the mlx
               man page).
        
               Parameters for mlx_string_put () have the same meaning. Instead  of  a  simple  pixel,  the  specified
               string will be displayed at ( x , y ).
        
               In  both  functions, it is impossible to display anything outside the specified window, nor display in
               another window in front of the selected one.
        
        COLOR MANAGEMENT
               The color parameter has an integer type. The displayed color needs to be encoded in this integer, fol-
               lowing  a  defined scheme. All displayable colors can be split in 3 basic colors: red, green and blue.
               Three associated values, in the 0-255 range, represent how much of each color is mixed  up  to  create
               the original color. Theses three values must be set inside the integer to display the right color. The
               three least significant bytes of this integer are filled as shown in the picture below:
        
                       | 0 | R | G | B |   color integer
                       +---+---+---+---+
        
               While filling the integer, make sure you avoid endian problems. Remember that the "blue"  byte  should
               always be the least significant one.
        
        SEE ALSO
               mlx(3), mlx_new_window(3), mlx_new_image(3), mlx_loop(3)
        
        AUTHOR
               Copyright ol@ - 2002-2015 - Olivier Crouzet
        
                                                      September 19, 2002                                  MiniLibX(3)
        
      • mlx_new_image

        MiniLibX(3)                                                                                       MiniLibX(3)
        
        NAME
               MiniLibX - Manipulating images
        
        SYNOPSYS
               void *
               mlx_new_image ( void *mlx_ptr, int width, int height );
        
               char *
               mlx_get_data_addr ( void *img_ptr, int *bits_per_pixel, int *size_line, int *endian );
        
               int
               mlx_put_image_to_window ( void *mlx_ptr, void *win_ptr, void *img_ptr, int x, int y );
        
               unsigned int
               mlx_get_color_value ( void *mlx_ptr, int color );
        
               void *
               mlx_xpm_to_image ( void *mlx_ptr, char **xpm_data, int *width, int *height );
        
               void *
               mlx_xpm_file_to_image ( void *mlx_ptr, char *filename, int *width, int *height );
        
               int
               mlx_destroy_image ( void *mlx_ptr, void *img_ptr );
        
        DESCRIPTION
               mlx_new_image  ()  creates  a new image in memory. It returns a void * identifier needed to manipulate
               this image later. It only needs the size of the image to be created, using the width and height param-
               eters, and the mlx_ptr connection identifier (see the mlx manual).
        
               The  user  can  draw inside the image (see below), and can dump the image inside a specified window at
               any time to display it on the screen. This is done using mlx_put_image_to_window (). Three identifiers
               are  needed  here,  for  the connection to the display, the window to use, and the image (respectively
               mlx_ptr , win_ptr and img_ptr ). The ( x , y ) coordinates define where the image should be placed  in
               the window.
        
               mlx_get_data_addr  () returns information about the created image, allowing a user to modify it later.
               The img_ptr parameter specifies the image to use. The three next parameters should be the addresses of
               three  different valid integers.  bits_per_pixel will be filled with the number of bits needed to rep-
               resent a pixel color (also called the depth of the image).  size_line is the number of bytes  used  to
               store one line of the image in memory.  This information is needed to move from one line to another in
               the image.  endian tells you wether the pixel color in the image needs to be stored in little endian (
               endian == 0), or big endian ( endian == 1).
        
               mlx_get_data_addr  returns  a char * address that represents the begining of the memory area where the
               image is stored. From this adress, the first bits_per_pixel bits represent  the  color  of  the  first
               pixel  in  the  first  line of the image. The second group of bits_per_pixel bits represent the second
               pixel of the first line, and so on.  Add size_line to the adress to get the  begining  of  the  second
               line. You can reach any pixels of the image that way.
        
               mlx_destroy_image destroys the given image ( img_ptr ).
        
        STORING COLOR INSIDE IMAGES
               Depending  on the display, the number of bits used to store a pixel color can change. The user usually
               represents a color in RGB mode, using one byte for each component  (see  mlx_pixel_put  manual).  This
               must  be translated to fit the bits_per_pixel requirement of the image, and make the color understand-
               able to the graphical system.  That is the purpose of the mlx_get_color_value () function. It takes  a
               standard RGB color parameter, and returns an unsigned int value.  The bits_per_pixel least significant
               bits of this value can be stored in the image.
        
               Keep in mind that the least significant bits position depends on the local computer's endian.  If  the
               endian  of  the  image  (in fact the endian of the X-Server's computer for remote X11 display) differs
               from the local endian, then the value should be transformed before being used.
        
        XPM IMAGES
               The mlx_xpm_to_image () and mlx_xpm_file_to_image () functions will create a new image the  same  way.
               They  will  fill  it  using  the specified xpm_data or filename , depending on which function is used.
               Note that MiniLibX does not use the standard Xpm library to deal with xpm images. You may not be  able
               to read all types of xpm images. It however handles transparency.
        
        RETURN VALUES
               The    three    functions    that    create   images,   mlx_new_image()   ,   mlx_xpm_to_image()   and
               mlx_xpm_file_to_image() , will return NULL if an  error  occurs.  Otherwise  they  return  a  non-null
               pointer as an image identifier.
        
        SEE ALSO
               mlx(3), mlx_new_window(3), mlx_pixel_put(3), mlx_loop(3)
        
        AUTHOR
               Copyright ol@ - 2002-2015 - Olivier Crouzet
        
                                                      September 19, 2002                                  MiniLibX(3)
        
      • mlx_loop

        MiniLibX(3)                                                                                       MiniLibX(3)
        
        NAME
               MiniLibX - Handle events
        
        SYNOPSYS
               int
               mlx_loop ( void *mlx_ptr );
        
               int
               mlx_key_hook ( void *win_ptr, int (*funct_ptr)(), void *param );
        
               int
               mlx_mouse_hook ( void *win_ptr, int (*funct_ptr)(), void *param );
        
               int
               mlx_expose_hook ( void *win_ptr, int (*funct_ptr)(), void *param );
        
               int
               mlx_loop_hook ( void *mlx_ptr, int (*funct_ptr)(), void *param );
        
        EVENTS
               Both X-Window and MacOSX graphical systems are bi-directionnal.  On one hand, the program sends orders
               to the screen to display pixels, images, and so on. On the other hand, it can get information from the
               keyboard and mouse associated to the screen. To do so, the program receives "events" from the keyboard
               or the mouse.
        
        DESCRIPTION
               To receive events, you must use mlx_loop (). This function never returns. It is an infinite loop  that
               waits  for  an  event,  and  then  calls a user-defined function associated with this event.  A single
               parameter is needed, the connection identifier mlx_ptr (see the mlx manual).
        
               You can assign different functions to the three following events:
               - A key is pressed
               - The mouse button is pressed
               - A part of the window should be re-drawn (this is called an "expose" event, and it is your  program's
               job to handle it).
        
               Each window can define a different function for the same event.
        
               The  three  functions  mlx_key_hook (), mlx_mouse_hook () and mlx_expose_hook () work exactly the same
               way.  funct_ptr is a pointer to the function you want to be called when an event occurs. This  assign-
               ment  is  specific to the window defined by the win_ptr identifier. The param adress will be passed to
               the function everytime it is called, and should be used to store the parameters it might need.
        
               The syntax for the mlx_loop_hook () function is identical to the previous ones, but the given function
               will be called when no event occurs.
        
               When it catches an event, the MiniLibX calls the corresponding function with fixed parameters:
        
                 expose_hook(void *param);
                 key_hook(int keycode,void *param);
                 mouse_hook(int button,int x,int y,void *param);
                 loop_hook(void *param);
        
               These  function  names  are  arbitrary.  They here are used to distinguish parameters according to the
               event. These functions are NOT part of the MiniLibX.
        
               param is the address specified in the mlx_*_hook calls. This address is never used nor modified by the
               MiniLibX.  On  key  and mouse events, additional information is passed: keycode tells you which key is
               pressed (X11 : look for the include file "keysymdef.h", MacOS : create a small software and  find  out
               by  yourself),  (  x  , y ) are the coordinates of the mouse click in the window, and button tells you
               which mouse button was pressed.
        
        GOING FURTHER WITH EVENTS
               The MiniLibX provides a much generic access to all type of events. The mlx.h include define mlx_hook()
               in  the  same  manner  mlx_*_hook functions work. The event and mask values will be taken from the X11
               include file "X.h" (even for MacOSX, for compatibility purposes)
        
               See source code of mlx_int_param_event.c to find out how the MiniLibX will call your own function  for
               a specific event.
        
        SEE ALSO
               mlx(3), mlx_new_window(3), mlx_pixel_put(3), mlx_new_image(3)
        
        AUTHOR
               Copyright ol@ - 2002-2015 - Olivier Crouzet
        
                                                      September 19, 2002                                  MiniLibX(3)
        
    • mlx function

      int mlx_clear_window(void *mlx_ptr, void *win_ptr)

      return void *0 if failed

      int mlx_destroy_image(void *mlx_ptr, void *img_ptr)

      int mlx_destroy_window(void *mlx_ptr, void *win_ptr)

      int mlx_do_key_autorepeatoff(void *mlx_ptr)

      int mlx_do_key_autorepeaton(void *mlx_ptr)

      int mlx_do_sync(void *mlx_ptr)

      int mlx_expose_hook(void *win_ptr, int (*funct_ptr)(), void *param)

      unsigned int mlx_get_color_value(void *mlx_ptr, int color)

      char * mlx_get_data_addr(void *img_ptr, int *bits_per_pixel, int *size_line, int *endian)

      return void *0 if failed

      int mlx_hook(void *win_ptr, int x_event, int x_mask, int (*funct)(), void *param)

      void *mlx_init()

      int mlx_key_hook(void *win_ptr, int (*funct_ptr)(), void *param)

      int mlx_loop(void *mlx_ptr)

      int mlx_loop_hook(void *mlx_ptr, int (*funct_ptr)(), void *param)

      int mlx_mouse_get_pos(void *win_ptr, int *x, int *y)

      int mlx_mouse_hide()

      int mlx_mouse_hook(void *win_ptr, int (*funct_ptr)(), void *param)

      int mlx_mouse_move(void *win_ptr, int x, int y)

      int mlx_mouse_show()

      void * mlx_new_image(void *mlx_ptr, int width, int height)

      void * mlx_new_window(void *mlx_ptr, int size_x, int size_y, char *title)

      int mlx_pixel_put(void *mlx_ptr, void *win_ptr, int x, int y, int color)

      void * mlx_png_file_to_image(void *mlx_ptr, char *file, int *width, int *height)

      int mlx_put_image_to_window(void *mlx_ptr, void *win_ptr, void *img_ptr, int x, int y)

      endian : 0 = sever X is little endian, 1 = big endian endian : useless on macos, client and graphical framework have the same endian

      int mlx_string_put(void *mlx_ptr, void *win_ptr, int x, int y, int color, char *string)

      void * mlx_xpm_file_to_image(void *mlx_ptr, char *filename, int *width, int *height)

      void * mlx_xpm_to_image(void *mlx_ptr, char **xpm_data, int *width, int *height)

    • key data

      KEY_PRESS 2

      KEY_release 3

      KEY_EXIT 17

    • key data 2

      define KEY_ESC 53

      define KEY_Q 12

      define KEY_W 13

      define KEY_E 14

      define KEY_R 15

      define KEY_A 0

      define KEY_S 1

      define KEY_D 2

  • 벡터 함수 만들기

    • minckim님의 자료부터 하나씩 해봅시다.

    cub3d_lect.pdf

    • https://github.com/minckim0/cub3d_lect
    • 벡터를 사용해야 한다는데... 벡터 계산하는 함수부터 구현해볼까요?
    • 벡터 덧셈, 벡터 뺄셈, 벡터 곱셈, 벡터 나눗셈 함수를 만들어봅시다.
    • 그래서 우선 연립방정식을 푸는 함수를 구현해봅시다.
    • 그리고 벡터의 회전 함수를 구현해봅시다.
    • <math.h>라이브러리의 M_PI 매크로를 이용하면 pi 값을 얻을 수 있습니다.
    • 지금까지 완전 딴 생각을 하고 있었습니다.
    • 몇 번을 봐도 pdf는 불친절하고 코드는 아직 보기 싫어요. 이 pdf가 과연 친절한 걸까요?
    • 이제 그만하고 다시 로데브 번역본을 봅시다ㅠㅠ 지쳐가요...
  • 맵 라인 그리기 - 격자 그리기

  • 맵 벽 칠하기

  • 플레이어 그리기

  • 플레이어 움직이기 - 키 입력

  • 동시 입력 구현하기

  • 방향 벡터 구현하기 - 대각선과 회전 벡터

  • 카메라 벡터 구현하기

    • 이제 방향벡터와 수직인 카메라 벡터를 만들어봅시다.
    • 그런데 잘 그려지지는 않네요... 그냥 넘어갑시다.
  • 광선 만들기

    • 코드를 전반적으로 수정했습니다.
    • pixel에 x, y를 주는 것만으로 알아서 계산하도록 ft_pixel을 만들었고, 모두 벡터 계산으로 pixel에 색을 바꾸는 식으로 코드를 수정했습니다.
    • 또한 시점과 종점, 그리고 색깔을 주면 알아서 라인이 그려지도록 만들었습니다.
    • 또한 라인을 그릴 때는 기준 delta만큼 반복하도록 코드를 수정했습니다. 그러니 방향벡터와 카메라 벡터가 잘 그려집니다.
    • 라인을 그릴 때 주의할 점은 double과 int의 변환으로 인한 차이를 아는 것 입니다. 기울기만큼 pixel 값을 변화 시킬 때는 double이지만, 실제로 pixel은 int밖에 없으므로 언제 int로 형변환해야 하는지 아는 것이 중요합니다.
    • 다음은 ray를 그릴 차례입니다.
  • 광선 벽에 부딪히기

로데브 적용

  • 로데브 이해하기

    • 아... 로데브를 몇 번을 봤는데... 이제야 이해가 되는 느낌이에요
    • 카메라 평면과 방향벡터를 구현해야 하고, 방향벡터는 광선이 아니군요...
    • 그럼 카메라 평면과 방향벡터를 구현해봅시다!
    • 생각을 해보니... 플레이어 이미지를 3 * 3으로 줬는데, 생각을 해보니 방향벡터 이미지도 또 만들어야 해서 그럴 바에는 그냥 한번에 플레이어와 방향벡터를 한 이미지로 처리하면 어떨까 하는 생각이 들어 플레이어를 맵처럼 크게 그리고 움직이게 하려고 했는데... 엉뚱한 결과가 나왔군요. 다시 방향벡터 이미지를 새로 만듭시다.
    • 사실 생각을 해보니... 방향벡터 이미지도 새로 만들어서 맵 위에 그리면... 그리지 않은 부분은 또 검은색이 맵 위에 덮어 씌워질텐데... 이거 완전 잘못 생각 하고 있었나봐요... 맵을 새로 계속 갱신해야 하나봐요.
    • 저처럼 이미지 두개로 하려고 하지 마세요... 심지어 이거 아직 튜토리얼입니다.
  • 벽 세우기

    문제: 동서남북을 어떻게 인식할까요?

    해결: side와 ray.x, ray.y로 구분합시다.

    문제: 동서남북의 기준은 플레이어인가요 벽인가요?

    해결: 플레이어가 북쪽을 바라보면 북쪽 텍스쳐가 나와야 합니다. 즉 장애물은 남쪽방향이겠죠.

    문제: 로데브를 적용하세요. 그런데 왜 느릴까요?

    해결: 이미지를 넣을 때 x반복문 않에 넣어서 그러네요;;;

    문제: 회전이 되지 않아요.

    해결: 왜 sin, cos초기화를 제대로 안했죠?

    문제: 거리가 이상하게 구해져서 이미지에 값이 안들어가져요.

    해결: 맵을 복사할 때 memcpy를 사용하는데, 복사하는 함수 위치가 변수 초기화 이전에 있어서, 쓰레기 값이 들어갔네요...

    문제: 아래 유도 해설을 이해하세요.

    https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7c067a1d-2b7d-4155-a61d-3c4919c13040/Untitled.png

    The image shows:
    P: position of the player
    H: hitpoint of the ray on the wall
    perpWallDist: the length of this line is the value to compute now, the distance perpenducilar from the wall hit point to the camera plane instead of Euclidean distance to the player point, to avoid making straight walls look rounded.
    yDist matches what is "(mapY - posY + (1 - stepY) / 2)" in the code above, this is the y coordinate of the Euclidean distance vector, in world coordinates.
    Euclidean is the Euclidean distance from the player P to the exact hit point H. Its direction is the rayDir, but its length is all the way to the wall.
    rayDir: the direction of the ray marked "Euclidean", matching the rayDirX and rayDirY variables in the code. Note that its length |rayDir| is not 1 but slightly higher, due to how we added a value to (dirX,dirY) (the dir vector, which is normalized to 1) in the code.
    rayDirX and rayDirY: the X and Y components of rayDir, matching the rayDirX and rayDirY variables in the code.
    dir: the main player looking direction, given by dirX,dirY in the code. The length of this vector is always exactly 1. This matches the looking direction in the center of the screen, as opposed to the direction of the current ray. It is perpendicular to the camera plane, and perpWallDist is parallel to this.
    orange dotted line (may be hard to see, use CTRL+scrollwheel or CTRL+plus to zoom in a desktop browser to see it better): the value that was added to dir to get rayDir. Importantly, this is parallel to the camera plane, perpendicular to dir.
    camera plane: this is the camera plane, the line given by cameraX and cameraY, perpendicular to the main player's looking direction.
    
    A: point of the camera plane closest to H, the point where perpWallDist intersects with camera plane
    B: point of X-axis through player closest to H, point where yDist crosses X-axis through the player
    C: point at player position + rayDirX
    D: point at player position + rayDir.
    E: This is point D with the dir vector subtracted, in other words, E + dir = D.
    points A, B, C, D, E, H and P are used in the explanation below: they form triangles which are considered: BHP, CDP, AHP and DEP.
    
    And the derivation of the perpWallDist computation above then is:
    
    1: "(mapY - posY + (1 - stepY) / 2) / rayDirY" is "yDist / rayDirY" in the picture.
    2: Triangles PBH and PCD have the same shape but different size, so same ratios of edges
    3: Given step 2, the triangles show that the ratio yDist / rayDirY is equal to the ratio Euclidean / |rayDir|, so now we can derive perpWallDist = Euclidean / |rayDir| instead.
    4: Triangles AHP and EDP have the same shape but different size, so same ratios of edges. Length of edge ED, that is |ED|, equals length of dir, |dir|, which is 1. Similarly, |DP| equals |rayDir|.
    5: Given step 4, the triangles show that the ratio Euclidean / |rayDir| = perpWallDist / |dir| = perpWallDist / 1.
    6: Combining steps 5 and 3 shows that perpWallDist = yDist / rayDirY, the computation used in the code above
    
    [Thanks to Roux Morgan for helping to clarify the explanation of perpWallDist in 2020, the tutorial was lacking some information before this]
    

    해결: 삼각비를 이용한 공식이다.

    문제: 벽을 세우기 무섭습니다.

    해결: 다시 처음부터 시작입니다. 로데브를 적용해서 바로 벽을 세웁시다. 공부는 할 만큼 했습니다.

    • 로데브에서 벽을 세우는 과정은 절반은 이해를 했습니다. 그런데 문제가 발생해서 다 뜯어고쳐야 합니다.
    • 좌표계를 완전히 수정하는 날입니다.
    • 이제는 벽에 맞출 차례입니다. 바로 DDA의 등장.
    • https://github.com/365kim/raycasting_tutorial
    • 이 공식이 이해가 되지 않아 생각중입니다.
    deltaDistX = sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX))
    deltaDistY = sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY))
    
    • 음... 로데브를 읽다보니... 좌표를 완전 다르게 구현해야겠군요... 플레이어의 위치는 격자가 교차하는 곳을 좌표로 계산해서 소수로 표현해야겠습니다. 이런 망했어요.
    • 아 그리고 위 식은 이해를 했습니다. x가 1이면, 당연히 y는 y / x겠지요. (x, y)에서 x로 나누면 (1, y / x)
  • 텍스쳐 적용

    생각: 텍스쳐까지 그렸으면, 이제 스프라이트와 나머지 작업만 해주면 끝납니다. 하지만 스프라이트가 가장 어려우니, 나머지 파싱이나 비트맵 부분을 해도 됩니다. 우선 다시 슬랙과 자료조사를 해 봅시다.

    생각: 코드를 분할합시다.

    문제: 텍스터 작업을 해 봅시다. 그런데 텍스쳐가 제대로 나오지 않습니다.

    해결: 복사할 때 x = -1초기화를 y 반복문 안에 넣어주어야 하는데 그러지 않았습니다.

    문제: 텍스쳐를 입혀봅시다. 근데 계속 seg가 발생합니다.

    해결: x++이 아니라... ++x를 해야죠...

  • #cub3d #sprite - 1.5

    문제: 로데브 스트라이프를 다 이해하고 적용했는데 이상합니다. 코드를 적용할 때 무언가 실수한 것 같습니다.

    해결: 아직...


    문제: 파싱 도중에 다시 스프라이트로 오니 스프라이트가 그려지지 않습니다.

    해결: 파싱을 대충 해서, 맵의 col, row 값을 가져오지 못해 sprite의 개수가 0이여서 그렇습니다.

맵 파싱

  • 맵 파싱

    문제: 맵 파싱을 시작합시다. 야매로 get_next_line로 읽고 포인터를 이동해서 파싱이 제대로 되는지 해봅시다.

    해결: 야매로 대충 파싱은 성공했습니다. 이제 다듬기만 하면 됩니다.


    문제: 맵 파싱을 어떻게 해야 할까요?

    해결: get_next_line으로 한 줄씩 읽어오고, map은 list로 작업합니다. 하지만 조건들이 꽤나 많이 있습니다.

    조건1: 공백이 여러개 들어와도

  • 맵 유효성

  • 맵 해상도

    정보: 맵 해상도가 현재 해상도를 넘어가면 현재 해상도에 맞게 창을 그려야 하는데, 그 정보를 받아오는 함수가 mlx_get_screen_size입니다. 하지만 이 함수는 mlx_opengl에는 없고 mlx_mms에 있어서 결국 두 프로그램 모두 사용해야 합니다.

  • 맵 테스터

    https://github.com/humblEgo/cub3D_map_tester

    https://github.com/mlaraki/cub3D_leaks_maps_tester

기타

  • BMP 저장

    문제: —save옵션을 주고 비트맵으로 저장해봅시다. 그러면 어떻게 접근하는게 좋을까요?

    해결: https://dojang.io/mod/page/view.php?id=702이 링크를 보고 공부를 해 봅시다.


    문제: 파일을 어떻게 만들까요?

    해결: open과 close를 이용하여 파일을 만드는 법을 배웁시다.

    https://bubble-dev.tistory.com/entry/CC-open-함수-파일-생성-읽기-쓰기

    https://www.it-note.kr/19

    https://mong9data.tistory.com/111

    6.1 cub3d bitmap

    우선 bitmap을 만들기 위해서 open과 close 함수를 사용해서 파일을 만들고 작업해야 한다.

    open의 첫 번째 인자는 파일의 이름. 두 번째 인자는 fd설정, 마지막 인수는 파일의 권한이다.

    void	save(int *data)
    {
    	int		fd;
    
    	fd = open("save.bmp", O_WRONLY | O_TRUNC | O_APPEND | O_CREAT | O_EXCL
    	, S_IRWXU | S_IRWXG | S_IRWXO);
    	bitmap_file_header(fd);
    	bitmap__header(fd);
    	close(fd);
    }
  • mlx_mms적용

  • 에러 처리

    문제: ./cub3d map.cub와 같이 인자를 하나만 받아야 하며, .cub 확장자로 인자가 입력되어야 합니다. .cub가 아닌 경우를 예외처리 하려면 어떻게 해야 할까요?

    해결: ft_strncmp()를 사용합시다.

마무리