Tutorial 17 - Carga de Modelos 3DS (Malla)
Fecha Lunes, 05 noviembre a las 15:59:46
Tema DirectX/Opengl


Después de tanto tiempo, veamos un ejemplo en el que visualizaremos algo más que cubos esferas o cilindros. En este caso se trata de carga de modelos en formato 3DS.



Podéis encontrar por Internet una descripción amplia del formato 3DS, pero básicamente es un fichero compuesto por bloques con un identificador y unos datos. A esta combinación se le denomina "Chunck". Por tanto el formato del fichero pasa a ser una serie de "Chunck''s" que a su vez, pueden contener otra serie de "Chunck''s" en cada bloque de datos.

Así que definiremos la geometría básica 3D en el fichero 3d_structures.h como sigue a continuación:


    ///////////////////////////////////////////////////////////////////////////////
    /// struct   Mesh: Almacena información sobre una geometría.
    ///
    /// rief   
    /// author  Antonio Lucas Moreno version 1.0 date 07/09/2007 12:33:11
    ///////////////////////////////////////////////////////////////////////////////
    struct Mesh
    {
        Vertex       *  pVertex;        ///< Lista de vértices.
        unsigned int    numVertex;      ///< Nº de vértices.
        unsigned int *  pIndexes;       ///< Lista de índices, en qué orden se renderizan los vértices.
        unsigned int    numIndexes;     ///< Nº de índices.
    };

Definiremos la clase C3DSLoader que será la encargada de leer el fichero 3DS y almacenar la información de la geometría (Mesh) definida anteriormente como una lista de Mesh''s. Vemos también la definición del CHUNK como una estructura formada por su identificador y el tamaño de los datos a leer. Según el identificador leído, el bloque de datos se leerá de una forma u otra.


        ///////////////////////////////////////////////////////////////////////////////
        /// struct  Chunck: Almacena información sobre un bloque de datos del fichero.
        ///
        /// rief   
        /// author  Antonio Lucas Moreno version 1.0 date 14/09/2007 10:21:18
        ///////////////////////////////////////////////////////////////////////////////
        struct Chunck
        {
            unsigned short  id;             ///< Identificador del bloque.
            unsigned int    lenght;         ///< Tamaño en bytes del bloque.
        }
        m_Chunck;

Según el identificador leído, el bloque de datos se leerá de una forma u otra.


    #define MAIN3DS                     0X4D4D
    #define EDIT3DS                     0X3D3D

    #define EDIT_MATERIAL               0xAFFF
        #define MAT_NAME                0xA000
        #define MAT_AMBIENT             0xA010
        #define MAT_DIFFUSE             0xA020
        #define MAT_SPECULAR            0xA030
        #define MAT_TEXMAP              0xA200
        #define MAT_MAPNAME             0xA300

    #define EDIT_OBJECT                 0x4000
        #define OBJ_TRIMESH             0x4100
            #define TRI_VERTEXL         0x4110
            #define TRI_FACEL1          0x4120
            #define TRI_MATERIAL        0x4130
            #define TRI_MAPPINGCOORS    0x4140
            #define TRI_LOCAL           0x4160
        #define OBJ_LIGHT               0x4600
        #define OBJ_CAMERA              0x4700

    #define KEYF3DS                     0xB000
        #define KEYFRAME_HEADER             0xB00A
        #define KEYFRAME_START_AND_END      0xB008
        #define KEYFRAME_MESH_INFO          0xB002
            #define KEYFRAME_OBJECT_NAME        0xB010 
            #define KEYFRAME_INSTANCE_NAME      0xB011
            #define PIVOT                       0xB013 
            #define POSITION_TRACK_TAG          0xB020 
            #define ROTATION_TRACK_TAG          0xB021 
            #define SCALE_TRACK_TAG             0xB022
            #define KEYFRAME_NODEID             0xB030


    // color chuncks
    #define COL_RGB                     0x0010
    #define COL_TRU                     0x0011
    #define COL_UNK                     0x0013

