Tutorial 19 - Carga de Modelos 3DS (Textura)
Fecha Miércoles, 07 noviembre a las 12:01:07
Tema DirectX/Opengl


Tercera y última entrega sobre la carga de Modelos 3DS. En este caso añadiremos la carga de texturas.



Tenemos que añadir algunas modificaciones a la definición del vértice y el material para añadir la información para trabajar con texturas.


    ///////////////////////////////////////////////////////////////////////////////
    /// struct  Vertex: Estructura del vértice.
    ///
    /// rief   
    /// author  Antonio Lucas Moreno version 1.02 date 30/08/2005 9:24:19
    ///////////////////////////////////////////////////////////////////////////////
    struct Vertex
    {
        float           x, y, z;            ///< Coordenadas del vértice.
        float           nx, ny, nz;         ///< Normal del vértice.
    // ********************************* NEW **************************************
        float           su, tv;             ///< Coordenadas de textura.
    // ****************************************************************************
    };

/////////////////////////////////////////////////////////////////////////////// /// struct MATERIAL Estructura que define las propiedades del material. /// /// rief /// author Antonio Lucas Moreno version 1.0 date 28/09/2005 9:26:27 /////////////////////////////////////////////////////////////////////////////// struct Material { char name[256]; ///< Nombre del material. fColorRGBA ambient; ///< Color ambiente(RGBA). fColorRGBA diffuse; ///< Color difuso(RGBAA). fColorRGBA specular; ///< Color especular(RGBA). fColorRGBA emissive; ///< Color emisivo(RGBA). // ********************************* NEW ************************************** Texture texture; ///< Textura asociada al material. // **************************************************************************** };

En la clase C3DSLoader añadimos la ruta donde se almacena el modelo para poder cargar las texturas posteriormente.


    // ********************************* NEW **************************************
        char            m_PathName[256];            ///< Directorio donde se almacena el modelo.
    // ****************************************************************************

Ahora tenemos que leer más información sobre la geometría, en el método ReadMesh(). En este caso, las coordenadas de textura.


        // Coordenadas de textura
        case TRI_MAPPINGCOORS:

            fread (&numberElements, sizeof (unsigned short), 1, m_pFile);
            for (i=0; i// Las coordenadas de textura están almacenadas como en OpenGL:
                //
                //      t
                //      ^
                //      |
                //      +--> s
                fread (&pMesh->pVertex[i].su, sizeof (float), 1, m_pFile);
                fread (&pMesh->pVertex[i].tv, sizeof (float), 1, m_pFile);
            }
            break;

Cuando leamos el material, en le método ReadMaterial(), hay que crear la textura así como asignar el material (con su textura) a cada cara.


        // Nombre del fichero de textura.
        case MAT_MAPNAME:

            if ( ReadString(textureName,sizeof(textureName)) > 0 )
            {
                // Cargamos la textura
                sprintf( textureFileName,"%s/%s", m_PathName, textureName );
                ulColorRGBA colorKey;
                colorKey.rgba=0xffffffff;
                IVideoDriver::GetVideoDriver()->LoadTexture( textureFileName, pMaterial->texture, colorKey );
            }
            break;

