本节课实现了加载Milkshape3D建模生成的文件,本课的大部分内容主要是分析Milkshape3D的模型文件格式
本课中的读取文件的内容大部分来自NeHe课程中的代码:
主要加载文件的代码在以下函数之中:
virtual bool loadModelData( const char *filename )
编译运行程序:
附:本课源码(源码中可能存在错误和不足,仅供参考)
#include "../osgNeHe.h" #include <iostream> #include <fstream> #include <QtCore/QTimer> #include <QtGui/QApplication> #include <QtGui/QVBoxLayout> #include <osgViewer/Viewer> #include <osgDB/ReadFile> #include <osgQt/GraphicsWindowQt> #include <osg/MatrixTransform> #include <osg/Texture2D> #include <osg/Material> #include <osg/AnimationPath> using namespace std; ////////////////////////////////////////////////////////////////////////// ///////////////File From NeHe Tutorial////////////////////////// ////////////////////////////////////////////////////////////////////////// osg::Texture2D * LoadGLTexture( const char *filename ) { osg::Image *textureImage = osgDB::readImageFile(filename); osg::Texture2D *texture2D = new osg::Texture2D; texture2D->setImage(textureImage); texture2D->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); texture2D->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); return texture2D; } class Model { public: struct Mesh { int m_materialIndex; int m_numTriangles; int *m_pTriangleIndices; }; struct Material { float m_ambient[4], m_diffuse[4], m_specular[4], m_emissive[4]; float m_shininess; osg::Texture2D *m_texture; char *m_pTextureFilename; }; struct Triangle { float m_vertexNormals[3][3]; float m_s[3], m_t[3]; int m_vertexIndices[3]; }; struct Vertex { char m_boneID; float m_location[3]; }; public: Model() { m_numMeshes = 0; m_pMeshes = NULL; m_numMaterials = 0; m_pMaterials = NULL; m_numTriangles = 0; m_pTriangles = NULL; m_numVertices = 0; m_pVertices = NULL; } virtual ~Model() { int i; for ( i = 0; i < m_numMeshes; i++ ) delete[] m_pMeshes[i].m_pTriangleIndices; for ( i = 0; i < m_numMaterials; i++ ) delete[] m_pMaterials[i].m_pTextureFilename; m_numMeshes = 0; if ( m_pMeshes != NULL ) { delete[] m_pMeshes; m_pMeshes = NULL; } m_numMaterials = 0; if ( m_pMaterials != NULL ) { delete[] m_pMaterials; m_pMaterials = NULL; } m_numTriangles = 0; if ( m_pTriangles != NULL ) { delete[] m_pTriangles; m_pTriangles = NULL; } m_numVertices = 0; if ( m_pVertices != NULL ) { delete[] m_pVertices; m_pVertices = NULL; } } virtual bool loadModelData( const char *filename ) = 0; osg::Geode* drawGeode() { osg::Geode *geode = new osg::Geode; geode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); for ( int i = 0; i < m_numMeshes; i++ ) { osg::Geometry *geometry = new osg::Geometry; osg::Vec3Array *vertexArray = new osg::Vec3Array; osg::Vec2Array *textureArray = new osg::Vec2Array; osg::Vec3Array *normalArray = new osg::Vec3Array; int materialIndex = m_pMeshes[i].m_materialIndex; if ( materialIndex >= 0 ) { osg::Material *material = new osg::Material; material->setAmbient(osg::Material::FRONT,osg::Vec4(m_pMaterials[materialIndex].m_ambient[0], m_pMaterials[materialIndex].m_ambient[1],m_pMaterials[materialIndex].m_ambient[2],m_pMaterials[materialIndex].m_ambient[3])); material->setDiffuse(osg::Material::FRONT,osg::Vec4(m_pMaterials[materialIndex].m_diffuse[0],m_pMaterials[materialIndex].m_diffuse[1],m_pMaterials[materialIndex].m_diffuse[2],m_pMaterials[materialIndex].m_diffuse[3])); material->setEmission(osg::Material::FRONT,osg::Vec4(m_pMaterials[materialIndex].m_emissive[0],m_pMaterials[materialIndex].m_emissive[1],m_pMaterials[materialIndex].m_emissive[2],m_pMaterials[materialIndex].m_emissive[3])); material->setSpecular(osg::Material::FRONT,osg::Vec4(m_pMaterials[materialIndex].m_specular[0],m_pMaterials[materialIndex].m_specular[1],m_pMaterials[materialIndex].m_specular[2],m_pMaterials[materialIndex].m_specular[3])); material->setShininess(osg::Material::FRONT, m_pMaterials[materialIndex].m_shininess); geometry->getOrCreateStateSet()->setAttributeAndModes(material); if ( m_pMaterials[materialIndex].m_texture > 0 ) { geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, m_pMaterials[materialIndex].m_texture); } } 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]; normalArray->push_back(osg::Vec3(pTri->m_vertexNormals[k][0], pTri->m_vertexNormals[k][1], pTri->m_vertexNormals[k][2])); textureArray->push_back(osg::Vec2(pTri->m_s[k], pTri->m_t[k])); vertexArray->push_back(osg::Vec3(m_pVertices[index].m_location[0], m_pVertices[index].m_location[1], m_pVertices[index].m_location[2])); } } geometry->setVertexArray(vertexArray); geometry->setNormalArray(normalArray, osg::Array::BIND_PER_VERTEX); geometry->setTexCoordArray(0, textureArray, osg::Array::BIND_PER_VERTEX); geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, vertexArray->size())); geode->addDrawable(geometry); } return geode; } void reloadTextures() { for ( int i = 0; i < m_numMaterials; i++ ) if ( strlen( m_pMaterials[i].m_pTextureFilename ) > 0 ) m_pMaterials[i].m_texture = LoadGLTexture( m_pMaterials[i].m_pTextureFilename ); else m_pMaterials[i].m_texture = 0; } protected: int m_numMeshes; Mesh *m_pMeshes; int m_numMaterials; Material *m_pMaterials; int m_numTriangles; Triangle *m_pTriangles; int m_numVertices; Vertex *m_pVertices; }; ////////////////////////////////////////////////////////////////////////// #ifdef _MSC_VER # pragma pack( push, packing ) # pragma pack( 1 ) # define PACK_STRUCT #elif defined( __GNUC__ ) # define PACK_STRUCT __attribute__((packed)) #else # error you must byte-align these structures with the appropriate compiler directives #endif typedef unsigned short word; // File header struct MS3DHeader { char m_ID[10]; int m_version; } PACK_STRUCT; // Vertex information struct MS3DVertex { char m_flags; float m_vertex[3]; char m_boneID; char m_refCount; } PACK_STRUCT; // Triangle information struct MS3DTriangle { word m_flags; word m_vertexIndices[3]; float m_vertexNormals[3][3]; float m_s[3], m_t[3]; char m_smoothingGroup; char m_groupIndex; } PACK_STRUCT; // Material information struct MS3DMaterial { char m_name[32]; float m_ambient[4]; float m_diffuse[4]; float m_specular[4]; float m_emissive[4]; float m_shininess; // 0.0f - 128.0f float m_transparency; // 0.0f - 1.0f char m_mode; // 0, 1, 2 is unused now char m_texture[128]; char m_alphamap[128]; } PACK_STRUCT; // Joint information struct MS3DJoint { char m_flags; char m_name[32]; char m_parentName[32]; float m_rotation[3]; float m_translation[3]; word m_numRotationKeyframes; word m_numTranslationKeyframes; } PACK_STRUCT; // Keyframe data struct MS3DKeyframe { float m_time; float m_parameter[3]; } PACK_STRUCT; // Default alignment #ifdef _MSC_VER # pragma pack( pop, packing ) #endif #undef PACK_STRUCT class MilkshapeModel : public Model { public: MilkshapeModel() { } virtual ~MilkshapeModel() { } virtual bool loadModelData( const char *filename ) { ifstream inputFile( filename, ios::in | ios::binary | ios::_Nocreate ); if ( inputFile.fail()) return false; inputFile.seekg( 0, ios::end ); long fileSize = inputFile.tellg(); inputFile.seekg( 0, ios::beg ); char *pBuffer = new char[fileSize]; inputFile.read( pBuffer, fileSize ); inputFile.close(); const char *pPtr = pBuffer; MS3DHeader *pHeader = ( MS3DHeader* )pPtr; pPtr += sizeof( MS3DHeader ); if ( strncmp( pHeader->m_ID, "MS3D000000", 10 ) != 0 ) return false; // "Not a valid Milkshape3D model file." if ( pHeader->m_version < 3 || pHeader->m_version > 4 ) return false; // "Unhandled file version. Only Milkshape3D Version 1.3 and 1.4 is supported." ); 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 ); } int nGroups = *( word* )pPtr; m_numMeshes = nGroups; m_pMeshes = new Mesh[nGroups]; pPtr += sizeof( word ); for ( i = 0; i < nGroups; i++ ) { pPtr += sizeof( char ); // flags pPtr += 32; // name 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; } }; class ViewerWidget : public QWidget, public osgViewer::Viewer { public: ViewerWidget(osg::Node *scene = NULL) { QWidget* renderWidget = getRenderWidget( createGraphicsWindow(0,0,100,100), scene); QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(renderWidget); layout->setContentsMargins(0, 0, 0, 1); setLayout( layout ); connect( &_timer, SIGNAL(timeout()), this, SLOT(update()) ); _timer.start( 10 ); } QWidget* getRenderWidget( osgQt::GraphicsWindowQt* gw, osg::Node* scene ) { osg::Camera* camera = this->getCamera(); camera->setGraphicsContext( gw ); const osg::GraphicsContext::Traits* traits = gw->getTraits(); camera->setClearColor( osg::Vec4(0.0, 0.0, 0.0, 1.0) ); camera->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) ); camera->setProjectionMatrixAsPerspective(45.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 0.1f, 100.0f ); camera->setViewMatrixAsLookAt(osg::Vec3d(75, 75, 75), osg::Vec3d(0, 0, 0), osg::Vec3d(0, 1, 0)); this->setSceneData( scene ); return gw->getGLWidget(); } osgQt::GraphicsWindowQt* createGraphicsWindow( int x, int y, int w, int h, const std::string& name="", bool windowDecoration=false ) { osg::DisplaySettings* ds = osg::DisplaySettings::instance().get(); osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits; traits->windowName = name; traits->windowDecoration = windowDecoration; traits->x = x; traits->y = y; traits->width = w; traits->height = h; traits->doubleBuffer = true; traits->alpha = ds->getMinimumNumAlphaBits(); traits->stencil = ds->getMinimumNumStencilBits(); traits->sampleBuffers = ds->getMultiSamples(); traits->samples = ds->getNumMultiSamples(); return new osgQt::GraphicsWindowQt(traits.get()); } virtual void paintEvent( QPaintEvent* event ) { frame(); } protected: QTimer _timer; }; osg::Node* buildScene() { Model *pModel = new MilkshapeModel(); pModel->loadModelData( "Data/model.ms3d"); pModel->reloadTextures(); osg::Group *root = new osg::Group; osg::MatrixTransform *rotYMT = new osg::MatrixTransform; rotYMT->addUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(), osg::Y_AXIS, 2.5)); rotYMT->addChild(pModel->drawGeode()); root->addChild(rotYMT); return root; } int main( int argc, char** argv ) { QApplication app(argc, argv); ViewerWidget* viewWidget = new ViewerWidget(buildScene()); viewWidget->setGeometry( 100, 100, 640, 480 ); viewWidget->show(); return app.exec(); }