TrendShift 딥다이브 · 2026-07-02

Box3D 딥다이브
— Box2D를 만든 사람이 20년 만에 내놓은 3D 게임 물리 엔진

Box3D는 수많은 게임의 2D 물리를 책임져 온 전설적 오픈소스 엔진 Box2D의 창시자 Erin Catto(에린 카토)가 만든 3차원 강체(rigid body) 물리 엔진이다. "물리 엔진"이란 게임 속 물체가 중력에 떨어지고, 부딪혀 튕기고, 경첩처럼 회전하는 걸 대신 계산해 주는 라이브러리다. 핵심 라이브러리는 순수 C17로 작성되었고 외부 의존성이 전혀 없다(C 런타임과 libm만 필요). 데이터 지향 설계·SIMD·멀티스레딩·크로스플랫폼 결정론을 정면에 내세운다. (저장소: erincatto/box3d · MIT 라이선스 · 최초 릴리스 v0.1.0 (2026-06-30, 알파) · TrendShift Daily 9위)
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

이 저장소가 대체 무엇인가.

핵심 메시지

"게임 세계의 물리 법칙 계산기" — 물체의 위치·회전·충돌·관절을 매 프레임 대신 풀어 주는 엔진, 그 3D 버전이다.

게임에서 상자가 바닥에 떨어져 구르고, 캐릭터가 벽에 부딪혀 멈추고, 자동차 바퀴가 서스펜션에 눌리는 장면을 떠올려 보자. 이 모든 움직임 뒤에는 매 순간 "이 물체가 다음 1/60초 동안 어디로, 얼마나 회전하며 움직여야 하는가"를 계산하는 수학 엔진이 있다. 그 계산을 전담하는 라이브러리가 물리 엔진이고, Box3D는 그 3차원 버전이다.

비유하면: 게임 개발자 = 무대 연출가, 물리 엔진 = 무대 위 모든 소품이 중력·마찰·충돌의 법칙을 따르게 만드는 보이지 않는 스태프. 연출가는 "상자를 여기 둬"라고 배치만 하면, 그다음 상자가 굴러 떨어지는 사실적인 움직임은 Box3D가 뉴턴 역학을 수치적으로 풀어서 만들어 낸다.

README의 자기소개는 딱 한 문장이다: "Box3D is a 3D physics engine for games." 하지만 이 담백한 한 줄 뒤에는 중요한 맥락이 있다. 저자 Erin Catto는 2007년부터 Box2D를 만들어 온 사람으로, Box2D는 앵그리버드류 물리 퍼즐부터 수많은 인디·상용 게임 엔진의 2D 물리 기반이 된 업계 표준급 오픈소스다. 그가 오랜 GDC(게임 개발자 컨퍼런스) 강연과 연구로 다듬은 물리 솔버 기법을 이제 3차원으로 확장한 것이 Box3D다.

실제로 저장소를 뜯어보면, 이것은 "장난감 예제"가 아니라 87개의 C 소스 파일로 이루어진 본격 엔진이다. 충돌 감지(broad/narrow phase), 강체 솔버, 8종의 관절(joint), 볼록 껍질(convex hull)·캡슐·구·삼각형 메시·높이 필드 충돌, 광선/도형 캐스트, 센서, 캐릭터 무버, 그리고 녹화·재생(recording/replay)과 크로스플랫폼 결정론까지 들어 있다. 핵심 라이브러리(box3d::box3d)는 C 런타임 외에 어떤 외부 라이브러리에도 의존하지 않는다 — 게임 엔진에 그대로 끼워 넣기 좋다는 뜻이다.

용어
강체(Rigid Body)
물리 시뮬레이션에서 "모양이 변하지 않는 단단한 물체"를 뜻한다. 상자·구슬·자동차 차체처럼 눌러도 찌그러지지 않는다고 가정하는 물체다. 반대로 천·젤리·물처럼 변형되는 건 "소프트 바디" 또는 "유체"라 부르며 별도 기법이 필요하다. Box3D는 (대부분의 게임 물리 엔진처럼) 강체 시뮬레이션에 집중한다 — 게임 대부분의 물체는 강체로 충분하기 때문이다.

2왜 주목받는가

Box2D 창시자의 3D 후속작 · 갓 나온 v0.1.0 · "Soft Step" 솔버 · 결정론 · 무의존성 C17.

Box3D가 공개 직후 TrendShift 상위권에 오른 이유는 단순한 "새 물리 엔진"이어서가 아니다. Erin Catto라는 이름값과, 그가 십수 년간 발표해 온 솔버 연구가 마침내 3D로 결실을 맺었다는 서사 때문이다. 최초 릴리스 v0.1.02026-06-30에 올라왔다 — 즉 이 문서를 쓰는 시점 기준 갓 며칠 된 따끈한 알파 소프트웨어다.

경쟁 3D 물리 엔진과 무엇이 다른가

