ASE 익스포터 (플러그인) 수정 - 2. 몇가지 수정 & 피지크, 스킨 얻어오기

 

 다음의 수정 사항들은, 파서에서 가공할 수 있는 데이터를 익스포터에서 처리해 줌으로써 최적화된 데이터

를 좀더 편하고 빠르게 처리할 수 있도록 합니다. 파서의 소스를 더욱 간결하게 만들 수 있지만, 기존의 파서

로는 더이상 ASE파일을 읽어 들일 수 없게 됩니다. 따라서 ASE인 ASCII Exporter 파일의 속성은 변함이 없

지만, 기존의 ASE파일 보다는 사용자 정의 파일에 가깝게 됩니다. 이러한 최적화가 끝난 후 데이터를 바이

너리로 변환해서 출력한다던가, 에니메이션 구조에 적합한 데이터로 변환하여 출력 한다면 여러분만의 모델

 파일이 완성이 될것입니다.

 

1. 메쉬의 정점정보를 로컬 좌표로 변환

변환함수

void AsciiExp::ExportMesh(INode* node, TimeValue t, int indentLevel)

변환위치

for (i=0; igetNumVerts(); i++)
{
    Point3 v = mesh->verts[i]; // using node local space coordinate

    fprintf(pStream, "%s\t\t%s %4d\t%s\n",

    indent.data(), D_MESH_VERTEX, i, Format(v));
}

변환할내용

Point3 v = tm * mesh->verts[i]; 이부분을 Point3 v = mesh->verts[i]; 로 고치면 됩니다.

 출력을 위한 3D Max는 오브젝트를 화면에 출력하기 위해 최적화된 정보를 보여줍니다. 하지만 우리의 목

적은 바로 자유로운 움직임을 가능하게 하는 게임에서 쓸 목적을 가지고 있습니다. 간단히 TM을 빼버려서

로컬좌표로 출력되게 합니다.

 

2. 노드에서 Local TM 가져오기

변환함수

void AsciiExp::ExportNodeTM(INode* node, int indentLevel)

삽입내용

INode *parent;
Matrix3 parentTM, nodeTM, localTM;
nodeTM = node->GetNodeTM(GetStaticFrame());
parent = node->GetParentNode();
parentTM = parent->GetNodeTM(GetStaticFrame());
localTM = nodeTM*Inverse(parentTM); //부모 tm의 역행렬을 곱해서 local tm을 얻는다.

 Local TM = TM * Inv(ParentTM) 입니다, 즉 나중에 이 Local TM을 얻으려면 부모의 역행렬을 가져와 계

산해야 하기 때문에 계산의 편의성을 위해 Local TM을 함께 얻어 오는것이 좋습니다. DumpLocalMatrix3

함수를 변경하셔서 Local,World 모두를 얻어옵니다.

3. Rotate Sample에서 Quaternion값 변경

변환함수

void AsciiExp::DumpRotSample(INode* node, int indentLevel)

변환위치

Quat q = ap.q / prevQ;

prevQ = ap.q;

변환할내용

Quat q = ap.q;로 고칩니다.

 Quaternion값이 변화량만 되있기 때문에 이것을 축적된 형태로 변환 합니다. 이것으로 간단하게 변환방법을

 알아봤습니다. 부수적으로 좌표계 변환처리와 필요없는 정보의 제외등의 작업 만 한다면 깔끔한 모델파일이

 출력된 것을 보실 수 있습니다.

 

*. Export Physique / Skin Data


 모든 소스는 3D MAX 7.0에서 제작 / 수정 되었고, 아래 피지크/스키닝 소스는 예전 저버전용 3D MAX Exporter에서 발췌하였으나, 7.0에서도 거의 수정없이 사용됨을 밝힙니다.

 

Physique Modifier 정보를 찾는 함수,


