Aprende a desarrollar aplicaciones gráficas 2D y 3D paso a paso.
Programacion Grafica: Desarrollo 2D/3D con C++ y DirectX/OpenGL/GLUT/SDL - Windows/Linux


  Crear una cuenta
 • Portada • Descargas • Tu Cuenta • 
Menú
 Portada
 Noticias
 Archivo de Noticias
 Temas
 Descargas
 Inicio
 Ver Nuevas
 Más Popular
 Más Votadas
 Contenido
 Contenido
 Secciones
 Enciclopedia
 Revisiones
 FAQ
 Tus Opciones
 Tu Cuenta
 Mensajes Privados
 Recomiéndenos
 Buscar
 Diarios
 Contacto
 Enlaces Web
 Inicio
 Enviar Enlace
 Ver Nuevos
 Más Popular
 Más Valorados
 Estadísticas
 Inicio
 Detalladas
 Top 10
 
Estadísticas
 
Tutorial 18 - Carga de Modelos 3DS (Sólido) 
DirectX/Opengl

En esta segunda parte sobre la carga de Modelos 3DS vermos cómo leer la información sobre los materiales.



Tenemos que redefinir nuevamente la información sobre la geometría, ya que debemos añadir más información tanto al vértice como a la gemotría (Mesh) para almacenar las caras que componen el modelo con el material asociado:


    // ********************************* NEW **************************************

    ///////////////////////////////////////////////////////////////////////////////
    /// struct   FACE Estructura del polígono que compone las geometrías.
    ///
    /// rief   
    /// author  Antonio Lucas Moreno version 1.0 date 26/08/2005 9:24:38
    ///////////////////////////////////////////////////////////////////////////////
    struct Face
    {
        unsigned int    vIndex[3];          ///< Contiene la referencia a los vértices dentro del array de vértices.
        Vector3         normal;             ///< Normal de la cara.
        Vector3         center;             ///< Centro de la cara.
        Material    *   pMaterial;          ///< Material usado.
    };

    // ****************************************************************************

    ///////////////////////////////////////////////////////////////////////////////
    /// struct   Mesh: Geometría 3D.
    ///
    /// rief   
    /// author  Antonio Lucas Moreno version 1.0 date 07/09/2007 12:33:21
    ///////////////////////////////////////////////////////////////////////////////
    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.
    // ********************************* NEW **************************************
        Face         *  pFaces;             ///< Lista de caras.
        unsigned int    numFaces;           ///< Nº de caras.
    // ****************************************************************************
    };

En la clase C3DSLoader añadimos una lista con los materias definidos en el modelo.


    typedef std::map < std::string, Material * >    MaterialList;

MaterialList m_MaterialList; ///< Lista de materiales.

Ahora tenemos que leer más información sobre la geometría, en este caso, los materiales asociados a cada cara (no cargaremos modelos con texturas hasta el siguiente tutorial).


    ///////////////////////////////////////////////////////////////////////////////
    ///     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->numFaces= numFaces;
                    pMesh->pFaces  = new Face[ numFaces ];

                    pMesh->pIndexes= new unsigned int[3*numFaces];
                    memset(pMesh->pIndexes,0,3*numFaces*sizeof(unsigned int));
                    pMesh->numIndexes= 0;

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

                        fread (&iVertex, sizeof (unsigned short), 1, m_pFile);
                        pMesh->pFaces[f].vIndex[1]            = iVertex;
                        pMesh->pIndexes[ pMesh->numIndexes++ ]= iVertex;

                        fread (&iVertex, sizeof (unsigned short), 1, m_pFile);
                        pMesh->pFaces[f].vIndex[2]            = iVertex;
                        pMesh->pIndexes[ pMesh->numIndexes++ ]= iVertex;

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

    // ********************************* NEW **************************************

                case TRI_MATERIAL:
                {
                    char materialName[64];
                    MaterialList::iterator itMat;
                    unsigned short iFace;

                    // Leemos el nombre del material
                    ReadString(materialName, sizeof(materialName));

                    itMat= m_MaterialList.find( std::string(materialName) );

                    // Leemos la asignación de materiales a las caras
                    fread(&numFaces, sizeof(unsigned short), 1, m_pFile);

                    for (i=0; isizeof(unsigned short), 1, m_pFile);
                        if (itMat != m_MaterialList.end())
                            pMesh->pFaces[iFace].pMaterial= itMat->second;
                        else
                            pMesh->pFaces[iFace].pMaterial= NULL;
                    }

                }   break;

    // ****************************************************************************

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

        return lenChunck;
    }

