To be a senior software engineer【4】(QT+openGL)

本文介绍如何将MilkShape3D模型导入到QT+OpenGL程序中,包括加载模型文件、解析文件结构和绘制模型等内容。文章还提供了解决模型加载后不可见问题的建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

4.从milkshape3D导入场景
   前两天突然就生病了。打完针输完液庆幸只是国产的感冒。看来要加强运动啦。这一节我们将会在上一节

的基础上,研究构建我们三维场景的更高效途径。首先,让我们回归一下之前我们都完成了哪些:
1.建立了一个三维场景
2.用自定义文件及数据格式的方式构建了我们需要的简单对象(立方体,贝塞尔曲面)
3.在以上的建模及编程中,我们用到了诸如显示列表的一些东西

   这一节中,我们将研究怎样将milkshape3D的模型场景加载到我们的程序中,这样,我们就可以在程序外部

编辑我们的场景,然后把它们加载进来。不过,如果是一些需要时刻改变的对象,比如我们的汽车,最好还是

单独加载并加以控制的代码。以下的代码都是基于QT+openGL实现的,有任何疑问可以给我留言,我会尽快回

复。
   对于milkshape3D模型场景的导入,我们首先需要明白几个概念。
1.milkshape3D是什么。简单的说,它就是和大家熟知的3DMAX相似的一种3D建模软件,存储的文件格式为 

xx.ms3d(xx代表文件名)。我们在实际的文件导入中,需要将它以2进制的形式打开,并将里面的信息存放进 

我们事先定义好的结构体中

2.需要哪些结构体。我们所用到的结构体都是用来存储ms3d文件信息的,它们包括下面列出的元素
  MS3DHeader     /*包含ms3d文件的版本信息*、
  MS3DVertex     /*顶点信息*/
  MS3DMaterial   /*材质(纹理贴图等)信息*/
  MS3DTriangle   /*绘制三角形信息*/
  MS3DJoint      /*节点(骨骼)信息*/
  MS3DKeyframe   /*关键窗口*/
其中,MS3DJoint和MS3DKeyframe我们这一节中用不到,不过我们还是给出他们的定义。
下面的结构体是存放上面结构体解析后的信息
  Mesh   /*网格*/
  Material  /*材质*/
  Triangle  /*三角形*/
  Vertex   /*定点*/
关于3D建模的知识,很抱歉,我只是一个门外汉,所以无法跟大家解释我们的三维图形是如何一步一步画出来

的,不过我知道他们的步骤,并按部就班的把信息解析出来,然后再画到我们的openGL中。

3.特别强调。关于以上结构体的定义和我们的实际编程,我们在解析2进制文件的时候需要用到字节对齐。不

清楚的朋友请自行补上,我们这个用的是1字节对齐,即有如下形式:
# pragma pack( 1 )
# define PACK_STRUCT
PACK_STRUCT为我们结构体的别名

对于QT+linux的环境,我们需要作两个变量定义
typedef unsigned char  byte;
typedef unsigned short word;