Cuando llegamos a leer el identificador OBJ_TRIMESH leemos la información sobre una geometría, puede haber muchas en el fichero, leyendo primero el número de vértices (cuando encontramos el identificador TRI_VERTEXL) y posteriormente (cuando encontramos el identificador TRI_FACEL1) leemos el orden en que se recorren los vértices.


    ///////////////////////////////////////////////////////////////////////////////
    ///     ReadMesh: Lee una geometría 3DS de un bloque de datos.
    ///
    ///     @param  unsigned int lenChunck: Tamaño en bytes del bloque de datos.
    ///     @param  Mesh *pMesh: Geometría creada.
    ///
    ///     @return  int: Tamaño del bytes del bloque.
    ///////////////////////////////////////////////////////////////////////////////
    int C3DSLoader::ReadMesh(unsigned int lenChunck, Mesh *pMesh)
    {
        int i,f;
        unsigned int bytesRead= 0;
        unsigned short numberElements;
        unsigned short faceFlags;
        unsigned short iVertex, numFaces;
        long beginChunckPos;

        beginChunckPos= ftell(m_pFile);

        do
        {
            ReadChunck();

            switch (m_Chunck.id)
            {
                case TRI_VERTEXL:

                    fread (&numberElements, sizeof (unsigned short), 1, m_pFile);

                    pMesh->numVertex= (unsigned int)numberElements;
                    pMesh->pVertex= new Vertex[pMesh->numVertex];

                    memset(pMesh->pVertex,0,pMesh->numVertex*sizeof(Vertex));

                    for (i=0; inumVertex; i++)
                    {
                        fread (&pMesh->pVertex[i].x, sizeof(float), 1, m_pFile);
                        fread (&pMesh->pVertex[i].z, sizeof(float), 1, m_pFile);
                        fread (&pMesh->pVertex[i].y, sizeof(float), 1, m_pFile);
                        pMesh->pVertex[i].z= -pMesh->pVertex[i].z;
                    }
                    break;

                case TRI_FACEL1:

                    fread (&numFaces, sizeof (unsigned short), 1, m_pFile);

                    pMesh->numIndexes= 3*numFaces;
                    pMesh->pIndexes= new unsigned int[pMesh->numIndexes];

                    memset(pMesh->pIndexes,0,pMesh->numIndexes*sizeof(unsigned int));

                    i= 0;

                    for (f=0; fsizeof (unsigned short), 1, m_pFile);
                        pMesh->pIndexes[i++]= iVertex;

                        fread (&iVertex, sizeof (unsigned short), 1, m_pFile);
                        pMesh->pIndexes[i++]= iVertex;

                        fread (&iVertex, sizeof (unsigned short), 1, m_pFile);
                        pMesh->pIndexes[i++]= iVertex;

                        fread (&faceFlags, sizeof (unsigned short), 1, m_pFile);
                    }
                    break;

                default:
                    SkipChunck();
                    break;
            }
        }
        while ((ftell(m_pFile) - beginChunckPos) < (lenChunck-SIZE_OF_CHUCK-1));

        return lenChunck;
    }

Para renderizar una geometría sólo hay que recorrer los vértices en el orden indicado por los índices.


    ///////////////////////////////////////////////////////////////////////////////
    ///     Render: Dibuja el modelo 3DS.
    ///
    ///     @param   nada
    ///
    ///     @return  nada
    ///////////////////////////////////////////////////////////////////////////////
    void C3DSLoader::Render()
    {
        MeshList::iterator itMesh;
        Mesh * pMesh= NULL;

        for (itMesh= m_MeshList.begin(); itMesh!=m_MeshList.end(); itMesh++)
        {
            pMesh= itMesh->second;
            if (pMesh)
            {
                IVideoDriver::GetVideoDriver()->RenderIndexedVertex( eTriangleList,
                                                                    (BYTE*)pMesh->pVertex, pMesh->numVertex,
                                                                    sizeof(Vertex), pMesh->pIndexes,
                                                                    0, pMesh->numIndexes,
                                                                    eFVF_XYZ|eFVF_NORMAL );
            }
        }
    }

