FdF(Fil de Fer)는 "철사"라는 뜻의 프랑스어로, 3D 와이어프레임 렌더링 프로그램을 만드는 프로젝트입니다. MiniLibX 그래픽 라이브러리를 사용하여 3차원 지형을 2D 화면에 투영합니다.
- 기초 컴퓨터 그래픽스 이해
- 3D → 2D 투영 구현
- 이벤트 처리 학습
- 최적화 기법 적용
- MiniLibX 라이브러리 사용
- 맵 파일(.fdf) 파싱
- 와이어프레임 모델 표시
- 3D → 2D 투영 구현
- 기본적인 에러 처리
0 0 0 0 0
0 1 2 1 0
0 2 4 2 0
0 1 2 1 0
0 0 0 0 0
- 각 숫자는 (x,y) 좌표의 높이(z) 값
- 공백으로 구분된 정수
- 모든 줄은 같은 개수의 숫자를 포함
# MacOS
brew install Xquartz
brew install minilibx
# Linux
sudo apt-get install gcc make xorg libxext-dev libbsd-dev
git clone https://github.com/your-username/fdf.git
cd fdf
make
./fdf maps/42.fdf
./fdf maps/pyramide.fdf
typedef struct s_point
{
int x;
int y;
int z;
int color;
} t_point;
typedef struct s_fdf
{
void *mlx;
void *win;
void *img;
char *addr;
int bits_per_pixel;
int line_length;
int endian;
t_point **map;
int width;
int height;
} t_fdf;
// 맵 파싱
t_point **parse_map(char *filename, t_fdf *fdf);
// 선 그리기
void draw_line(t_fdf *fdf, t_point start, t_point end);
// 이미지에 픽셀 찍기
void my_mlx_pixel_put(t_fdf *fdf, int x, int y, int color);
// 이벤트 처리
int handle_key(int key, t_fdf *fdf);
int handle_mouse(int button, int x, int y, t_fdf *fdf);
// Bresenham's Line Algorithm 구현
void draw_line(t_fdf *fdf, t_point start, t_point end)
{
int dx = abs(end.x - start.x);
int dy = abs(end.y - start.y);
int sx = start.x < end.x ? 1 : -1; // x 방향 결정
int sy = start.y < end.y ? 1 : -1; // y 방향 결정
int err = (dx > dy ? dx : -dy) / 2; // 초기 오차 값
while (1)
{
my_mlx_pixel_put(fdf, start.x, start.y, start.color);
if (start.x == end.x && start.y == end.y)
break;
int e2 = err;
if (e2 > -dx) { err -= dy; start.x += sx; } // x 좌표 업데이트
if (e2 < dy) { err += dx; start.y += sy; } // y 좌표 업데이트
}
}
// 등각 투영 변환 함수
void iso(int *x, int *y, int z)
{
int previous_x = *x;
int previous_y = *y;
// 30도 회전 (cos(30) ≈ 0.866, sin(30) ≈ 0.5)
*x = (previous_x - previous_y) * cos(0.523599); // 0.523599 rad = 30도
*y = -z + (previous_x + previous_y) * sin(0.523599);
}
// 에러 타입 정의
typedef enum e_error
{
E_ARGS, // 인자 개수 오류
E_FILE, // 파일 오류
E_MALLOC, // 메모리 할당 오류
E_MAP, // 맵 형식 오류
E_MLX // MLX 관련 오류
} t_error;
// 통합 에러 처리 함수
void handle_error(t_error error, t_fdf *fdf)
{
ft_putstr_fd("Error: ", 2);
if (error == E_ARGS)
ft_putstr_fd("Invalid number of arguments\n", 2);
else if (error == E_FILE)
ft_putstr_fd("Cannot open file\n", 2);
else if (error == E_MALLOC)
ft_putstr_fd("Memory allocation failed\n", 2);
else if (error == E_MAP)
ft_putstr_fd("Invalid map format\n", 2);
else if (error == E_MLX)
ft_putstr_fd("MLX error occurred\n", 2);
cleanup_fdf(fdf);
exit(1);
}
// 메모리 누수 방지를 위한 정리 함수
void cleanup_fdf(t_fdf *fdf)
{
if (!fdf)
return;
if (fdf->img)
mlx_destroy_image(fdf->mlx, fdf->img);
if (fdf->win)
mlx_destroy_window(fdf->mlx, fdf->win);
if (fdf->mlx)
{
mlx_destroy_display(fdf->mlx);
free(fdf->mlx);
}
free_map(fdf->map);
free(fdf);
}
// 이미지 버퍼 직접 조작
void my_mlx_pixel_put(t_fdf *fdf, int x, int y, int color)
{
char *dst;
if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT)
{
dst = fdf->addr + (y * fdf->line_length +
x * (fdf->bits_per_pixel / 8));
*(unsigned int*)dst = color;
}
}
// 더블 버퍼링
void render_frame(t_fdf *fdf)
{
draw_to_image(fdf);
mlx_put_image_to_window(fdf->mlx, fdf->win, fdf->img, 0, 0);
}
// 최적화 전/후 성능 비교
void performance_comparison(void)
{
// 1. 픽셀 그리기 최적화
// Before: mlx_pixel_put() - 약 100ms/1000픽셀
mlx_pixel_put(mlx, win, x, y, color);
// After: 직접 메모리 접근 - 약 1ms/1000픽셀
char *dst;
dst = data->addr + (y * data->line_length + x * (data->bits_per_pixel / 8));
*(unsigned int*)dst = color;
// 2. 뷰포트 컬링 최적화
// Before: 모든 점 처리 - O(n²)
for (all points)
draw_point();
// After: 화면 내 점만 처리 - O(visible_points)
if (is_in_viewport(point))
draw_point();
}
문제 | 원인 | 해결방안 |
---|---|---|
화면 깜빡임 | 단일 버퍼 사용 | 더블 버퍼링 구현 |
느린 렌더링 | mlx_pixel_put 사용 | 이미지 버퍼 직접 조작 |
부정확한 회전 | 부동소수점 오차 | 각도 보정 및 반올림 |
메모리 누수 | 부적절한 해제 | cleanup 함수 구현 |
맵 크기 | 렌더링 시간 | 메모리 사용량 |
---|---|---|
10x10 | ~1ms | ~1MB |
50x50 | ~5ms | ~2MB |
100x100 | ~15ms | ~4MB |
200x200 | ~50ms | ~8MB |
test_maps/42.fdf # 기본 테스트 맵
test_maps/pyra.fdf # 피라미드 형태
test_maps/elem.fdf # 기초 요소 테스트
test_maps/10-2.fdf # 10x10 간단한 맵
test_maps/20-60.fdf # 20x20 복잡한 맵
test_maps/50-4.fdf # 50x50 중간 크기
test_maps/100-6.fdf # 100x100 큰 맵
test_maps/basictest.fdf # 기본 기능 테스트
test_maps/elem-col.fdf # 컬러 테스트
test_maps/elem-fract.fdf # 소수점 테스트
test_maps/pentenegpos.fdf # 양수/음수 혼합
test_maps/plat.fdf # 평면 테스트
- 맵 파일 파싱
- 와이어프레임 그리기
- 투영 변환
- 기본 에러 처리
- 메모리 누수 없음
- 마우스 컨트롤
- 추가 투영법
- 색상 그라데이션
- 회전/이동/확대축소
- 더블 버퍼링 구현
- 이미지 버퍼 직접 조작
- 뷰포트 컬링 적용
- 성능 테스트 통과
- 메모리 누수 체크
valgrind --leak-check=full ./fdf maps/42.fdf
- Norminette 검사
norminette *.c *.h
- 에러 케이스 테스트
./fdf # 인자 없음
./fdf invalid.fdf # 잘못된 파일
./fdf maps/empty.fdf # 빈 파일