明白了这些概念后,就是按部就班的解析并存储信息了
1.loadModelData
bool scene::loadModelData( const char *filename )
{
        ifstream inputFile( filename, ios::in | ios::binary);
        if(inputFile.fail())
            return false;

 //以二进制的方式打开文件,如果失败则返回

        inputFile.seekg( 0, ios::end );
        long fileSize = inputFile.tellg();
        inputFile.seekg( 0, ios::beg );

        byte *pBuffer = new byte[fileSize];
        inputFile.read( (char*)pBuffer, fileSize );
        inputFile.close();

 //分配一个内存,载入文件,并关闭文件

        const byte *pPtr = pBuffer;
        MS3DHeader *pHeader = ( MS3DHeader* )pPtr;
        pPtr += sizeof( MS3DHeader );

        if ( strncmp( pHeader->m_ID, "MS3D000000", 10 ) != 0 )
        {       printf("不是一个有效的MS3D文件/n");
                return false; // 如果不是一个有效的MS3D文件则返回
            }

        if ( (pHeader->m_version < 3 )|| (pHeader->m_version > 4 ))
        {
            printf("不能支持这种版本的文件,%d/n",pHeader->m_version);
                return false; // 如果不能支持这种版本的文件,则返回失败
            }
        printf("支持这种版本的文件,Version=%d/n",pHeader->m_version);
 //上面的文件读取文件头

        int nVertices = *( word* )pPtr;
        m_numVertices = nVertices;
        m_pVertices = new Vertex[nVertices];
        pPtr += sizeof( word );

        int i;
        for ( i = 0; i < nVertices; i++ )
        {

                MS3DVertex *pVertex = ( MS3DVertex* )pPtr;
                m_pVertices[i].m_boneID = pVertex->m_boneID;
                memcpy( m_pVertices[i].m_location, pVertex->m_vertex, sizeof( float )*3 );
                pPtr += sizeof( MS3DVertex );
        }


 //上面的代码读取顶点数据


        int nTriangles = *( word* )pPtr;
        m_numTriangles = nTriangles;
        m_pTriangles = new Triangle[nTriangles];
        pPtr += sizeof( word );

        for ( i = 0; i < nTriangles; i++ )
        {
                MS3DTriangle *pTriangle = ( MS3DTriangle* )pPtr;
                int vertexIndices[3] = { pTriangle->m_vertexIndices[0], pTriangle-

>m_vertexIndices[1], pTriangle->m_vertexIndices[2] };
                float t[3] = { 1.0f-pTriangle->m_t[0], 1.0f-pTriangle->m_t[1], 1.0f-pTriangle-

>m_t[2] };
                memcpy( m_pTriangles[i].m_vertexNormals, pTriangle->m_vertexNormals, sizeof(

float )*3*3 );
                memcpy( m_pTriangles[i].m_s, pTriangle->m_s, sizeof( float )*3 );
                memcpy( m_pTriangles[i].m_t, t, sizeof( float )*3 );
                memcpy( m_pTriangles[i].m_vertexIndices, vertexIndices, sizeof( int )*3 );
                pPtr += sizeof( MS3DTriangle );
        }

 //上面的代码用来读取三角形信息,因为MS3D使用窗口坐标系而OpenGL使用笛卡儿坐标系,所以需要反转每

个顶点Y方向的纹理坐标


        int nGroups = *( word* )pPtr;
        m_numMeshes = nGroups;
        m_pMeshes = new Mesh[nGroups];
        pPtr += sizeof( word );
        for ( i = 0; i < nGroups; i++ )
        {
                pPtr += sizeof( byte );
                pPtr += 32;

                word nTriangles = *( word* )pPtr;
                pPtr += sizeof( word );
                int *pTriangleIndices = new int[nTriangles];
                for ( int j = 0; j < nTriangles; j++ )
                {
                        pTriangleIndices[j] = *( word* )pPtr;
                        pPtr += sizeof( word );
                }

                char materialIndex = *( char* )pPtr;
                pPtr += sizeof( char );

                m_pMeshes[i].m_materialIndex = materialIndex;
                m_pMeshes[i].m_numTriangles = nTriangles;
                m_pMeshes[i].m_pTriangleIndices = pTriangleIndices;
        }


 //上面的代码填充网格结构


        int nMaterials = *( word* )pPtr;
        m_numMaterials = nMaterials;
        m_pMaterials = new Material[nMaterials];
        pPtr += sizeof( word );
        for ( i = 0; i < nMaterials; i++ )
        {
                MS3DMaterial *pMaterial = ( MS3DMaterial* )pPtr;
                memcpy( m_pMaterials[i].m_ambient, pMaterial->m_ambient, sizeof( float )*4 );
                memcpy( m_pMaterials[i].m_diffuse, pMaterial->m_diffuse, sizeof( float )*4 );
                memcpy( m_pMaterials[i].m_specular, pMaterial->m_specular, sizeof( float )*4 );
                memcpy( m_pMaterials[i].m_emissive, pMaterial->m_emissive, sizeof( float )*4 );
                m_pMaterials[i].m_shininess = pMaterial->m_shininess;
                m_pMaterials[i].m_pTextureFilename = new char[strlen( pMaterial->m_texture )+1];
                strcpy( m_pMaterials[i].m_pTextureFilename, pMaterial->m_texture );
                pPtr += sizeof( MS3DMaterial );
        }

        reloadTextures();


// 上面的代码加载纹理数据


        delete[] pBuffer;

        return true;
}