Definimos una nueva función para leer la información sobre un material, cada vez que nos encontremos con el identificador EDIT_MATERIAL:


    ///////////////////////////////////////////////////////////////////////////////
    ///     ReadMaterial: Lee la información sobre el material.
    ///
    ///     @param  unsigned int lenChunck: Tamaño en bytes del bloque de datos.
    ///
    ///     @return  int: Tamaño del bytes del bloque.
    ///////////////////////////////////////////////////////////////////////////////
    int C3DSLoader::ReadMaterial(unsigned int lenChunck)
    {
        unsigned int bytesRead= 0;
        long beginChunckPos;

        Material * pMaterial= new Material;
        if (!pMaterial)
            return -1;

        memset(pMaterial,0,sizeof(Material));

        beginChunckPos= ftell(m_pFile);

        do
        {
            ReadChunck();

            switch (m_Chunck.id)
            {
                // nombre del material
                case MAT_NAME:
                    ReadString(pMaterial->name, sizeof(pMaterial->name));
                    break;

                case MAT_AMBIENT:
                    // a continuación viene el chunck con el color (lo leemos)
                    ReadChunck();
                    pMaterial->ambient= ReadColor();
                    break;

                case MAT_DIFFUSE:
                    // a continuación viene el chunck con el color (lo leemos)
                    ReadChunck();
                    pMaterial->diffuse= ReadColor();
                    break;

                case MAT_SPECULAR:
                    // a continuación viene el chunck con el color (lo leemos)
                    ReadChunck();
                    pMaterial->specular= ReadColor();
                    break;

                case MAT_TEXMAP:
                    break;

                default:
                    SkipChunck();
                    break;
            }
        }
        // hasta que lleguemos al final del chunck
        while ((ftell(m_pFile) - beginChunckPos) < (lenChunck-SIZE_OF_CHUCK-1));

        if ( pMaterial )
            m_MaterialList[ std::string(pMaterial->name) ]= pMaterial;

        return lenChunck;
    }

Se define una función auxiliar, que calcula los vectores normales de cada vértice, calculando primero un vector normal a la cara y posteriormente la normal de cada vértice como la suma de las normales de las caras que comparten dicho vértice.


    ///////////////////////////////////////////////////////////////////////////////
    ///     CalculateNormals: Calcula los vectores normales de los vértices.
    ///
    ///     @param  Mesh * pMesh: Lista de triángulos/caras.
    ///
    ///     @return  int:   
    ///                     - -1  : error.
    ///                     .
    ///                     - otro: correcto.
    ///                     .
    ///////////////////////////////////////////////////////////////////////////////
    int C3DSLoader::CalculateNormals(Mesh * pMesh)
    {
        unsigned int f, i;
        Vector3 vVector1, vVector2, vNormal, vFace[3], vSum;

        // Este array almacenará el número de caras que comparten el vértice.
        unsigned int * sharedFaces;

        if (pMesh)
        {
            sharedFaces= new unsigned int[pMesh->numVertex];
            if (!sharedFaces)
                return -1;

            memset( sharedFaces,0, pMesh->numVertex * sizeof(unsigned int) );

            // Primero calculamos la normal de cada cara y vamos tomando nota de las caras
            // compartidas por cada vértice y calculando el acumulado de la normal para cada vértice.
            for (f=0; fnumFaces ; f++)
            {
                // Creamos un vector para cada vértice de la cara
                for (i=0; i<3; i++)
                {
                    vFace[i]= Vector3( pMesh->pVertex[ pMesh->pFaces[f].vIndex[i] ].x ,
                                       pMesh->pVertex[ pMesh->pFaces[f].vIndex[i] ].y ,
                                       pMesh->pVertex[ pMesh->pFaces[f].vIndex[i] ].z );
                }
                // Creamos dos vectores coplanares a la cara 
                // (recordemos que las caras se recorren en sentido antihorario).
                //
                //  v0 *------->* v2
                //        F   .
                //         .
                //        v
                //         * v1
                //
                vVector1= vFace[0] - vFace[1];
                vVector2= vFace[0] - vFace[2];

                // Multiplicamos v1*v2 y nos dará un vector perpendicular a la cara
                // (regla de la mano derecha).
                //
                //    vNormal
                //     ^
                //     |
                //     |
                //  v0 *------->* v2
                //        F   .
                //         .
                //        v
                //         * v1
                //
                vNormal= vVector1 * vVector2;

                Vector3 vCenter(0.0f,0.0f,0.0f);

                // Vamos acumulando la normal de la cara a la que pertenece el vértice
                for (i=0; i<3; i++)
                {
                    vSum.x= pMesh->pVertex[ pMesh->pFaces[f].vIndex[i] ].nx;
                    vSum.y= pMesh->pVertex[ pMesh->pFaces[f].vIndex[i] ].ny;
                    vSum.z= pMesh->pVertex[ pMesh->pFaces[f].vIndex[i] ].nz;

                    vSum= vSum + vNormal;

                    pMesh->pVertex[ pMesh->pFaces[f].vIndex[i] ].nx= vSum.x;
                    pMesh->pVertex[ pMesh->pFaces[f].vIndex[i] ].ny= vSum.y;
                    pMesh->pVertex[ pMesh->pFaces[f].vIndex[i] ].nz= vSum.z;

                    // Incrementamos el número de caras compartidas por el vértice
                    sharedFaces[ pMesh->pFaces[f].vIndex[i] ]++;

                    vCenter= vCenter + Vector3( pMesh->pVertex[ pMesh->pFaces[f].vIndex[i] ].x ,
                                                pMesh->pVertex[ pMesh->pFaces[f].vIndex[i] ].y ,
                                                pMesh->pVertex[ pMesh->pFaces[f].vIndex[i] ].z );
                }

                // Normalizamos
                vNormal.Normalize();

                // Asignamos también la normal a la cara
                pMesh->pFaces[f].normal= vNormal;

                // Fijamos el centro de la cara
                pMesh->pFaces[f].center= vCenter/3;
            }

            // Tras calcular las normales de cada cara y las acumuladas en los vértices,
            // normalizamos las de los vértices.
            for (i=0; inumVertex; i++)
            {
                vSum.x= pMesh->pVertex[i].nx;
                vSum.y= pMesh->pVertex[i].ny;
                vSum.z= pMesh->pVertex[i].nz;

                vNormal= vSum / (float)sharedFaces[i];

                vNormal.Normalize();

                pMesh->pVertex[i].nx= vNormal.x;
                pMesh->pVertex[i].ny= vNormal.y;
                pMesh->pVertex[i].nz= vNormal.z;
            }
            delete [] sharedFaces;

            return 0;
        }
        else
            return -1;
    }