Modifier* AsciiExp::FindPhysiqueModifier(INode *pNode)
{
  // Get object from node. Abort if no object.
  Object* ObjectPtr = pNode->GetObjectRef();


  if (!ObjectPtr) return NULL;

  // Is derived object ?
  while (ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID && ObjectPtr)
  {
    // Yes -> Cast.
    IDerivedObject *DerivedObjectPtr = (IDerivedObject *)(ObjectPtr);

    // Iterate over all entries of the modifier stack.
    int ModStackIndex = 0;
    while (ModStackIndex < DerivedObjectPtr->NumModifiers())
    {
      // Get current modifier.
      Modifier* ModifierPtr = DerivedObjectPtr->GetModifier(ModStackIndex);

      // Is this Physique ?
      if (ModifierPtr->ClassID() == Class_ID(PHYSIQUE_CLASS_ID_A, PHYSIQUE_CLASS_ID_B))
      {
      // Yes -> Exit.
        return ModifierPtr;
      }

      // Next modifier stack entry.
      ModStackIndex++;
    }
    ObjectPtr = DerivedObjectPtr->GetObjRef();
  }

  // Not found.
  return NULL;
}

Skin Modifier 정보를 찾는 함수,


Modifier* AsciiExp::FindSkinModifier(INode *pNode)
{
  // Get object from node. Abort if no object.
  Object* ObjectPtr = pNode->GetObjectRef();


  if (!ObjectPtr) return NULL;

  // Is derived object ?
  while (ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID && ObjectPtr)
  {
    // Yes -> Cast.
    IDerivedObject *DerivedObjectPtr = (IDerivedObject *)(ObjectPtr);

    // Iterate over all entries of the modifier stack.
    int ModStackIndex = 0;
    while (ModStackIndex < DerivedObjectPtr->NumModifiers())
    {
      // Get current modifier.
      Modifier* ModifierPtr = DerivedObjectPtr->GetModifier(ModStackIndex);

      // Is this Skin ?
      if (ModifierPtr->ClassID() == SKIN_CLASSID)
      {
      // Yes -> Exit.
        return ModifierPtr;
      }

      // Next modifier stack entry.
      ModStackIndex++;
    }
    ObjectPtr = DerivedObjectPtr->GetObjRef();
  }

  // Not found.
  return NULL;
}

PhysiqueData를 익스포트 하는 함수,


