1편 : [일기장] 무작정 FBX SDK 사용해보기 (삽질 + Mesh)
1. 이제 FbxSurfaceMaterial가 뭔지 알아볼 차례
- 1편에서 Layer 정보를 냅다 출력할 때, FbxSurfaceMaterial의 Element도 출력을 했었는데
- 그때는 Material 의 개수와, 각 property들을 찾는 문자열(?)만 출력이 되었다.
(결국 예제코드 컨닝하기...)
- FbxSurfaceMaterial 클래스가 맴버로 가지고 있는 property 내용을 FbxSurfaceMaterial 객체에서 FindProperty()를 이용해서 FpxProperty를 받는다...
- 이제 보니 FbxObject의 상속을 받는 친구였다. FbxObject는 대부분의 FBX object의 베이스 클래스라고 한다.
- 출처 : FBX SDK Documentation: C++: FbxObject Class Reference (autodesk.com)
- Fbx Sdk API에서 Object로서 필요한 대부분의 기능을 FbxObject 클래스에서 제공한다고 한다.
- 내부적으로 FbxClassId 라는 클래스로 unique 함과 그를 이용한 여러 기능(hierarchy 등)을 제공한다.
(기능이 너무 많다. 일단 Material에 집중해야 겠다.)
- 1편의 3번 항목 FbxNode에서 이런저런 attribute를 가진 node 들이 모여서 hierarchy를 이뤄서... Scene을 이룬다고 했다. FbxNode::GetMaterial 항목을 이용해서 현재 노드가 가진 FbxSurfaceMaterial 포인터를 가져와 사용하는 것이다.
(내가 사용하는 요친구는 Mesh가 붙어있는 Node마다 Material이 하나씩 붙어있다.)
- 그리고 예제처럼 FbxObject를 상속받은 FbxSurfaceMaterial 포인터에서 머테리얼 속성에 맞는 Property Name을 FbxObject::FindProperty()로 얻어서 App(메모리)에 올리는 것이다.
2. 예제의 코드를 적당히 내 구조에 맞게 변환하면...
(아 물론 중간중간 자잘한 문제들도 고치면서 ㄹㅇㅋㅋ)
- 다행히 잘 작동한다.
3. Animation은... 어디 숨어있는거냐
- 1편의 5번 항목에서 남은건 이제 Deformer 하나인데, 혹시 FbxDeformer 이거랑 관련이 있을까?
- 문서를 보면 FbxObject의 상속을 받고, 이 친구를 상속받는 클래스는 FbxBlendShape, FbxSkin, FbxVertexCacheDeformer가 있다.
- 그리고 맴버 Enum으로 EDeformerType을 가지고 있다. eSkin 혹은 eVertexCache가 주로 쓰이는 느낌이고... FbxGeometry에 붙어서 그것의 shape에 영향을, 일반적으로 animation에 영향을 미친다고 한다.
- eSkin은 FbxCluster 를 가지고 있는데, 각기 다른 가중치를 가지고 Geometry Control Point의 Subset에 영향을 미친다. 흔히 말하는 skinning 인 것이다.
- eVertexCache의 경우는 FbxCache를 가지고 있고, 얘는 그냥 geometry의 control point의 애니메이션 정보를 가지고 있다고 한다.
- Multi-Layer 관련 뭐시기는 FbxAnimLayer와 FbxAnimStack 으로 넘어갔다고 한다.
(뭔지 모르겠으니 일단 속성을 출력해보자.)
- eSkin 이면 FbxCluster를 이용해서 뭔가를 한다고 했으니 FbxCluster 문서와 샘플코드를 엿보자.
4. FbxCluster (+ FbxSkin)
(일단 문서를 냅다 해석해보겠다)
- cluster ( = link)는 Geometry에 따라 작동하는 entity라고 한다. 더 정확하게 하면, geometry의 control point의 subset을 따라 작동하는 것이다. cluster가 동작하는 각각의 control point에 대해, cluster가 얼마나 영향을 줄지는 가중치에 따라 정해진다고 한다. ELinkMode는 어떻게 적용되는지 지정한다.
- cluster의 Link node는 cluster의 control points에 영향을 주는 node이다. 만약 node가 animated 된다면, control point도 따라서 움직이게 된다.
- FbxSkin 클래스도 함께 보라고 한다. 예를들어 humaniod를 렌더링 한다고 하면, 그 친구는 bone이 있을 것이고. 각각의 bone은 node로 표현이 될 것이다. geometry(mesh를 의미) 를 bone node에 bind 하기 위해서는 FbxSkin을 사용해야 한다. Skin은 각각이 bone에 해당하는, 많은 Cluster를 가지고 있다. 각각의 bone node는 mesh 위의 control point에 영향을 미칠 것이다. (가중치에 따라서)
- 맴버 함수로 얻을 수 있는 속성을 냅다 출력해보자.
- cluster 하나에 link 가 하나씩 붙어있고, 그 link의 이름은 skeleton bone의 이름을 가지고 있다.
- control point의 index 배열과 weights 배열을 얻을 수 있고,
- cluster 본인과, link의 Transform을 얻을 수 있다.
(아마 요 친구가 offset transform 역할을 할 것이다.)
- 이정도면, 이전 교재에서 했던 내용처럼 내 App에 적용시킬 수 있을 것이다.
- 근데.. 아직 애니메이션 정보(Clip)를 얻지 못했다. 어디서 구하지?
5. FbxAnimLayer과 FbxAnimStack에 있나?
- FbxAnimLayer는 animation을 시키는데 사용하는 Curves를 가지고 있는 Node라고 한다.
- FbxAnimStack은 그 FbxAnimLayer가 모인 Node를 말한다.
- 예제를 보면 Scene에서 GetSrcObject< FbxAnimStack >()을 이용해서 가져오는데, 잠깐 FbxScene을 살펴보면, FbxObject -> FbxCollection -> FbxDocument -> FbxScene 으로 상속을 받는다.
(GetSrcObject<>() 함수가 FbxObject 함수이다.)
(이와 비슷한 기능을 하는 GetMember<>() 함수는 FbxCollection 함수이다.)
- 무튼 위 함수를 이용해서, FbxAnimStack과 그 안에 있는 FbxAnimLayer을 얻을 수 있다.
(흠... 이제 어떡하지? 다시 예제를 살펴보자.)
6. FbxAnimCurve 와 FbxAnimCurveNode (안씁니다.)(씁니다)
- 예제에서는 루트노드의 Local Transform(FbxProperty)을 LclTranslation, LclRotation, LclScaling 로 얻어오고, - FbxProperty::Getcurve()에 Scene에서 얻은 AnimLayer와 속성값(X, Y, Z)을 넣어서 FbxAnimCurve를 가져온다.
- 일단 내 Fbx의 루트 노드로는 실패했다. Skeleton Attribute를 가진 노드도, Mesh 노드도 실패했다. 예제와 다른 방법이 필요하다.
- AnimCurve 바로 아래 항목에 FbxAnimCurveNode항목이 존재해서.. GetCurveNode()를 시도해봤더니 다행히 작동했다.
- 아주 간략하게 문서 번역으로 내용만정리하고, Animation Clip 정보를 뽑아보자. (현재 참조하는 노드는 'mixamorig:Hips' 으로 최상위에 Skeleton 속성 노드다.)
(야매 번역)
- AnimCurve : FbxAnimCurveKey가 모여서 정의 되는 클래스이고, 시간에 거쳐서 값이 정해진다. 애니메이션 커브가 함수로 작용하기 위해서는 한 시점에는 하나의 key만 있을 수 있다. key는 시간 순서대로 저장되어있고, 그들은 index로 접근이 가능하다. (0 ~ FbxAnimCurve::KeyGetCount -1). 시간 단위는 FbxTime으로 구할 수 있다. 각각의 key는 Tangent와 interpolation을 가지고 있다. tangent는 animation curve의 들어오고 나가는 방법을 제어하고, Interpolation은 Key들 사이의 행동을 제어한다. Tangent와 Interpolation에는 제어 방법을 정하는 mode가 있지만 문서를 직접들어가서 보자.
- AnimCurveNode : AnimCurve의 집합체이다. AnimCurve 끼리 연결점 역할을 하고, 다른 AnimCurveNode와도 연결을 할 수 있다 (IsComposite()으로 확인). 채널 별로(예를 들면 X,Y, Z) AnimCurve 값을 가질 수 있다.
- 이제 애니메이션 데이터를 대충 출력해보면?
- 대충 요런 느낌이구나...(두개가 똑같은 애니메이션 이니까, 하나만 해도 되겠다.)
- Hip Skeleton의 Translation에 대한 Animation Curve를 구했고, 시간에 따라 Curve의 Value에도 접근하는데 성공했다.
- 이제 모든 Skeleton을 돌아 다니면서, Translation, Rotation, Scaling에 대한 Animation Curve에서 Key값으로 애니메이션 정보(keyframes)를 저장하고 + Skinning을 시키는 것을, 내 App의 구조에 맞게 고쳐야 할 것이다.
- 안된다. 모든 keyframe마다 LocalTransform FbxProperty를 가지고 있지 않다. 그렇다고 중간중간 null exception을 처리하면서, 모든 bone에 엄청 많은 keyframe을 읽기는 너무 힘들다.
- 다른 방법을 찾아야 한다. (일단 지금은 EvaluateLocalTranslation 가지고 열심히 해보는 중이다.)
- 프레임이 온전하게 넘어오지 않는다... 이걸로 다시 가야 한다.
7. Animation 노가다는 빨리 안 끝날것 같아서 글 먼저 올립니다. (나중에 결과 올릴게요)
- Fbx의 엄청나게 많은 기능들 중에서 쥐똥만큼만 공부했는데도 이것도 제대로 못한다....
- 화이팅 ㅠㅠ
- 1차 시도
- 애니메이션과 offset matrix 얻는 것을 위 처럼 시도를 해보았는데...
- 게다가 원본 파일은 461 프레임인데, 어째서인지 100 프레임만 import를 한다.
- import 옵션인지, 혹은 다른 문제인지... 이것저것 시도해보는 중인데 쉽지 않다.
- 2차 시도
- 다시 Animation Curve로 돌아간다... 일단 본 마다 Curve에서 얻을 수 있는 애니메이션의 스펙(?)이 어떻게 되는지 냅다 출력해보자.
- 일단 AnimStack이 왜 2개로 나뉘어져있을까.... 정말정말 화가 나지만 일단 넘어가자.
- 한쪽 스택에만 Curve가 있는 본이 있고, 둘 다 있는 본이 있고, 둘다 없는 본이 있다.
- 출력 창에서는 알 수 없지만, 내부적으로 AnimCurve가 nullptr이 거나 Curve에서 채널이 존재하지 않는 FbxProperty 요소가 있다.
- 그리고 다행히도 프레임 461가 온전하게 존재한다.
- 이걸로 해야할 듯 하다. 새로운 자료구조를 만들어서 keyframe을 얻고 시간에 맞는 animation을 새로 만들어야 겠다.
- 시도한 결과는...?
- 3차시도
- local translation이 넘어오지 않고 있었다...
- 그리고 에디터 용으로 쓸법한 AnimCurve를 채널 하나하나 읽어서 transform을 만들고 있었는데,
- EvalutateLocalTranslation(), EvaluateLocalRotation(), EvaluateLocalScaling() 으로도 가능했다...
(이전 시도에는 프레임 index를 넘어가서 문제가 생겼는데 왜 지금은...)
- 아무튼 위 방법을 이용해서 시도해보니
- 팔이 이상하다. (물론 다른 곳이 이상하지 않다는건 아니다.)
- 팔이 너무 이상해서 아주 간단한 애니메이션으로 어떤식으로 문제가 생기는지 봐야겠다.
- 다리는 앞뒤로 차는데 반해, 팔은 양옆으로 친다... (이렇게 걷는 사람이 세상에 어디있어 ㄷㄷㄷ)
- 팔만 parent가 잘 못 되었다거나, offset / pivot 설정이 안 된것도 아닐텐데...
- (마음이 꺾여 버렸다... 잠시 접고 친구랑 진행하던 게임 프로젝트로 다시 돌아가야 겠다... ㅠㅠㅠㅠ)
4차 시도 (성공)
- 팔 뿐만 아니라, 다리도 요상하게 뚝뚝 끊기고... 아마 Rotation에서 뭔가 문제가 생긴 것 같은데
- App에서는 Quat으로 slerp해서 bone의 위치를 정하는데, 여기서 xyz Eular를 받고, 또 이걸 다시 Quat으로 바꾼다음에 넘겨주는 과정에서 문제가 생긴 듯 하다.
- 그래서 오른쪽 처럼 바로 FbxQuaternion의 xyzw을 넘겨주니
- 다행히 잘 작동한다.
8. 참고로 Animation 정보를 테스트해보는 Traverse 함수는 다음과 같다.
- Skinning 정보를 테스트해보는 Traverse 함수는 예제와 매우 흡사하기 때문에 여기에 올리지 않습니다. 궁금하시면 github에서 보시길...
'Programming > D3D12' 카테고리의 다른 글
(공부중) #1 App 구조 예쁘게 다시 바꿔보기 (0) | 2024.07.01 |
---|---|
[일기장] 무작정 FBX SDK 사용해보기 (삽질 + Mesh) (0) | 2024.04.30 |
[책공부] 캐릭터 애니메이션 chap 23 (연습 문제 X) (0) | 2024.03.25 |
[책공부] Quaternion + chap 22 연습 문제 (0) | 2024.03.12 |
[책공부] Ambient Occlusion + chap 21 연습 문제 (0) | 2024.03.08 |