2.drawModel
void scene::drawModel()
{

   glLoadIdentity();

   glTranslatef( 0.0,  0.0, -4.0 );

//gluLookAt( 75, 75, 75, 0, 0, 0, 0, 1, 0 );  // (3) Eye Postion (3) Center Point (3) Y-Axis    

                                                                                  Up Vector
        // 按网格分组绘制
        for ( int i = 0; i < m_numMeshes; i++ )
        {

                int materialIndex = m_pMeshes[i].m_materialIndex;
                if ( materialIndex >= 0 )
                {
                        printf("materialIndex > 0/n");
                        glMaterialfv( GL_FRONT, GL_AMBIENT, m_pMaterials

[materialIndex].m_ambient );
                        glMaterialfv( GL_FRONT, GL_DIFFUSE, m_pMaterials

[materialIndex].m_diffuse );
                        glMaterialfv( GL_FRONT, GL_SPECULAR, m_pMaterials

[materialIndex].m_specular );
                        glMaterialfv( GL_FRONT, GL_EMISSION, m_pMaterials

[materialIndex].m_emissive );
                        glMaterialf( GL_FRONT, GL_SHININESS, m_pMaterials

[materialIndex].m_shininess );

                        if ( m_pMaterials[materialIndex].m_texture > 0 )
                        {
                                printf("m_pMaterials[materialIndex].m_texture > 0/n");
                                glBindTexture( GL_TEXTURE_2D, m_pMaterials

[materialIndex].m_texture );
                                glEnable( GL_TEXTURE_2D );
                        }
                        else
                                glEnable( GL_TEXTURE_2D );//disable instead
                }
                else
                {
                        glEnable( GL_TEXTURE_2D );//disable instead
                }

                glBegin( GL_TRIANGLES );
                {
                       for ( int j = 0; j < m_pMeshes[i].m_numTriangles; j++ )
                        {
                                int triangleIndex = m_pMeshes[i].m_pTriangleIndices[j];
                                const Triangle* pTri = &m_pTriangles[triangleIndex];

                                for ( int k = 0; k < 3; k++ )
                                {
                                        int index = pTri->m_vertexIndices[k];

                                        glNormal3fv( pTri->m_vertexNormals[k] );
                                        glTexCoord2f( pTri->m_s[k], pTri->m_t[k] );
                                        glVertex3fv( m_pVertices[index].m_location );
                                }
                        }
                }
                glEnd();
        }

                glEnable( GL_TEXTURE_2D );

      
}

上面的代码我并没有将纹理贴图部分加上,添加的方法如果你阅读过前几节应该不难做到。整个过程中最容易

出问题的地方可能在模型加载成功后的不可见上。这里特别强调,milkshape3D中的坐标系统与QT+openGL里的

并不那么一致,所以可能你加载的模型场景太大所以显示不出来,或者整个屏幕都显示一种颜色。这里给出一

种简单的解决办法(P.S.很抱歉初学的我还没找到正规的解决办法):大家翻回到前几节可能会注意到,我们绘

制的图形的大小,即我们可是屏幕的大小在-2到+2之间,所以我们在加载milkshape3D模型前需要对其进行缩

放以适应我们的QT。当然,也希望对此有研究成果的朋友不吝赐教,告诉我该怎么办。我的邮箱地址是

chuckgao.cg@gmail.com ,欢迎大家跟我联系。         

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值