Definimos también la clase C3DSModel como un objeto 3D representado por una geometría 3DS. Hacemos esto, como norma general de diseño, en la que separamos el objeto 3D de su representación. Esto es así, porque pueden existir diferentes objetos 3D con la misma representación.

La aplicación principal, finalmente crea un objeto de la clase C3DSLoader, carga el modelo y se lo asigna al objeto de la clase C3DSModel.


    ///////////////////////////////////////////////////////////////////////////////
    ///     CreateSceneObjects: Crea los objetos que componen la escena 3D.
    ///
    ///     @param  nada
    ///
    ///     @return  nada
    ///////////////////////////////////////////////////////////////////////////////
    void GraphicApplication::CreateSceneObjects()
    {
    // ********************************* NEW **************************************
        C3DSModel *pModel= NULL;
        char pathFile[256], fileName[256];

    #ifdef _MSC_VER
        sprintf( pathFile, "../../models/%s", MODEL_NAME );
    #else
        sprintf( pathFile, "../../../models/%s", MODEL_NAME );
    #endif
        sprintf( fileName, "%s.3ds", MODEL_NAME );

        if ( m_3DSMesh.Load( pathFile, fileName ) == -1 )
            System::ShowMessage("Error al cargar el fichero: ",fileName,true);

        ObjectList::iterator itObject= m_ObjectList.find( std::string(fileName) );

        // Si no se encuentra el modelo     
        if ( itObject == m_ObjectList.end() )
        {
            pModel= new C3DSModel( fileName );
            if ( pModel )
            {
                pModel->SetMesh( &m_3DSMesh );

                // **************************************************************************************************************************
                // NOTA:
                // La conversión una clase derivada a una clase-base (upcast) se resuelve en tiempo de compilación -> static_cast < T > (ptr)
                // Si fuese una conversión de una clase-base a una derivada (downcast) se resuelve en tiempo de ejecución -> dynamic_cast< T > (ptr)
                // **************************************************************************************************************************
                m_ObjectList[ std::string(fileName) ]= static_cast( pModel );
            }
        }
    // ****************************************************************************
    }

Para renderiza el objeto, sólo hay que llamar al método Render() de la geometría del objeto que es la que sabe cómo debe renderizarse.


    ///////////////////////////////////////////////////////////////////////////////
    ///     RenderSceneObjects: Renderiza los objetos que componen la escena 3D.
    ///
    ///     @param  nada
    ///
    ///     @return  nada
    ///////////////////////////////////////////////////////////////////////////////
    void GraphicApplication::RenderSceneObjects()
    {
    // ********************************* NEW **************************************
        C3DSModel *pModel;
        C3DSLoader *pMesh;
        ObjectList::iterator itObj;

        for ( itObj=m_ObjectList.begin(); itObj!=m_ObjectList.end(); ++itObj )
        {
            if ( itObj->second )
            {
                // *****************************************************************************************************************
                // NOTA:
                // La conversión es de una clase-base a una derivada y se resuelve en tiempo de ejecución -> dynamic_cast< T > (ptr)
                // Para poder usar dynamic_cast en Visual C, hay que activar la opción RTTI.
                // Menú: Project -> Settings... -> Pestaña C/C++ -> Category: C++ Language, marcar "Enable Run-Time Type Information (RTTI)".
                // *****************************************************************************************************************
                pModel= dynamic_cast( itObj->second );

                pMesh= pModel->GetMesh();
                if ( pMesh )
                    pMesh->Render();
            }
        } // end for

    // ****************************************************************************
    }

Para más información sobre el formato: The Unofficial 3DStudio 3DS File Format V1.0 By Jeff Lewis

Podéis descargaros el ejemplo: tutorial-17.zip



El modelo 747.3ds no lo he diseñado yo, no sirvo para diseñador 3D, lo que no recuerdo es la página donde lo descargué.

Compartir en Facebook



Este artículo proviene de Programacion Grafica: Desarrollo 2D/3D con C++ y DirectX/OpenGL/GLUT/SDL - Windows/Linux
http://www.programaciongrafica.com

La dirección de esta noticia es:
http://www.programaciongrafica.com/modules.php?name=News&file=article&sid=18