El método para renderizar una geometría añade información para renderizar según el material de la cara, siempre que el material varíe, ya que cambiar de material o de textura es una operación costosa.


    ///////////////////////////////////////////////////////////////////////////////
    ///     Render: Dibuja el modelo 3DS.
    ///
    ///     @param   nada
    ///
    ///     @return  nada
    ///////////////////////////////////////////////////////////////////////////////
    void C3DSLoader::Render()
    {
        MeshList::iterator itMesh;
        Mesh * pMesh= NULL;
    // ********************************* NEW **************************************
        unsigned int i, f, startIndex, endIndex;
        Material * pLastMaterial= NULL;

        IVideoDriver *pVideoDriver= IVideoDriver::GetVideoDriver();
        if (!pVideoDriver)
            return;
    // ****************************************************************************

        for (itMesh= m_MeshList.begin(); itMesh!=m_MeshList.end(); itMesh++)
        {
            pMesh= itMesh->second;
            if (pMesh)
            {
    // ********************************* NEW **************************************

                // Renderizamos sus caras
                i            = 0;
                startIndex   = 0;
                endIndex     = 0;
                pLastMaterial= pMesh->pFaces[0].pMaterial;

                for ( f=0; fnumFaces; f++ )
                {
                    // Si cambia el material, renderizamos...
                    if ( pMesh->pFaces[f].pMaterial != pLastMaterial && endIndex > startIndex )
                    {
                        // Fijamos el material
                        pVideoDriver->SetMaterial( *pLastMaterial );

                        pVideoDriver->RenderIndexedVertex( eTriangleList,
                                                        (BYTE*)pMesh->pVertex, pMesh->numVertex,
                                                        sizeof(Vertex), pMesh->pIndexes,
                                                        startIndex, endIndex-startIndex,
                                                        eFVF_XYZ|eFVF_NORMAL );

                        startIndex   = endIndex;
                        pLastMaterial= pMesh->pFaces[f].pMaterial;
                    }

                    endIndex += 3;

                } // end for

                // Falta renderizar el último material
                if ( endIndex > startIndex )
                {
                    pVideoDriver->SetMaterial( *pLastMaterial );
                    pVideoDriver->RenderIndexedVertex( eTriangleList,
                                                    (BYTE*)pMesh->pVertex, pMesh->numVertex,
                                                    sizeof(Vertex), pMesh->pIndexes,
                                                    startIndex, endIndex-startIndex,
                                                    eFVF_XYZ|eFVF_NORMAL );
                }
    // ****************************************************************************
            }
        }
    }

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



Nota: Compartir en Facebook
Enviado el Lunes, 05 noviembre a las 15:29:14 por administrador
 
Enlaces Relacionados
· Más Acerca de DirectX/Opengl
· Noticias de administrador


Noticia más leída sobre DirectX/Opengl:
Tutorial 16 - Movimiento-3D - (Tercera Persona)

 
Votos del Artículo
Puntuación Promedio: 4.33
votos: 3


Por favor tómate un segundo y vota por este artículo:
Excelente
Muy Bueno
Bueno
Regular
Malo

 
Opciones

 Versión Imprimible  Versión Imprimible

 
"Tutorial 18 - Carga de Modelos 3DS (Sólido)" | Entrar / Crear Cuenta | 0 Comentarios
Los comentarios son propiedad de quien los envió. No somos responsables por su contenido.

No se permiten comentarios Anónimos, Regístrese por favor

Sitio potenciado por NukeProjects.Net        NukeStilo 1.0 se basa en PHP-Nuke        Theme Diseñado por NukeMods

© Antonio Lucas Moreno