비교 축기존 대표 엔진들Box3D의 접근
계보Bullet(범용), PhysX(NVIDIA), Jolt(Horizon 게임 출신)업계 표준 Box2D의 정통 3D 후속 — API 철학·솔버 계보가 이어짐
언어대개 C++ (클래스·상속·템플릿)순수 C17 + 불투명 핸들 API — ABI 안정성·바인딩 용이
의존성서드파티 수학/컨테이너 라이브러리 동반이 흔함코어는 C 런타임 + libm뿐. 자체 컨테이너·할당자·해시테이블 내장
솔버전통적 순차 임펄스(Sequential Impulse) 계열Catto의 "Soft Step" 소프트 제약 솔버 — 서브스텝 기반 안정성
결정론플랫폼마다 부동소수점 결과가 미묘하게 달라지기 쉬움크로스플랫폼 결정론을 명시적 설계 목표로 (-ffp-contract=off 등)
재현성별도 도구 필요녹화·재생(recording/replay) 기능이 엔진에 내장 (recording_replay.c 116KB)
주목 포인트 — 데이터 지향 설계를 정면에 내세운 물리 엔진
"Data-oriented design, portable C17, extensive multithreading and SIMD"

README의 System 항목은 이 엔진의 성격을 압축한다. 객체 하나하나를 클래스 인스턴스로 다루는 전통적 OOP 대신, 같은 종류 데이터를 배열로 촘촘히 모아 CPU 캐시 적중률과 SIMD 벡터화를 극대화하는 데이터 지향 설계(DOD)를 택했다. "많은 물체 더미(large piles of bodies)에 최적화"라는 문구가 이를 뒷받침한다. 물리 엔진은 성능이 곧 생명인 분야라, 이 설계 선택 자체가 훌륭한 학습 교재가 된다.

기대치 조정 — 아직 v0.1.0 알파다
"I consider this to be alpha software."

릴리스 노트에서 저자 스스로 알파임을 명시한다. API는 앞으로 바뀔 수 있고, 문서(docs/)도 채워지는 중이며, Pull Request는 현재 막혀 있다(버그·기능 요청은 이슈로만 접수). 프로덕션 게임에 바로 넣기보다는, "최신 물리 엔진 아키텍처를 최고 수준 저자의 코드로 배우는 교재"이자 "장기적으로 지켜볼 프로젝트"로 접근하는 것이 현실적이다.

인상적인 정직성 — LLM 사용 범위를 README에 공개
"LLM Usage" 섹션

README 하단에는 흔치 않은 항목이 있다. 저자는 유닛 테스트·샘플 앱·Box2D↔Box3D 코드 이식·빌드 설정·코드 리뷰·벤치마킹에 LLM을 사용했고, 그 외 모든 코드는 자신이 직접 작성했으며 "모든 코드 한 줄 한 줄에 책임진다"고 밝힌다. AI 도구 사용이 일상이 된 시대에 오픈소스 저자가 사용 범위를 투명하게 밝힌 좋은 선례다.

3기술 스택 전체 지도

C17 코어 라이브러리 + C++20 샘플 앱 + CMake 빌드 + SIMD + 크로스플랫폼 그래픽/디버깅 도구.

Box3D는 "엔진 코어"와 "그것을 눈으로 보여 주는 샘플 앱"을 명확히 분리한다. 코어는 그래픽이 전혀 없는 순수 계산 라이브러리이고, 샘플 앱은 그 결과를 3D로 그려 주는 별도 프로그램이다. 이 분리 덕분에 코어는 어떤 렌더러(Unity·Unreal·자작 엔진)에도 붙일 수 있다.

