본문 바로가기

컴퓨터 공학 자료(학부)/OpenGL

3d max 파일 loader for OPENGL


제가 맥스에 대해서 아직 해 본 경험이 없기 때문에 약간의 의사 전달하는데 문제를 일으킬 수도 있습니다. 그리구 많은 분들이 맥스로 만든 파일을 자신들의 확장자로 만들어서 플러그인을 제작을 하는데 전 아직 해보진 않아서 뭐라 말씀 드릴 수는 없지만 제가 자료가 있길래 올려 드립니다.

많은 사이트에 올려져 있더라구요!!!   그리고 영어로 된 3DS에 대한 파일 형식 설명 자료 클릭하세요...
3DS에 대한 원문을 해석한 내용이 있어서 올립니다. 클릭하세요...

맥스의 플러그인인 3DS는 여러개의 OBJECT에 속한 MATERIAL로 다시 나눠서 FACE를 계산해서 공간을 구현을 한다.

여기서 난 맥스【� 사용하는 용어를 모르기 때문에 OBJECT는 오브젝트, MATERIAL은 물질, FACE는 폴리곤이라 그냥 말할것이다.

     PRIMARY (0x4D4D)  ==>3DS마다의 Primary Chunk
     |
     +--OBJECTINFO (Ox3D3D)   ==>Main Chunks
     |       |
     |       +--MATERIAL (OxAFFF)  ==>텍스트 정보가 들어가 있다.
     |       |    |
     |       |    +--MATNAME (OxA000)  ==>물질의 이름
     |       |    +--MARDIFFUSE (OxA020)  ==>오브젝트/물질의 색 저장
     |       |    +--MATMAP (OxA200)  ==>새로운 물질을 위한 header
     |       |    +--MATMAPFILE (OxA300)  ==>텍스쳐의 파일이름을 갖는다.
     |       |    +--OBJECT_MESH (Ox4100)  ==>새로 읽어들일 오브젝트정보가 들어있다.
     |       |             |
     |       |             +--OBJECT_VERTICES (Ox41110)   ==>오브젝트의 버텍스
     |       |             +--OBJECT_FACES (0x4120)  ==>오브젝트 페이스(폴리곤)
     |       |             +--OBJECT_MATERIAL (0x4130)  ==>이 오브젝트가 물질,다른 텍스쳐맵,컬러를 가지고 있는지 발견한다.
     |       |             +--OBJECT_UV (0x4140)  ==>UV 텍스쳐 좌표
     |       |
     |       +--OBJECT (0x4000)   ==>페이스(폴리곤),버텍스 등에 대한 정보 저장
     |               
     +--VERSION (0x0002)   ==>현재 사용되고 있는 .3DS버젼
     |
     +--EDITKEYFRAME (0xB000)  ==>키프레임을 위한 모든 정보   

이런 차례로 3DS파일의 구조에 있는 정보를 읽어 들인다. 지금 정보가 이렇게 구성되어 있다구 해서 하나의 정보가 빠졌다구 해서 뭐가 잘못되거나 하진 않는다. 중요한 정보는 꼭 필요하겠지만 그렇지 않은 정보는 없다구 해서 공간을 구현을 하는데는 문제가 없다는 것이다.

그럼 진짜 소스를 보면서 어떻게 이 모든 정보들을 빼와서 공간을 구현을 하는지 보자.

지금 이 부분이 3DS파일의 전체 내용을 거의 생성한다구 보면 된다. 그럼 내려가 보자.
bool CLoad3DS::Import3DS(t3DModel *pModel, char *strFileName)
{
        char strMessage[255] = {0};
        m_FilePointer = fopen(strFileName, "rb"); //바이너리 모드로 읽어 들인다.

        if(!m_FilePointer)
        {
                sprintf(strMessage, "Unable to find the file: %s!", strFileName);
                MessageBox(NULL, strMessage, "Error", MB_OK);
                return false;
        }
        ReadChunk(m_CurrentChunk); //읽기 시작이다.
        if (m_CurrentChunk->ID != PRIMARY)   //3DS파일의 첫메인이라구 할수 있는 부분의 정보를 원한다.
        {
                sprintf(strMessage, "Unable to load PRIMARY chuck from file: %s!", strFileName);
                MessageBox(NULL, strMessage, "Error", MB_OK);
                return false;
        }
        ProcessNextChunk(pModel, m_CurrentChunk);
          //VERSION,OBJECTINFO,MATERIAL,OBJECT,EDITKEYFRAME순으로 구성하는 정보를 읽어 들인다.
        ComputeNormals(pModel);
          //각 오브젝트의 단위 벡터를 구한다. 계산 속도를 향상 시키기 위해서...
        CleanUp();
        return true;
}

그럼 하나하나 함수에 대해서 어떻게 읽어 들이는지 알아보면 끝이다.