El método para renderizar una geometría añade información para renderizar con texturas.


    ///////////////////////////////////////////////////////////////////////////////
    ///     Render: Dibuja el modelo 3DS.
    ///
    ///     @param   nada
    ///
    ///     @return  nada
    ///////////////////////////////////////////////////////////////////////////////
    void C3DSLoader::Render()
    {
        MeshList::iterator itMesh;
        Mesh * pMesh= NULL;
        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)
            {
                // 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 );

    // ********************************* NEW **************************************
                        pVideoDriver->SetTextureRenderStatus( eMinfilter, eLinearFilter );
                        pVideoDriver->SetTextureRenderStatus( eMagfilter, eLinearFilter );
                        pVideoDriver->SetTexture( pLastMaterial->texture );
    // ****************************************************************************

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

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

                    endIndex += 3;

                } // end for

                // Falta renderizar el último material
                if ( endIndex > startIndex )
                {
                    pVideoDriver->SetMaterial( *pLastMaterial );

    // ********************************* NEW **************************************
                    pVideoDriver->SetTextureRenderStatus( eMinfilter, eLinearFilter );
                    pVideoDriver->SetTextureRenderStatus( eMagfilter, eLinearFilter );
                    pVideoDriver->SetTexture( pLastMaterial->texture );
    // ****************************************************************************

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

Se ha hecho un cambio importante en la clase GLApplication. La función de carga de texturas se ha definido en una nueva clase Tools como un método estático de forma que pueda ser llamada desde cualquier lugar del código sin necesidad de crear ningún objeto de la clase.

Definimos variables miembro para cada geometría que vamos a cargar.


    // ********************************* NEW **************************************
        C3DSLoader      m_3DSMeshFalcon;    ///< Geometría del modelo 3DS
        C3DSLoader      m_3DSMeshTie;       ///< Geometría del modelo 3DS
        C3DSLoader      m_3DSMeshDestructor;///< Geometría del modelo 3DS
    // ****************************************************************************

Estas geometrías, serán cargadas en el método CreateSceneObjects() y asociadas a cada objeto 3D creado. Es importante ver como se carga una sóla geometría de un caza Tie, pero se crean tantos cazas como queramos, pero la geometría es la misma.


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

        // ---------------- *** Cargamos el Halcón Milenario *** ------------------
    #ifdef _MSC_VER
        sprintf( pathFile, "../../models/%s", "falcon" );
    #else
        sprintf( pathFile, "../../../models/%s", "falcon" );
    #endif
        sprintf( fileName, "%s.3ds", "falcon" );

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

        pModel= new C3DSModel( "FALCON" );
        if ( pModel )
        {
            pModel->SetMesh( &m_3DSMeshFalcon );

            // Los modelos de este tutorial están orientados hacia el eje Y positivo.
            // Lo orientamos hacia la Z positiva.
            pModel->Roll( 90.0f );  // Giro en Z
            pModel->Yaw ( 90.0f );  // Giro en Y
            pModel->SetPosition( Vector3(0,0,0) );

            // **************************************************************************************************************************
            // 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( pModel->GetName() ) ]= static_cast( pModel );
        }

        // -------------------- *** Cargamos un caza Tie *** ----------------------
    #ifdef _MSC_VER
        sprintf( pathFile, "../../models/%s", "tie-fighter" );
    #else
        sprintf( pathFile, "../../../models/%s", "tie-fighter" );
    #endif
        sprintf( fileName, "%s.3ds", "tie-fighter" );

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

        pModel= new C3DSModel( "TIE-01" );
        if ( pModel )
        {
            pModel->SetMesh( &m_3DSMeshTie );

            // Los modelos de este tutorial están orientados hacia el eje Y positivo.
            // Lo orientamos hacia la Z positiva.
            pModel->Roll( 90.0f );  // Giro en Z
            pModel->Yaw ( 90.0f );  // Giro en Y
            pModel->SetPosition( Vector3(-20,0,-5) );

            m_ObjectList[ std::string( pModel->GetName() ) ]= static_cast( pModel );
        }

        // Fijémonos como creamos un nuevo objeto 3D que referencia a la misma geometría que el anterior.
        // La geometría sólo se carga y genera una vez.
        pModel= new C3DSModel( "TIE-02" );
        if ( pModel )
        {
            pModel->SetMesh( &m_3DSMeshTie );

            // Los modelos de este tutorial están orientados hacia el eje Y positivo.
            // Lo orientamos hacia la Z positiva.
            pModel->Roll( 90.0f );  // Giro en Z
            pModel->Yaw ( 90.0f );  // Giro en Y
            pModel->SetPosition( Vector3(20,0,-5) );

            m_ObjectList[ std::string( pModel->GetName() ) ]= static_cast( pModel );
        }

        // -------------- *** Cargamos el Destructor Imperial *** ----------------
    #ifdef _MSC_VER
        sprintf( pathFile, "../../models/%s", "destroyer" );
    #else
        sprintf( pathFile, "../../../models/%s", "destroyer" );
    #endif
        sprintf( fileName, "%s.3ds", "destroyer" );

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

        pModel= new C3DSModel( "DESTROYER" );
        if ( pModel )
        {
            pModel->SetMesh( &m_3DSMeshDestructor );

            // Los modelos de este tutorial están orientados hacia el eje Y positivo.
            // Lo orientamos hacia la Z positiva.
            pModel->Roll( 90.0f );  // Giro en Z
            pModel->Yaw ( 90.0f );  // Giro en Y
            pModel->SetPosition( Vector3(0,-100,-100) );

            m_ObjectList[ std::string( pModel->GetName() ) ]= static_cast( pModel );
        }

    }

El método en el que se renderizan los objetos, RenderSceneObjects(), cambia de forma que aplica las transformaciones realizadas sobre las naves, ya sea posición u orientación, simplemente transforma del espacio local al global


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

        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 );

    // ********************************* NEW **************************************
                // Transformamos del espacio local al espacio global.
                transform.Translate( pModel->GetPosition() );
                worldTransform= pModel->GetLocalMatrix() * transform;
                m_pVideoDriver->SetTransform( worldTransform );
    // ****************************************************************************

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

Si en DirectX os aparecen unas líneas extrañas que van al infitino, comentad el defin __CONSOLE__ en el fichero win-main.cpp. Por alguna razón que desconozco hace eso si la aplicación está en modo consola.

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



Los modelos los obtuve algún día de la red. No os puedo decir dónde pues no me acuerdo.

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=20