레이어기술 / 버전역할
코어 언어C17 (ISO C 2018)물리 엔진 본체. 클래스 대신 함수 + 불투명 핸들. ABI가 안정적이라 어떤 언어에서든 바인딩하기 쉬움
샘플 언어C++20데모/테스트용 앱. 코어를 링크해 실제로 굴려 보는 놀이터
빌드 시스템CMake ≥ 3.22 + CMakePresetsWindows/Linux/macOS/Xcode/Emscripten 한 벌의 설정으로 관리
SIMD 수학SSE2(x86) / Neon(ARM)벡터·행렬 연산을 한 번에 4개씩 처리. BOX3D_DISABLE_SIMD로 끌 수 있음
멀티스레딩사용자 제공 태스크 시스템 (콜백 기반)엔진이 스레드를 직접 만들지 않고, 게임의 잡 시스템에 일을 넘길 수 있게 훅만 제공
샘플 그래픽sokol (D3D11 / Metal / OpenGL 4.5)OS별 그래픽 API를 하나로 감싼 헤더 전용 라이브러리. Win=D3D11, mac=Metal, Linux=GL
샘플 UIDear ImGui즉시 모드 GUI. 파라미터 슬라이더·디버그 패널을 즉석에서 그림
WebAssemblyEmscripten (pthread + 메모리 성장)브라우저에서 돌아가는 WASM 빌드도 CMake에 준비되어 있음
프로파일링Tracy (BOX3D_PROFILE)프레임별 성능을 실시간 타임라인으로 뜯어보는 프로파일러(옵션)
문서Doxygen (BOX3D_DOCS)헤더 주석에서 API 레퍼런스를 자동 생성
디버깅 보조.natvis + SanitizerVisual Studio에서 엔진 자료구조를 예쁘게 표시. ASan/TSan/UBSan/MSan 옵션 내장
테스트/벤치자체 유닛 테스트 + benchmark/내부 함수까지 검증하려 테스트는 정적 링크로 빌드
용어
불투명 핸들 API (Opaque Handle API)
C++ 라이브러리는 보통 World world;처럼 구조체 내부를 노출하지만, Box3D는 b3WorldId·b3BodyId·b3ShapeId처럼 내부가 숨겨진 작은 정수 표(ID)만 건네준다. 사용자는 그 ID로 함수를 호출할 뿐, 물체의 실제 메모리 배치는 모른다. 장점: (1) 엔진이 내부 자료구조(예: 배열 재배치)를 자유롭게 바꿔도 사용자 코드가 안 깨진다, (2) 다른 언어(Python·Rust·C#)에서 바인딩하기 쉽다, (3) 잘못된 포인터 접근 위험이 준다. 데이터 지향 설계와 궁합이 좋은 선택이다.
용어
SIMD (Single Instruction, Multiple Data)
CPU가 "덧셈 하나"를 "숫자 4개(또는 8개)에 동시에" 적용하는 명령어 집합이다. 물리 엔진은 수천 개 물체의 좌표를 똑같은 방식으로 계산하므로, 4개씩 묶어 한 번에 처리하면 크게 빨라진다. x86에서는 SSE2, ARM(애플 실리콘·모바일)에서는 Neon을 쓴다. Box3D는 simd.c/simd.h에서 이 둘을 추상화하고, 필요하면 BOX3D_DISABLE_SIMD로 꺼서 순수 스칼라 코드로 되돌릴 수 있다(디버깅·이식용).

4아키텍처 심화 분석

한 번의 b3World_Step 안에서 벌어지는 일 — 브로드페이즈 → 내로우페이즈 → 제약 그래프 → Soft Step 솔버 → 적분.

물리 엔진의 심장은 b3World_Step(worldId, timeStep, subStepCount) 함수 하나다. 게임이 매 프레임 이 함수를 부르면, 엔진은 "지난 1/60초 동안 세계가 어떻게 변했는가"를 통째로 계산한다. 그 내부는 대략 이런 파이프라인으로 흐른다.

b3World_Step( dt = 1/60, subStepCount = 4 ) ──────────────────────────────────────────────────────────── ① BROAD PHASE (broad_phase.c + dynamic_tree.c) "누가 누구랑 부딪힐 가능성이 있나?" 를 AABB 트리로 후보만 추림 수천 개 물체를 O(n^2) 없이 빠르게 → 충돌 쌍(pair) 후보 목록 │ ▼ ② NARROW PHASE (distance.c=GJK, convex_manifold.c, mesh_contact.c ...) 후보 쌍을 정밀 검사 → 실제 겹침 지점·깊이·법선 = "접촉 매니폴드" │ ▼ ③ CONSTRAINT GRAPH (constraint_graph.c) 접촉·관절을 "동시에 풀어도 안전한 색깔 그룹"으로 그래프 색칠 → 스레드들이 서로 안 겹치는 그룹을 병렬로 처리 (락 없는 병렬) │ ▼ ④ SOLVER : SOFT STEP (solver.c + contact_solver.c + *_joint.c) dt 를 subStepCount(=4) 로 쪼갠 서브스텝 반복: for k in 1..4: (내부적으로 240Hz) 속도 적분 → 제약(접촉·관절) 풀이 → 위치 보정(relax) "소프트 제약" = 딱딱한 벽 대신 아주 강한 스프링처럼 다뤄 안정적 │ ▼ ⑤ INTEGRATE & SLEEP (island.c + solver_set.c + body.c) 최종 속도로 위치·회전(쿼터니언) 갱신 움직임이 멎은 물체 무리(=island)는 "잠재움" → 다음 스텝 계산 스킵 │ ▼ (옵션) RECORDING (recording.c / recording_replay.c / world_snapshot.c) 매 스텝 입력을 기록 → 나중에 100% 동일하게 재생 (결정론 전제)

① 브로드페이즈 — "부딪힐 리 없는 쌍"을 먼저 걸러낸다

물체가 n개면 충돌 검사 조합은 순진하게 하면 이다. 1,000개면 50만 번. 이걸 피하려고 dynamic_tree.c(무려 54KB)가 AABB 트리(각 물체를 감싸는 축 정렬 상자를 계층 트리로 관리)를 유지한다. 두 물체의 감싸는 상자조차 안 겹치면 정밀 검사를 아예 건너뛴다. 이 단계에서 살아남은 소수의 "충돌 후보 쌍"만 다음 단계로 넘어간다.

용어
AABB (Axis-Aligned Bounding Box, 축 정렬 경계 상자)
복잡한 모양의 물체를 "그것을 완전히 감싸는, 축에 반듯하게 정렬된 직육면체"로 근사한 것. 상자끼리 겹치는지는 각 축의 최소/최대값만 비교하면 되어 아주 싸다. 실제 모양이 겹치는지는 나중에(내로우페이즈) 정밀 검사하고, AABB는 "겹칠 가능성"만 빠르게 판정하는 1차 필터다.

② 내로우페이즈 — GJK로 정밀 접촉을 계산한다

후보 쌍이 정해지면 실제로 얼마나, 어디서 겹치는지 계산한다. 볼록 도형 사이 거리는 distance.c(47KB)의 GJK 알고리즘이 담당하고, 겹침이 확인되면 convex_manifold.c(50KB)·triangle_manifold.c·mesh_contact.c 등이 접촉 매니폴드(접촉점 좌표 + 파고든 깊이 + 밀어낼 법선 방향)를 만든다. 도형 종류별(구·캡슐·볼록껍질·삼각형 메시·높이 필드) 전용 코드가 나뉘어 있다.

용어
GJK 알고리즘 / 접촉 매니폴드(Contact Manifold)
GJK(Gilbert–Johnson–Keerthi)는 두 볼록 도형 사이 최단 거리를 반복적으로 좁혀 구하는 고전 알고리즘으로, "겹쳤는가/얼마나 떨어졌는가"를 효율적으로 답한다. 접촉 매니폴드는 그 결과를 솔버가 쓸 수 있게 정리한 것 — 두 물체가 맞닿은 지점들, 파고든 깊이, 밀어내야 할 방향(법선)을 담는다. 이 매니폴드가 다음 단계 솔버의 "입력 문제"가 된다.

③ 제약 그래프 색칠 — 락 없이 병렬로 푸는 비결

여러 스레드가 동시에 물리를 풀 때 위험한 건, 두 스레드가 같은 물체를 동시에 수정하는 경우다(데이터 레이스). constraint_graph.c는 이를 그래프 색칠(graph coloring)로 막는다. 접촉·관절을 노드로 보고, "같은 물체를 공유하는 제약끼리는 다른 색"이 되도록 칠한다. 그러면 같은 색 그룹 안의 제약들은 절대 같은 물체를 건드리지 않으므로, 스레드들이 락 없이 병렬로 처리해도 안전하다. 색깔을 하나씩 순서대로 처리하되, 각 색 안에서는 마음껏 병렬화하는 것이다.

이런 상황을 상상해봐

식당 주방에서 요리사 여러 명이 동시에 일한다. 두 사람이 같은 도마를 동시에 쓰면 사고가 난다. 그래서 "같은 도마를 쓰는 주문은 서로 다른 시간대(색)에 배정"하고, 같은 시간대에는 서로 다른 도마를 쓰는 주문만 몰아서 동시에 처리한다. 제약 그래프 색칠이 바로 이 스케줄링이다 — 충돌(도마 공유) 없는 작업만 묶어 병렬로 돌린다.

④ 솔버: "Soft Step" — 딱딱한 벽을 아주 강한 스프링처럼

이 엔진의 이름값 있는 부분이다. 전통적 솔버는 "물체가 절대 겹치면 안 된다"는 딱딱한(hard) 제약을 강제하다가 펄쩍 튀거나 부들부들 떠는(jitter) 불안정을 겪곤 한다. Erin Catto가 GDC 강연들에서 다듬어 온 Soft Step(소프트 제약) 접근은, 제약을 "아주 뻣뻣하지만 감쇠가 있는 스프링"으로 모델링해 수치적으로 훨씬 안정적으로 만든다. 그리고 한 스텝(1/60초)을 subStepCount(기본 4)개의 서브스텝으로 잘게 나눠 반복한다 — 60Hz 겉보기로 돌리되 내부적으로는 240Hz로 제약을 여러 번 다잡는 셈이다.

// docs/hello.md 원문 — 서브스텝의 의미를 저자가 직접 설명
float timeStep = 1.0f / 60.0f;   // 프레임레이트에 묶지 말 것 (고정 스텝 권장)
int   subStepCount = 4;          // 4로 나누면 제약이 240Hz로 풀림
b3World_Step(worldId, timeStep, subStepCount);  // 매 틱 1회 호출
// "More sub-steps improve accuracy at the cost of performance."
용어
서브스텝(Sub-step) & 솔버(Solver)
솔버는 "이 많은 접촉·관절 제약을 동시에 만족시키는 속도를 찾아라"는 연립 문제를 반복 근사로 푸는 부분이다. 서브스텝은 한 프레임의 시간(dt)을 더 잘게 쪼개 이 풀이를 여러 번 하는 것 — 큰 걸음 한 번보다 작은 걸음 여러 번이 훨씬 정확하고 안정적이다. Box3D는 dt를 subStepCount로 나눠, 각 서브스텝마다 속도 적분 → 제약 풀이 → 위치 보정을 반복한다.

⑤ 아일랜드 & 잠재우기 — 멈춘 물체는 계산하지 않는다

서로 접촉·관절로 연결된 물체 무리를 아일랜드(island)라 부른다(island.c). 한 아일랜드의 모든 물체가 충분히 오래 거의 안 움직이면, 엔진은 그 무리를 "잠재워(sleep)" 다음 스텝부터 계산에서 빼 버린다. 바닥에 쌓여 정지한 상자 100개를 매 프레임 다시 푸는 건 낭비이기 때문이다. 새 충돌이 들어오면 그 아일랜드만 다시 깨운다. 이것이 "많은 물체 더미에 최적화"의 핵심 비결 중 하나다.

⑥ 결정론 & 녹화·재생 — 어디서 돌려도 똑같은 결과

Box3D는 크로스플랫폼 결정론을 설계 목표로 못 박았다. 같은 입력을 주면 Windows·Linux·macOS에서 비트 단위로 동일한 시뮬레이션 결과가 나오도록 하는 것이다. 이를 위해 CMake에서 -ffp-contract=off(부동소수점 곱셈-덧셈 융합 최적화 차단)를 켜 컴파일러가 결과를 미묘하게 바꾸는 걸 막는다. 그 위에 recording_replay.c(116KB)·world_snapshot.c가 얹혀, 시뮬레이션을 녹화했다가 나중에 똑같이 재생할 수 있다.

왜 결정론이 중요한가
멀티플레이어 · 리플레이 · 버그 재현

결정론이 보장되면 온라인 게임에서 각 클라이언트가 물리를 따로 돌려도 결과가 일치해(락스텝) 네트워크로 위치 전체를 안 보내도 된다. 또 e스포츠 리플레이를 "입력만 저장 → 재생"으로 가볍게 만들 수 있고, 물리 버그가 정확히 재현되어 디버깅이 쉬워진다. 결정론은 물리 엔진에서 "있으면 좋은" 게 아니라 특정 장르에선 필수 인프라다.

5디렉토리 구조 해부

최상위 레이아웃 + src/ 87개 소스 파일을 역할별로 묶어서.

box3d/ ├── include/box3d/ # 공개 API 헤더 (사용자가 보는 유일한 부분) │ ├── box3d.h # 메인 API — 세계/물체/도형/관절 함수 전부 (79KB) │ ├── types.h # b3WorldDef, b3BodyDef 등 모든 설정 구조체 (90KB) │ ├── collision.h # 충돌 질의(레이/도형 캐스트, 매니폴드) API │ ├── math_functions.h # b3Vec3, b3Quat, 행렬 등 수학 (32KB) │ ├── id.h # 불투명 핸들 타입 (b3WorldId, b3BodyId ...) │ ├── base.h · constants.h · config.h ├── src/ # 엔진 본체 (87개 C 파일 — 아래 역할별 분류) ├── samples/ # sokol+imgui 데모 앱 (C++20) — 눈으로 보는 놀이터 ├── shared/ # 샘플·테스트·벤치가 공유하는 코드 ├── test/ # 유닛 테스트 (hello world 포함, 정적 링크) ├── benchmark/ # 성능 벤치마크 (BOX3D_BENCHMARKS 옵션) ├── docs/ # Doxygen 사용자 매뉴얼 (hello.md 등) ├── extern/ # 외부 의존성 (샘플용 — 코어는 무의존) ├── data/ # 샘플이 쓰는 에셋/데이터 ├── CMakeLists.txt # 루트 빌드 정의 ├── CMakePresets.json # windows/linux/macos 프리셋 ├── build.sh · build_vs2026.bat # 원터치 빌드 스크립트 └── README.md · LICENSE(MIT) · CONTRIBUTING.md

진짜 알맹이인 src/의 87개 파일을 역할별로 묶으면 물리 엔진의 해부도가 보인다.

src/ (역할별 그룹) │ ├─ [세계·시뮬레이션 코어] │ physics_world.c (130KB ★최대) · solver.c (79KB) · solver_set.c │ island.c (아일랜드/잠재우기) · body.c (67KB) · shape.c (72KB) │ ├─ [충돌 — 브로드페이즈] │ broad_phase.c · dynamic_tree.c (54KB, AABB 트리) · aabb.c │ ├─ [충돌 — 내로우페이즈 / 도형] │ distance.c (47KB, GJK) · hull.c (72KB, 볼록껍질) · mesh.c (65KB) │ height_field.c (45KB) · compound.c (35KB) · capsule.c · sphere.c │ manifold.c · convex_manifold.c (50KB) · triangle_manifold.c │ mesh_contact.c · contact.c · contact_solver.c (78KB ★제약 솔버) │ ├─ [관절 8종] joint.c (49KB) 아래에: │ revolute(경첩)·prismatic(미끄럼)·distance(거리)·weld(용접) │ wheel(바퀴)·motor(모터)·spherical(구관절)·parallel_joint │ ├─ [병렬화] constraint_graph.c (그래프 색칠) · scheduler.c · parallel_for.c │ ├─ [결정론·재현] recording.c (41KB) · recording_replay.c (116KB ★) │ world_snapshot.c (38KB) · recording_ops.inl │ ├─ [메모리·자료구조] arena_allocator.c · block_allocator.c · id_pool.c │ table.c · verstable.h (해시테이블) · bitset.c · container.h │ ├─ [수학·SIMD] math_functions.c · simd.c/simd.h · math_internal.h │ └─ [센서·무버·기타] sensor.c · mover.c (캐릭터) · timer.c · box3d.natvis
한눈에 읽는 법

파일 크기가 곧 "복잡도 지도"다. physics_world.c(130KB)가 오케스트라 지휘자, contact_solver.c(78KB)·solver.c(79KB)가 안정성을 책임지는 심장, hull.c·mesh.c·distance.c가 "어디서 부딪혔나"를 재는 측량 팀, recording_replay.c(116KB)가 결정론을 지키는 블랙박스다. 처음 읽는다면 docs/hello.mdinclude/box3d/box3d.h(공개 API) → samples/ 순서로, 내부 구현(src)은 필요할 때만 파고드는 게 좋다.

6학습 포인트 (기술별)

이 저장소에서 실제로 무엇을 배울 수 있는가 + 각 주제 실습 아이디어.

학습 1 · 데이터 지향 설계(DOD)

"객체 배열이 아니라 배열의 배열로 생각하기"

Box3D는 물체를 Body 객체 하나하나로 흩어 두지 않고, 같은 속성끼리 배열로 모아 캐시 친화적으로 배치한다. solver_set.c·arena_allocator.c를 읽으며 "왜 OOP 대신 이렇게 짰는가"를 따라가면 성능 지향 아키텍처의 사고방식을 얻는다.

실습: 같은 물체 1만 개를 (a) 객체 배열, (b) 속성별 배열(SoA)로 각각 순회하며 시간을 재 캐시 효과를 체감해 보기.

학습 2 · 불투명 핸들 C API 설계

"라이브러리 API를 어떻게 안정적으로 설계하는가"

include/box3d/만 봐도 "사용자에게 무엇을 숨기고 무엇을 보여줄지"에 대한 모범 답안이 보인다. Def 구조체 + b3Default***Def() 패턴은 "C에는 생성자가 없다"는 제약을 우아하게 푸는 방법이다.

실습: 내 작은 C 라이브러리(예: 간단한 파일 파서)를 불투명 핸들 + Default-Def 패턴으로 리팩터링해 보기.

학습 3 · 물리 솔버 & 수치 안정성

"제약을 스프링으로 보는 Soft Step 접근"

solver.c·contact_solver.c와 Erin Catto의 공개 GDC 슬라이드를 함께 보면, "왜 딱딱한 제약이 튀는가"와 "서브스텝·소프트 제약이 왜 안정적인가"를 이론+코드로 배울 수 있다. 게임 물리를 넘어 로보틱스·시뮬레이션에도 통하는 지식이다.

실습: 스프링-질량 하나를 오일러 적분 vs 세미-임플리싯 적분으로 각각 구현해, 큰 스텝에서 어느 쪽이 폭발하는지 관찰.

학습 4 · 공간 자료구조 (AABB 트리)

"n² 충돌 검사를 n log n으로"

dynamic_tree.c는 게임·GIS·레이트레이싱에 두루 쓰이는 동적 AABB 트리(BVH)의 실전 구현이다. 삽입·삭제·재균형·질의를 어떻게 빠르게 유지하는지가 다 들어 있다.

실습: 2D 점 1만 개에 대한 간단한 AABB 트리를 만들어, "반경 안의 이웃 찾기"를 브루트포스와 속도 비교.

학습 5 · 병렬 프로그래밍 (그래프 색칠)

"락 없이 병렬화하는 실전 패턴"

constraint_graph.c + parallel_for.c는 "데이터 레이스를 자료구조 수준에서 원천 차단"하는 고급 패턴을 보여준다. 뮤텍스로 막는 대신, 애초에 충돌하지 않는 작업만 묶어 병렬로 돌린다.

실습: 그래프 색칠로 스레드에 작업을 배분하는 미니 스케줄러를 만들어, 뮤텍스 버전과 성능 비교.

학습 6 · 크로스플랫폼 결정론

"부동소수점은 왜 플랫폼마다 다른가"

-ffp-contract=off 한 줄의 의미를 파고들면 FMA(융합 곱셈-덧셈), 컴파일러 재배치, x87 vs SSE 정밀도 같은 부동소수점의 함정을 배운다. 네트워크 게임·리플레이를 만들려면 반드시 아는 지식이다.

실습: 같은 부동소수점 누적 연산을 최적화 플래그를 바꿔 컴파일해, 결과가 달라지는지 확인.

학습 7 · 크로스플랫폼 그래픽 (샘플 앱)

"sokol + imgui로 D3D11/Metal/GL을 한 코드로"

samples/는 헤더 전용 라이브러리 sokol로 세 OS의 그래픽 API를 하나로 감싸고, Dear ImGui로 디버그 UI를 즉석에서 그린다. "렌더러와 물리 코어를 어떻게 깔끔히 분리하는가"의 좋은 예다.

실습: 샘플 하나를 골라 파라미터(중력·마찰·서브스텝)를 ImGui 슬라이더로 바꿔 가며 거동 변화를 관찰.

7하드웨어 / 시스템 요구사항

코어를 빌드하려면 무엇이 필요한가.

항목요구사항
코어 컴파일러C17 지원 컴파일러 (MSVC, GCC, Clang)
샘플 컴파일러C++20 지원 컴파일러 (샘플 앱을 빌드할 때만)
빌드 도구CMake ≥ 3.22 + git (명령줄에서 실행 가능해야)
운영체제Windows · Linux · macOS 모두 빌드/실행 (샘플 포함)
CPU 명령어SSE2(x86) 또는 Neon(ARM) — BOX3D_DISABLE_SIMD로 끌 수 있음
그래픽(샘플)Windows=D3D11 · macOS=Metal · Linux=OpenGL 4.5
정밀도 옵션기본 float(단정밀도). 큰 세계엔 BOX3D_DOUBLE_PRECISION(double)
추가 도구(옵션)Doxygen(문서), Tracy(프로파일), Emscripten(WASM), sokol-shdc(셰이더)
라이선스MIT — 상용 게임에도 자유롭게 사용 가능
용어
단정밀도(float) vs 배정밀도(double) & MKS 단위
Box3D는 기본적으로 32비트 float로 계산한다 — 빠르고 대부분 게임에 충분하다. 다만 아주 넓은 오픈월드(좌표가 커질수록 float 오차가 커짐)에서는 BOX3D_DOUBLE_PRECISION으로 64비트 double을 켤 수 있다. 또 엔진은 미터·킬로그램·초(MKS) 단위에 맞춰 튜닝되어 있어, "1m 크기 물체"처럼 현실 규모로 다뤄야 안정적이다(빙하나 먼지 크기는 float 한계를 벗어난다고 문서가 경고한다).

8직접 해볼 수 있는 실습 과제

난이도별 5단계 — 빌드하고, 굴려 보고, 붙여 쓰고, 뜯어보기.

과제 1 · Hello Box3D 돌려보기 난이도 ★☆☆☆☆

공식 docs/hello.md의 최소 예제를 그대로 따라 해 본다. 그래픽 없이 콘솔에 상자가 y=4에서 떨어져 y≈1에 멈추는 좌표가 찍힌다. 물리 엔진 사용의 전체 뼈대(세계 생성 → 물체·도형 붙이기 → 스텝 루프 → 정리)를 30줄로 체득하는 것이 목표.

// 핵심 골격 (docs/hello.md 요약, C)
#include <box3d/box3d.h>
#include <stdio.h>

int main(void) {
    b3WorldDef worldDef = b3DefaultWorldDef();      // 기본값 채우기
    worldDef.gravity = (b3Vec3){ 0.0f, -10.0f, 0.0f };
    b3WorldId world = b3CreateWorld(&worldDef);

    // 바닥(정적 물체)
    b3BodyDef groundDef = b3DefaultBodyDef();
    groundDef.position = (b3Vec3){ 0.0f, -10.0f, 0.0f };
    b3BodyId ground = b3CreateBody(world, &groundDef);
    b3BoxHull groundBox = b3MakeBoxHull(50.0f, 10.0f, 50.0f);   // 절반 크기(half-extent)
    b3ShapeDef gsd = b3DefaultShapeDef();
    b3CreateHullShape(ground, &gsd, &groundBox.base);

    // 떨어지는 상자(동적 물체)
    b3BodyDef boxDef = b3DefaultBodyDef();
    boxDef.type = b3_dynamicBody;                   // 안 켜면 안 떨어진다!
    boxDef.position = (b3Vec3){ 0.0f, 4.0f, 0.0f };
    b3BodyId body = b3CreateBody(world, &boxDef);
    b3BoxHull cube = b3MakeCubeHull(1.0f);
    b3ShapeDef sd = b3DefaultShapeDef();
    sd.density = 1.0f; sd.baseMaterial.friction = 0.3f;   // 밀도 0이면 안 됨
    b3CreateHullShape(body, &sd, &cube.base);

    for (int i = 0; i < 90; ++i) {
        b3World_Step(world, 1.0f/60.0f, 4);          // dt, 서브스텝 4
        b3Vec3 p = b3Body_GetPosition(body);
        printf("%4.2f %4.2f %4.2f\n", p.x, p.y, p.z);
    }
    b3DestroyWorld(world);                           // 전부 한 번에 해제
    return 0;
}

과제 2 · 프리셋으로 샘플 앱 빌드·실행 난이도 ★★☆☆☆

CMake 프리셋으로 실제 3D 샘플을 띄워 본다. imgui 패널로 데모를 바꿔 가며 물리 거동을 눈으로 관찰하는 것이 목표.

# 리눅스 예시 (README 기준)
cmake --preset linux-release
cmake --build --preset linux-release
./build/bin/samples
# Windows: cmake --preset windows → build_vs2026.bat 도 가능
# macOS:   cmake --preset macos

과제 3 · 관절(joint)로 진자·자동차 만들기 난이도 ★★★☆☆

과제 1 코드에 revolute joint(경첩)로 진자를, wheel joint로 간단한 서스펜션 자동차를 붙여 본다. include/box3d/box3d.h의 관절 API와 samples/의 관절 데모를 참고하면 된다. 관절 8종의 차이를 직접 손으로 익히는 것이 목표.

과제 4 · 내 프로젝트에 FetchContent로 통합 난이도 ★★★☆☆

README가 권장하는 방식으로, 빈 CMake 프로젝트를 만들고 Box3D를 FetchContent로 끌어와 링크해 본다. "무의존 코어를 남의 프로젝트에 끼우는" 실전 경험이다.

include(FetchContent)
FetchContent_Declare(box3d
  GIT_REPOSITORY https://github.com/erincatto/box3d.git
  GIT_TAG v0.1.0)
FetchContent_MakeAvailable(box3d)
target_link_libraries(my_app PRIVATE box3d::box3d)

과제 5 · 내부 뜯어보기 — 잠재우기(sleep) 관찰 난이도 ★★★★★

상자 수백 개를 쌓아 놓고, 정지 후 어느 시점에 아일랜드가 "잠드는지"를 island.c에 로그를 심어 관찰한다. 나아가 BOX3D_PROFILE(Tracy)을 켜 잠든 물체가 스텝 시간에서 빠지는 걸 프로파일러로 확인한다. 엔진 최적화의 핵심을 코드 레벨에서 이해하는 것이 목표.

9관련 기술 심화 학습 로드맵

물리 엔진을 "쓰는 사람"에서 "이해하는 사람"으로 — 6주 계획.

주차주제할 일
1주차C 기초 다지기 & 빌드 환경C17 문법, 포인터/구조체, CMake 기본. 과제 1·2 완주. 불투명 핸들 API 감 잡기
2주차벡터·쿼터니언 수학3D 회전을 쿼터니언으로 다루는 법. math_functions.h 읽기. 오일러각과의 차이 이해
3주차강체 역학 & 적분속도·힘·토크·관성 텐서. 세미-임플리싯 오일러 적분. 스프링-질량 실습(과제 6-학습3)
4주차충돌 감지AABB 트리(dynamic_tree.c)와 GJK(distance.c) 읽기. 브로드/내로우페이즈 구분
5주차제약 솔버 & Soft StepErin Catto의 GDC 슬라이드 + contact_solver.c. 순차 임펄스·소프트 제약·서브스텝 이론
6주차성능 & 결정론데이터 지향 설계, 그래프 색칠 병렬화, -ffp-contract와 부동소수점 결정론. 과제 5 완주
학습 순서 팁

물리 엔진은 "수학 → 역학 → 충돌 → 솔버 → 최적화" 순으로 층이 쌓인다. 처음부터 solver.c(79KB)를 열면 좌절하기 쉽다. API 사용자(과제 1~4) → 이론(2~5주차) → 내부 구현(과제 5) 순서로, "쓸 줄 아는 상태에서 원리를 파고드는" 흐름이 훨씬 덜 지친다. Box2D 시절 자료(Catto의 강연·문서)가 방대하니 3D로 넘어오기 전 2D 개념을 먼저 잡아도 좋다.

10핵심 키워드 사전

이 문서와 저장소에 나온 주요 용어 정리.

용어
강체 물리 엔진 (Rigid Body Physics Engine)
모양이 변하지 않는 단단한 물체들의 운동·충돌·관절을 뉴턴 역학으로 시뮬레이션하는 라이브러리. Box3D가 이 부류다.
용어
Soft Step / 소프트 제약(Soft Constraint)
딱딱한 "겹치면 안 됨" 제약을 아주 뻣뻣한 스프링+감쇠로 모델링해 수치적 안정성을 얻는 Erin Catto의 솔버 기법. 서브스텝과 결합해 쓴다.
용어
브로드페이즈 / 내로우페이즈(Broad / Narrow Phase)
충돌 감지의 2단계. 브로드페이즈는 AABB로 "부딪힐 후보"만 빠르게 추리고, 내로우페이즈는 후보를 정밀 검사해 실제 접촉점을 계산한다.
용어
AABB 트리 / BVH
물체를 감싸는 축 정렬 상자를 계층 트리로 관리해, 충돌 후보 질의를 n²이 아닌 로그 시간에 처리하는 공간 자료구조.
용어
GJK 알고리즘
두 볼록 도형 사이 최단 거리·겹침을 반복 근사로 구하는 고전 알고리즘. Box3D의 distance.c가 구현.
용어
접촉 매니폴드(Contact Manifold)
두 물체가 맞닿은 지점들 + 파고든 깊이 + 밀어낼 법선을 담은 자료구조. 솔버의 입력이 된다.
용어
제약 그래프 색칠(Constraint Graph Coloring)
같은 물체를 공유하는 제약끼리 다른 색이 되게 칠해, 같은 색 그룹을 락 없이 안전하게 병렬 처리하는 기법.
용어
아일랜드(Island) & 잠재우기(Sleep)
서로 연결된 물체 무리(아일랜드)가 충분히 멈추면 계산에서 제외(sleep)해 성능을 아끼는 최적화.
용어
서브스텝(Sub-step)
한 프레임 시간(dt)을 여러 번 잘게 나눠 솔버를 반복 실행하는 것. 기본 4개면 60Hz가 내부적으로 240Hz가 된다.
용어
데이터 지향 설계(DOD, Data-Oriented Design)
객체 단위가 아니라 "같은 데이터를 배열로 모아" CPU 캐시·SIMD 효율을 극대화하는 설계 철학. Box3D의 근간.
용어
크로스플랫폼 결정론(Determinism)
같은 입력이면 어느 OS/CPU에서도 비트 단위로 동일한 결과가 나오게 하는 성질. 락스텝 멀티플레이·리플레이의 전제.
용어
불투명 핸들(Opaque Handle) & Def 패턴
b3BodyId처럼 내부를 숨긴 ID로 API를 노출하고, b3Default***Def()로 설정 구조체 기본값을 채우는 C 스타일 API 설계.
용어
SIMD (SSE2 / Neon)
한 명령으로 여러 데이터를 동시 처리하는 CPU 기능. x86은 SSE2, ARM은 Neon. 물리 연산 벡터화에 필수.
용어
sokol / Dear ImGui
샘플 앱이 쓰는 도구. sokol은 D3D11/Metal/OpenGL을 하나로 감싼 헤더 전용 그래픽 래퍼, ImGui는 즉시 모드 디버그 GUI.

11참고 링크

공식 저장소 · 문서 · 저자 자료.

구분링크
GitHub 저장소github.com/erincatto/box3d
공식 3D 문서box2d.org/documentation3d
Box2D 본가(이론·강연 보고)box2d.org
결정론 관련 글box2d.org/posts/2024/08/determinism
소개 영상(저자)YouTube — Introducing Box3D
고정 타임스텝 가이드Fix Your Timestep!
커뮤니티Discord · GitHub Discussions
라이선스MIT (상용 이용 가능)