max export 7. 어떻게 애니메이션 시킬까?

반응형


Control access, Key Export

Controller
요소(item)의 애니메이션을 지정하거나 제어하는 플러그인 유형.
여러가지 컨트롤러 유형이 존재.
ex) 변환 컨트롤로(PRS Controller)는 씬 내의 노드 위치를 결정. Matrix3를 제어하는 컨트롤러.
      표현식 컨트롤러는 요소의 매개변수가 수학적 표현식에 의해 제어될 수 있도록 허용.

Exporting Animation Keys
- 모든 애니메이션들은 Controll class의 instance들에 의해 포함되고 계산된다.
- 컨트롤들은 그들의 SuperClassID 타입에 기반하여 적용된다.
   ( CTRL_MATRIX3_CLASSID, CTRL_ROTATION_CLASS_ID)
- 컨트롤 클래스들은 종종 다른 클래스들을 참조한다.
   PRS matrix3 컨트롤은 position, rotation, 그리고 scale 컨트롤들을 포함한다.
   SubAnim 함수를 사용해서 컨트롤의 계층구조를 순회할 수 있다.
- Controller들의 key에 접근하기 위해 IIKeyControlInterface를 사용한다.
- 올바른 키 클래스를 사용하기 위해 컨트롤 타입을 알아야 한다.
- 변환 컨트롤러는 INode::GetTMControl을 통해 접근할 수 있다.

Graph Editors - Saved Track Views - Track View (Curve Editor) 기능


Graph Editors - New Schematic View 기능


현재 모델은 위와 같은 상태이다.
각 노드가 Position, Rotation, Scale Controllers를 가지고 있는 것을 볼 수 있다.

이번에 얻어야 할 정보는 Controller의 내부 값이다.
순서는 이전과 같이 진행한다.
1. 노드의 자식갯수 정보
2. 현재노드의 local transform 정보.
3. 현재노드의 애니메이션 정보
4. 현재노드의 오브젝트 정보
5. 현재노드의 자식노드들을 순회.


추가되어진건 3번뿐 이지만 까먹은 것도 있고 해서 복습차원에서 하나씩 다시 살펴봐야겠다.

1. 노드의 자식갯수 정보.

node->NumberOfChildren();

2. 현재노드의 local transform 정보 .
Matrix3 tmWorld  = node->GetNodeTM(timeValue);
Matrix3 tmParent = node->GetParentTM(timeValue);
Matrix3 tmLocal  = tmWorld * Inverse(tmParent);

3. 현재노드의 애니메이션 정보.
// 애니메이션 정보가 있는지 검사.
node->GetNodeTM(0, &nodeValid);
if(FOREVER == nodeValid)
 return;

// 변환컨트롤러 얻기. - 애니메이션 기본정보(PRS)를 얻기위해 필요함.
Control* nodeCtrl = node->GetTMController();

// 변환컨트롤러가 참조하는 세개의 컨트롤러들(PRS)
Control* subCtrls[3];
subCtrls[0] = nodeCtrl->GetPositionController();
subCtrls[1] = nodeCtrl->GetRotationController();
subCtrls[2] = nodeCtrl->GetScaleController();

// 맥스 애니메이션에 키정보가 있다면 KeyFrame 애니메이션을, 없다면 Sample 애니메이션을 사용.
for(int i=0; i<3; i++)
{
 Control* ctrl = subCtrls[i];
 if(NULL != ctrl)
 {
  if(ctrl->IsKeyable())
   continue;

  if (NULL != ctrl->GetXController()  && 
      NULL != ctrl->GetYController() && 
   NULL != ctrl->GetZController()  )
  {
   continue;
  }
 }
 sample = true;    // key정보가 없다면 sample 사용.
 break;
}

// sample와 key애니메이션.
if(sample)
 {
  ExportAnimationSample(node, treeDepth);
 }
 else
 {
  Write(treeDepth, "PRS Export: ");
  ExportAnimationKey(subCtrls[0], "Position", treeDepth + 1);
  ExportAnimationKey(subCtrls[1], "Rotation", treeDepth + 1);
  ExportAnimationKey(subCtrls[2], "Scale", treeDepth + 1);
 }

// key control을 얻는다.
IKeyControl* keyControl = GetKeyControlInterface(anim);

// 자식 애니메이션 정보를 얻는다.
// 이곳에서 POSITION의 XYZ값, ROTATION의 XYZ값, SCALE의 XYZ이다.

