파일 포맷¶
두 OTS 파일 모두 들여쓰기가 되어 있고 읽을 수 있는 순수 UTF-8 JSON입니다. 텍스트 에디터로 열어보거나, 버전 관리에서 둘을 diff하거나, 하나를 읽는 스크립트를 짤 수 있습니다. 이건 의도적입니다 — 들여다볼 수 없는 교환 포맷은 디버깅할 수 없는 교환 포맷입니다.
현재 포맷은 버전 4입니다.
공통 헤더¶
두 포맷은 똑같은 방식으로 시작합니다:
{
"format": "octo_anm",
"version": 4,
"source": "harmony",
"fps": 24.0,
"coordinate_system": {
"up": "y",
"hand": "right",
"unit": "meters",
"rot_order": "xyz",
"rot_repr_anim": "euler_deg",
"rot_repr_rest": "quat_wxyz"
}
}
| 키 | 의미 |
|---|---|
format |
octo_pvt 또는 octo_anm |
version |
포맷 버전. 정확히 일치해야 합니다 — 아래 참조 |
source |
어떤 애플리케이션이 썼는지: harmony, blender, 또는 maya |
fps |
소스 씬의 프레임 레이트 |
coordinate_system |
값들이 놓인 공간 — 좌표계 참조 |
하위 버전 마이그레이션 없음
reader는 오직 자기 버전만 받아들입니다. v3 파일을 v4 빌드에 로드하면 이렇게 보고합니다:
unsupported version 3 (this build writes/reads v4; no backward migration — re-export from the source DCC)
이건 실수가 아니라 선택입니다. 오래된 파일을 조용히 마이그레이션한다는 건 그 숫자들이 무슨 뜻이었는지 추측한다는 뜻이고, 추측이 틀리면 대놓고 망가진 rig이 아니라 미묘하게 어긋난 rig이 나옵니다. 소스에서 다시 export하는 건 몇 초면 되고 항상 정확합니다.
.pvt — pivot 파일¶
{
"format": "octo_pvt",
"version": 4,
"source": "harmony",
"name": "spaceship",
"scale_factor": 1.0,
"pivot_space": "local",
"fps": 24.0,
"coordinate_system": { "…": "…" },
"bones": [
{
"name": "hip",
"parent": null,
"pivot": [0.0, 0.0, 0.0],
"rotation": [1.0, 0.0, 0.0, 0.0],
"path": "Armature/hip",
"rig": "spaceship",
"is_camera": false
}
],
"cameras": []
}
| 키 | 의미 |
|---|---|
name |
rig의 이름 |
scale_factor |
사용자용 Scale 옵션. pivot은 쓸 때 이 값을 곱하고, 읽을 때 나눕니다 |
pivot_space |
local (parent 기준) 또는 armature |
bones[].pivot |
rest 위치 |
bones[].rotation |
rest 회전, quaternion [w, x, y, z] |
bones[].path |
소스 씬에서의 항목 전체 경로 — 매칭에 쓰이고, 서로 다른 rig에 있는 동명 항목을 구분하는 데도 쓰입니다 |
bones[].rig |
rig 태그 |
path, rig, is_camera는 비어 있으면 생략되므로, 단순한 파일은 작게 유지됩니다.
.anm — 애니메이션 파일¶
{
"format": "octo_anm",
"version": 4,
"source": "maya",
"frame_range": [1, 60],
"fps": 24.0,
"coordinate_system": { "…": "…" },
"pegs": [
{
"name": "hip-P",
"path": "Top/spaceship/hip-P",
"parent": "root",
"rest_pivot": [0.0, 0.0, 0.0],
"rest_rotation": [1.0, 0.0, 0.0, 0.0],
"rig": "spaceship",
"is_camera": false,
"source_modes": {
"position": "separate",
"rotation": "euler",
"scale": "separate",
"enable_3d": true
},
"keyframes": [
{
"frame": 1,
"pos": [0.0, 0.0, 0.0],
"rot": [0.0, 0.0, 0.0],
"rot_quat": [1.0, 0.0, 0.0, 0.0],
"scl": [1.0, 1.0, 1.0],
"interp": "BEZIER",
"handles": { "pos": [], "rot": [], "scl": [] }
}
]
}
],
"cameras": []
}
자급자족 필드¶
parent, rest_pivot, rest_rotation, rig은 각 peg 안에 접혀 들어간 .pvt 내용입니다. Create Rig가 .anm 하나만으로 동작하는 스켈레톤을 세울 수 있는 이유가 바로 이것입니다. 애니메이션 파일 (.anm)를 참조하세요.
Keyframe¶
| 키 | 의미 |
|---|---|
frame |
프레임 번호 |
pos |
위치, 정규 공간의 미터 |
rot |
Euler XYZ, 도(degree) — 사람이 읽을 수 있는 형태 |
rot_quat |
같은 회전을 quaternion [w, x, y, z]으로 표현한 것. world matrix bake에서 나옵니다 |
scl |
스케일 |
interp |
BEZIER, LINEAR, 또는 CONSTANT |
handles |
절대 bezier handle. 축별로, 채널별로 묶여 있습니다 |
rot_quat이 이깁니다
둘 다 있을 때는 rot_quat이 권위 있는 값이며 모든 importer가 그쪽을 선호합니다. 순서 독립적이라서 Euler 순서 모호성을 통째로 피해 갑니다. rot은 읽기 쉬운 대비책으로 남겨둡니다.
Maya와 Blender의 bake는 rot_quat과 나란히 handle을 내보내지 않습니다. 로컬 프레임 기준 handle은 bake된 world 커브에 대응되지 않기 때문입니다. 보간 타입 자체는 그대로 전달됩니다.
Handle은 (프레임, 값) 공간에서의 절대 [[left_x, left_y], [right_x, right_y]] 쌍이며 — 축마다 하나씩, pos, rot, scl로 묶입니다. 상대값이 아니라 절대값이라서, 들여올 때 프레임 레이트에 맞춰 다시 유도할 필요가 없습니다.
source_modes¶
peg이 소스 애플리케이션에서 어떻게 제작되었는지에 대한 기록 — 위치가 separate인지 path 기반인지, 회전이 Euler인지 quaternion-path인지, 3D가 켜져 있었는지.
힌트일 뿐, 그 이상은 아닙니다
source_modes는 어떤 값에도 영향을 주지 않습니다. 파일을 읽는 사람을 위한 진단 정보입니다. importer가 이것 때문에 커브를 쓰는 방식을 바꾸지는 않습니다.
카메라¶
두 포맷 모두 같은 카메라 블록을 담으며, 이름으로 애니메이션 항목과 연결됩니다:
| 키 | 의미 |
|---|---|
name, peg, path |
신원, 그리고 어떤 항목이 이 카메라를 움직이는지 |
is_default |
이것이 씬의 기본 카메라인지 여부 |
fov, fov_axis |
시야각 — Harmony의 관례에 맞춘 수직 값 |
override_fov |
Harmony의 카메라별 FOV 오버라이드 플래그 |
near_plane, far_plane |
clip plane, 정규 공간의 거리 |
near_field, far_field |
Harmony의 원래 clip 값을 field 단위로. Harmony 왕복이 무손실이 되도록 보존합니다 |
angle |
roll, Z축 기준 도(degree) |
offset_x/y/z, pivot_x/y |
peg 기준 배치 |
rig |
rig 태그 |
Cameras를 참조하세요.
실무 메모¶
파일은 작습니다. 60프레임 샷이 담긴 완전한 캐릭터 rig도 수백 킬로바이트 수준입니다. 이메일로 보내거나, 버전 관리에 넣거나, 기록용으로 샷 옆에 함께 두어도 괜찮습니다.
diff가 됩니다. 같은 rig을 두 번 export하면 비교 가능한 JSON이 나오므로, "이 두 버전 사이에 뭐가 바뀌었나"가 추측이 아니라 텍스트 diff가 됩니다.
이름은 여러분 마음대로. OTS는 파일 이름을 신경 쓰지 않습니다. 파일을 식별하는 건 그 안의 format 키입니다. .pvt / .anm 확장자를 유지하는 건 단지 파일 선택 창이 제대로 동작하게 하기 위해서입니다.