Programming/D3D12

[일기장] 무작정 FBX SDK 사용해보기 (Material + Animation)

Dorasima 2024. 5. 5. 12:29

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 관련 뭐시기는 FbxAnimLayerFbxAnimStack 으로 넘어갔다고 한다.

(뭔지 모르겠으니 일단 속성을 출력해보자.)

eSkin 이구만...

- 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을 새로 만들어야 겠다.

아니 근데 왜 또 얘는 487개야

- 시도한 결과는...?

짜증난다 진짜...

- 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에서 보시길...