Animatable* subCtrl = anim->SubAnim(i);
MSTR subName = anim->SubAnimName(i);

// KeyFrame 정보가 있는지 검사.
if(keyControl->GetNumKeys() == 0)
   return;

// IKeyControl에 의해 지원되는 세가지 Control Type
Class_ID clid = anim->ClassID();

// KeyType에 맞게 정보 출력
// - Linear

template <class KeyType>
void MgExport2::ExportAnimationKeyLinear(IKeyControl* keyControl, int treeDepth)
{
 Write(treeDepth, " Linear Interpolation");
 KeyType ioKey;
 for(int i=0; i<keyControl->GetNumKeys(); i++)
 {
  keyControl->GetKey(i, &ioKey);
  Write(treeDepth, "Key at time %fs:", TicksToSec(ioKey.time));
  WriteValue(treeDepth, " Value: ", ioKey.val);
 }
}

// - Bezier
template <class KeyType>
void MgExport2::ExportAnimationKeyBezier(IKeyControl* keyControl, int treeDepth)
{
 Write(treeDepth, " Bezier Interpolation");
 KeyType ioKey;
 for(int i=0; i<keyControl->GetNumKeys(); i++)
 {
  keyControl->GetKey(i, &ioKey);
  Write(treeDepth, "Key at time %fs:", TicksToSec(ioKey.time));
  WriteValue(treeDepth, " Value: ", ioKey.val);

  WriteValue(treeDepth, " In Tangent: ", ioKey.intan);
  WriteValue(treeDepth, " Out Tangent: ", ioKey.outtan);
  WriteValue(treeDepth, " In Length: ", ioKey.inLength);
  WriteValue(treeDepth, " Out Length: ", ioKey.outLength);
 }
}

// - TCB
template <class KeyType>
void MgExport2::ExportAnimationKeyTCB(IKeyControl* keyControl, int treeDepth)
{
 Write(treeDepth, "  TCB Interpolation");
 KeyType ioKey;
 for (int i = 0; i < keyControl->GetNumKeys(); i++)
 {
  keyControl->GetKey(i, &ioKey);
  Write(treeDepth, "Key at time %fs:", TicksToSec(ioKey.time));
  WriteValue(treeDepth, "    Value: ", ioKey.val);
  Write(treeDepth, "   tension: %f continuity: %f bias: %f", ioKey.tens, ioKey.cont, ioKey.bias);
  Write(treeDepth, "   ease in: %f ease out: %f", ioKey.easeIn, ioKey.easeOut);
 }
}

4. 현재노드의 오브젝트 정보.
Object* object = node->GetObjectRef();

// object를 통해 superClassID와 classID를 얻는다.
SClass_ID superClassID  = object->SuperClassID();
Class_ID  classID  = object->ClassID();

// tri Object로 변환가능한지 검사.
if(object->CanConvertToType(triObjectClassID))

// 변환가능하다면 변환해서 mesh정보를 얻는다.
Mesh& mesh =triObject->GetMesh();

// mesh로부터 정점 정보들을 얻는다. - position, triangle indices
for(i=0; i<cntVertex; i++)
{
 Point3& vertex = mesh.getVert(i);
 Write(treeDepth, "Vertex[%02i] : (%f,\t%f,\t%f)", i, vertex.x, vertex.y, vertex.z);
}

int cntTriangle = mesh.getNumFaces();
Write(treeDepth, "Number of Triangle : %i", cntTriangle);
for(i=0; i<cntTriangle; i++)
{
 // face를 얻는다.
 Face& face = mesh.faces[i];

 // face의 모든 정점들을 얻는다.
 DWORD* indices = face.getAllVerts();

 // Face의 모든 정점들을 출력한다.
 Write(treeDepth, "Face[%02i], Vertex IDs : (%u, %u, %u)", i, indices[0], indices[1], indices[2]);
}


5. 현재노드의 자식노드들을 순회.
for(int i=0; i<cntChildren; i++)
Export(node->GetChildNode(i), treeDepth);

Result...
좀 두서없이 적긴 했지만 결국 이번에 추가된건 Control을 얻어서 Position, Rotation, Scale Control들의
KeyFrame 정보를 얻는 것이다.

여기서는 KeyFrame 정보를 사용했지만 Sample 정보를 이용할 수 도 있다.
sample방식은 KeyFrame보다는 덜 정확하지만 복잡한 보간을 거치지 않고 간단한 보간을 사용할 수 있다는 점에서 장점이 있다.

TAGS.

Comments