void CLoad3DS::ReadChunk(tChunk *pChunk)
{
        pChunk->bytesRead = fread(&pChunk->ID, 1, 2, m_FilePointer); //OBJECT,MATERIAL의 ID정보가 2바이트에 들어가 있다.
        //fread(void *buffer, size_t size, size_t num, FILE *fp); 파일 fp로부터 buffer로 size크기의 num 갯수의 객체를 읽음
        //buffer : 데이터를 저장할 위치 size : 데이터의 크기

        pChunk->bytesRead += fread(&pChunk->length, 1, 4, m_FilePointer); //2부터6까지 4바이트에 정보를 읽어 들인다.
                                                   //이 파일의 길이는 78873바이트를 가진다는 정보를 얻어 온다.
}

void CLoad3DS::ProcessNextChunk(t3DModel *pModel, tChunk *pPreviousChunk)
{
  t3DObject newObject = {0};                                    
  tMaterialInfo newTexture = {0};                       
  unsigned int version = 0;                                     
  int buffer[50000] = {0};                              
  m_CurrentChunk = new tChunk;                                                  
  while (pPreviousChunk->bytesRead < pPreviousChunk->length)
  {
        ReadChunk(m_CurrentChunk);

        switch (m_CurrentChunk->ID)
        {
        case VERSION:   
            m_CurrentChunk->bytesRead += fread(&version, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead,m_FilePointer);

            if (version > 0x03)
                MessageBox(NULL, "This 3DS file is over version 3 so it may load incorrectly", "Warning", MB_OK);
          break;

        case OBJECTINFO:        
          ReadChunk(m_TempChunk);
          m_TempChunk->bytesRead += fread(&version, 1, m_TempChunk->length - m_TempChunk->bytesRead,m_FilePointer);
          m_CurrentChunk->bytesRead += m_TempChunk->bytesRead;

          ProcessNextChunk(pModel, m_CurrentChunk);
          break;

        case MATERIAL:  
          pModel->numOfMaterials++;
          pModel->pMaterials.push_back(newTexture);

          ProcessNextMaterialChunk(pModel, m_CurrentChunk);
        break;

        case OBJECT:    
          pModel->numOfObjects++;       
          pModel->pObject.push_back(newObject);         
          memset(&(pModel->pObject[pModel->numOfObjects - 1]), 0, sizeof(t3DObject));
          m_CurrentChunk->bytesRead += GetString(pModel->pObject[pModel->numOfObjects - 1].strName);

          ProcessNextObjectChunk(pModel, &(pModel->pObject[pModel->numOfObjects - 1]), m_CurrentChunk);
        break; 

        case EDITKEYFRAME:

          // 이 부분을 계산을 해야 키프레임을 인식을 해서 움직이게 할거 같은데 keyframe소스는 없으므로 알길이 없다.
          //ProcessNextKeyFrameChunk(pModel, m_CurrentChunk);

          m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead,m_FilePointer);
        break;

        default:
          m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead,m_FilePointer);
        break;
        }
         pPreviousChunk->bytesRead += m_CurrentChunk->bytesRead;
        }
        delete m_CurrentChunk;
        m_CurrentChunk = pPreviousChunk;

이제 필요한 모든 정보를 읽었으므로 진짜 그려보도록 하겠습니다.

for(int i = 0; i < g_3DModel.numOfObjects; i++)
{
if(g_3DModel.pObject.size() <= 0) break;
t3DObject *pObject = &g_3DModel.pObject[i];
        if(pObject->bHasTexture) { //텍스쳐가 사용이 됐는지 안됐는지를 여부를 파악
        glEnable(GL_TEXTURE_2D);
        glColor3ub(255, 255, 255);
        glBindTexture(GL_TEXTURE_2D, g_Texture[pObject->materialID]); //사용된 텍스쳐에대한 정보가 들어 있다.
        } else {
                glDisable(GL_TEXTURE_2D);

                glColor3ub(255, 255, 255);
        }
        glBegin(g_ViewMode);     

        for(int j = 0; j < pObject->numOfFaces; j++) //폴리곤 생성
        {
                for(int whichVertex = 0; whichVertex < 3; whichVertex++)
                {
                int index = pObject->pFaces[j].vertIndex[whichVertex];
                glNormal3f(pObject->pNormals[ index ].x, pObject->pNormals[ index ].y, pObject->pNormals[ index ].z);           
                if(pObject->bHasTexture) {
                        if(pObject->pTexVerts) {
                                                 glTexCoord2f(pObject->pTexVerts[ index ].x, pObject->pTexVerts[ index ].y);
                                                     //텍셀 값 계산
                                             }
                        } else { //텍스쳐 매핑을 사용하지 않고 그냥 맥스에서 지원하는 폴리곤에 색을 입혔으면 정보를 얻어 온다.
                        if(g_3DModel.pMaterials.size() && pObject->materialID >= 0)
                        {
                        BYTE *pColor = g_3DModel.pMaterials[pObject->materialID].color;

                        glColor3ub(pColor[0], pColor[1], pColor[2]);
                        }
                            }
                             glVertex3f(pObject->pVerts[ index ].x, pObject->pVerts[ index ].y, pObject->pVerts[ index ].z);
                            }
                        }
                glEnd();                                                        
        }



출처:http://www.misofruit.co.kr/seojewoo/programming/opengl/opengl-9.htm