void AsciiExp::ExportPhysiqueData(INode* node, Modifier* mod)
{
  //get a pointer to the export interface
  IPhysiqueExport *phyExport = (IPhysiqueExport *)mod->GetInterface(I_PHYEXPORT);  

  //get the physique version number.  
  //If the version number is > 30 you may have floating bones
  int ver = phyExport->Version();

  //get a pointer to the export context interface
  IPhyContextExport *mcExport = (IPhyContextExport *)phyExport->GetContextInterface(node);

  //convert to rigid for time independent vertex assignment
  mcExport->ConvertToRigid(true);

  //allow blending to export multi-link assignments
  mcExport->AllowBlending(true);

  int x = 0;
  float normalizedWeight;
  Point3 offsetVector;

  //these are the different types of rigid vertices supported
  IPhyBlendedRigidVertex *rb_vtx;
  IPhyRigidVertex *r_vtx;


  //get the vertex count from the export interface
  int numverts = mcExport->GetNumberVertices(); // 노드의 버텍스 수

  //Export the list of bones used by Physique // 이 부분은 버텍스세이더를 위해 만들어 놓은 부분같군여
  //See the function below for more details // 아직까징 Constants가 96개밖에 지원되지 않는 그래픽 카드가 많은데
  //ExportPhysiqueBoneList(mcExport);  // 매 피지크마다 본리스트가 나온다면 이 문제를 해결할수 있습니다
  // 지금은 일단 안쓰겠습니다

  fprintf(pStream,"\t%s {\n" , "*PHYSIQUE"); // 피지크 정보 출력 시작

  //gather the vertex-link assignment data
  for (int i = 0; i<numverts; i++)
  {
    //keep record of the total bone weight in order to normalize the flaoting bone weights later
    float totalWeight = 0.0f, weight = 0.0f;
    TSTR nodeName;

    //Get the hierarchial vertex interface for this vertex
//    IPhyVertexExport* vi = mcExport->GetVertexInterface(i, HIERARCHIAL_VERTEX);
    IPhyVertexExport* vi = (IPhyVertexExport*)mcExport->GetVertexInterface(i);
    if (vi){

      //check the vertex type and process accordingly 
      int type = vi->GetVertexType();
      switch (type)
      {
        //we have a vertex assigned to more than one link
      case RIGID_BLENDED_TYPE:     // 블랜드 타입
//        fprintf(pStream, "\t\t%s\t%d {\n", "*BLENDED_RIGID", i);

        //type-cast the node to the proper class  
        rb_vtx = (IPhyBlendedRigidVertex*)vi;

        //iterate through all links this vertex is assigned to
        for (x = 0; x<rb_vtx->GetNumberNodes(); x++)
        {
          //Get the node and its name for export
          nodeName = rb_vtx->GetNode(x)->GetName();

          //Get the returned normalized weight for export.  
          //Use the weight reference to keep track of the total weight.
//          normalizedWeight = rb_vtx->GetWeight(x, &weight);
          normalizedWeight = rb_vtx->GetWeight(x);
          if( normalizedWeight < 0.000001 ) continue ;
          totalWeight += weight;

          //Get the offset vector for export
          offsetVector = rb_vtx->GetOffsetVector(x);
          
          // 수정
          fprintf(pStream, "\t\t%s %d %s %0.6f %s \"%s\"\n",
            "*VERTEX ID", i, "Weight", normalizedWeight, "Bone", nodeName); 
        }
        break;

        // we have a vertex assigned to a single link
      case RIGID_TYPE:     // 논블랜드 타입
        {
          //type-cast the node to the proper class
          r_vtx = (IPhyRigidVertex*)vi;

          //get the node and its name for export
          nodeName = r_vtx->GetNode()->GetName();

          //the weight is 1.0f since it is not blended
          float NormalizedWeight = 1.0f;
          //add this vertex weight to the running total
          totalWeight += 1.0f;

          //get the offset vector for export
          offsetVector = r_vtx->GetOffsetVector();

          // 수정
          fprintf(pStream, "\t\t%s %d %s %0.6f %s \"%s\"\n",
            "*VERTEX ID", i, "Weight", NormalizedWeight, "Bone", nodeName); 
        }
        break;

        //Should not make it here since assignments were converted to Rigid.  
        //Should be one of the above two types
      default:
        break;
      }
      //release the vertex interface
      mcExport->ReleaseVertexInterface(vi);
    }

  }

  fprintf(pStream, "\t}\n");  // 피지크 정보 출력 끝

  //release the context interface
  phyExport->ReleaseContextInterface(mcExport);

  //Release the physique interface
  mod->ReleaseInterface(I_PHYINTERFACE, phyExport);
}


SkinData를 익스포트 하는 함수.



void AsciiExp::ExportSkinData(INode* node, Modifier* mod)
{
  ISkin *skin = (ISkin *)mod->GetInterface(I_SKIN);

  ISkinContextData *skin_data = (ISkinContextData *)skin->GetContextInterface(node);

//  int numverts = skin_data->GetNumPoints( ) ;

  Object *obj = node->EvalWorldState(ip->GetTime()).obj;

  TriObject *tri = (TriObject*)obj->ConvertToType(ip->GetTime(),Class_ID(TRIOBJ_CLASS_ID,0));
  Mesh* mesh = &tri->GetMesh();

  int numverts = mesh->numVerts ;
  
  fprintf(pStream,"\t%s {\n" , "*SKIN"); // 스킨 정보 출력 시작

  for (int i = 0; i<numverts; i++)
  {
    float totalWeight = 0.0f, weight = 0.0f;
    TSTR nodeName;

    int numWeights = skin_data->GetNumAssignedBones(i);

    for(int j = 0 ; j < numWeights ; j ++ )
    {
      INode * pBone = skin->GetBone( skin_data->GetAssignedBone( i, j ) );
      weight = skin_data->GetBoneWeight( i, j );
      if( weight < 0.000001 ) continue ;
      nodeName = pBone->GetName();

      fprintf(pStream, "\t\t%s %d %s %0.6f %s \"%s\"\n""*VERTEX ID", i, "Weight", weight, "Bone", nodeName); 
    }
  }
  fprintf(pStream, "\t}\n");  // 스킨 정보 출력 끝
  mod->ReleaseInterface( I_SKIN, skin );
}


 퍼가실땐 블러그에 방명록에 글 하나만 남겨주세요^^ http://oneil.cafe24.com