Skin Export

이번장에는 Skin 값을 출력해 본다.

맥스의 기본 파이프라인은 기본 기하 도형에서 시작하여 modifier를 이용해 다양한 형태의 메시를 만들수 있다.

아래 그림은 맥스에서 Skin Modifier를 사용한 편집 이미지이다.

Skin  값을 출력하기 전에 Export 대화 상자의 버그를 먼저 잡고 간다.
Ok를 누르면, Export하고 Cancel을 누르면 Export를 안해야 하는데 Export 하는 버그가 있다.
ShowDialog() 기존에 bool을 리턴하던 것을 int로 리턴하도록 수정 하였다.

//ExporterOption.h

int ExporterOption::ShowDialog();

 

//ExporterOption.cpp

int ExporterOption::ShowDialog()

{

    return    DialogBoxParam(....);

}

 

INT_PTR CALLBACK ExporterOption::DialogProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)

{

    switch(message)

    {

            case IDC_OK:

                ........

                EndDialog(hWnd, IDC_OK);

                return 1;

 

            case IDC_CANCEL:

                .........

                EndDialog(hWnd, IDC_CANCEL);

                return 1;

    }

}

 

//maxPluginTest.cpp

int maxPluginTest::DoExport(......)

{
    ..........

    if ( !suppressPrompts && m_option.ShowDiaglog() != IDC_OK )

    {

        //maxPluginTest.cpp익스포트 하지 않고 종료한다.

        return TRUE;

    }

    ...........

}

콜백 함수 DialogProc()에서 EndDialog()에서 두 번째 매개변수로 IDC_OK, IDC_CANCEL을 리턴하여,
maxPluginTest::DoExport()에서 ShowDialog()의 리턴값이 IDC_OK가 아니면 리턴하게 된다.

#include <iskin.h>

#include <modstack.h>

스킨 modifier의 정보를 얻어 오기 위해서 2개의 헤더를 포함 시킨다. 

class maxPluginTest : public SceneExport

{

    ...........

    void ExportSkin(INode* pNode, ISkin* pSkin, int iTreeDepth);

    ...........

    bool FindISkinMod(Object* pObj, IDerivedObject*& pIDObj, int& iSkinIdx);

    ...........

};

2개의 메쏘드를 추가한다.

ExportSkin()는 Skin을 익스포트 한다.
FindISkinMode()은 오브젝트가 스킨이면 true를 리턴한다.

void maxPluginTest::ExportObject(INode* pNode, int iTreeDepth)

{

    ObjectState os;

 

    IDerivedObject* pDerived = NULL;

    int iSkinIdx = -1;

    if (FindISkinMod(pNode->GetObjectRef(), pDerived, iSkinIdx))

    {

        os = pDerived->Eval(0, iSkinIdx + 1);

    }

    else

    {

        os = pNode->EvalWorldState(0);

    }

    Object* pObject = os.obj;

 

    if (pObject != NULL)

    {

        if (pObject->CanConvertToType(triObjectClassID))

        {

            .............

            if (pTriObject != NULL)

            {

                   ................

            }

 

            // Finally, export the skin (if we have it)

            if (pDerived != NULL && iSkinIdx >= 0)

            {

                Modifier* pMod = pDerived->GetModifier(iSkinIdx);

                ISkin* pSkin = (ISkin*)pMod->GetInterface(I_SKIN);

                ExportSkin(pNode, pSkin, iTreeDepth);

            }

        }

    }

}

FindISkinMode()을 이용해서 Skin을 찾으면 ExportSkin()으로 Skin을 익스포트 한다.

여기서 두가지 의문이 있다.

1. os = pDerived->Eval(0, iSkinIdx + 1)라인과  os = pNode->EvalWorldState(0)라인의 차이점을 모르겠다.
디버깅 해보면 두라인의 리턴값이 동일하게 나온다. 어떤 경우에 다르게 나오는지 알수가 없다.

2. os = pDerived->Eval(0, iSkinIdx + 1)에서 iSkinIdx를 대입하지 않고 iSkinIdx + 1을 해주느것도 아리송하다.
관련 글이 있기는 하지만 의문은 풀리지 않는다. 

아래 별도의 링크로 명쾌한 답을 얻을수 없지만 어느정도 도움이 될것이다.

http://dl3d.free.fr/phpBB2/viewtopic.php?p=4586

http://www.gpgstudy.com/forum/viewtopic.php?topic=24412

dl3d.free.fr글 별도 링크

void maxPluginTest::ExportSkin(INode* pNode, ISkin* pSkin, int iTreeDepth)

{

    ISkinContextData* pContext = pSkin->GetContextInterface(pNode);

 

    int iBoneCnt = pSkin->GetNumBones();

    Tab<int> dBoneVtxCounts;

    dBoneVtxCounts.SetCount(iBoneCnt);

    memset(dBoneVtxCounts.Addr(0), 0, iBoneCnt * sizeof(int));

    for (int iVtx = 0; iVtx < pContext->GetNumPoints(); iVtx++)

    {

        for (int iVBone = 0; iVBone < pContext->GetNumAssignedBones(iVtx); iVBone++)

        {

            int iBoneIdx = pContext->GetAssignedBone(iVtx, iVBone);

            dBoneVtxCounts[iBoneIdx]++;

        }

    }

 

    Write(iTreeDepth, "Modifier: Skin");

    for (int i = 0; i < iBoneCnt; i++)

    {

        INode* pBone = pSkin->GetBone(i);

        const char* pBoneName = pBone->GetName();

        Write(iTreeDepth, "   Bone %i: '%s' affects %i vertices", i, pBoneName, dBoneVtxCounts[i]);

    }

}

ExportSkin()에서 Skin Data를 익스포트 한다.

pSkin->GetContextInterface(pNode)를 통해 ISkinContextData를 리턴한다.

bool maxPluginTest::FindISkinMod(Object *pObj, IDerivedObject *&pIDObj, int &iSkinIdx)

{

    if (pObj == NULL || pObj->SuperClassID() != GEN_DERIVOB_CLASS_ID)

    {

        return false;

    }

 

    IDerivedObject* pDerived = (IDerivedObject*)pObj;

    for (int i = 0; i < pDerived->NumModifiers(); i++)

    {

        Modifier* pMod = pDerived->GetModifier(i);

#if   0

        void* pSkin = pMod->GetInterface(I_SKIN);

        if (pSkin != NULL)

        {

            iSkinIdx = i;

            pIDObj = pDerived;

            return true;

        }

#else

        if( pMod->ClassID() == SKIN_CLASSID )

        {

            iSkinIdx = i;

            pIDObj = pDerived;

            return true;

        }

#endif

    }

 

    Object* pNextObj = pDerived->GetObjRef();

    return FindISkinMod(pNextObj, pIDObj, iSkinIdx);

}

FindISkinMod()는 Skin을 찾는다.

SuperClassID()의 값이 GEN_DERIVOB_CLASS_ID면 IDerivedObject의 오브젝트이다.
 

결과)   skin.max         skin.TXT

소스)  maxPluginTest10_Modifiers.zip

참고) 3dsmax - Skin Modifier 간단 사용법