天天看點

基于OpenCASCADE自制三維模組化軟體(十一)使用ASSIMP導入導出一、導入模型二、導出模型項目倉庫

文章目錄

  • 一、導入模型
  • 二、導出模型
  • 項目倉庫

一、導入模型

  1. 打開導入對話框

    首先從

    Assimp::Importer

    中擷取ASSIMP支援導入的檔案格式,然後用

    QFileDialog::getOpenFileName

    函數彈出檔案對話框。
// cmainwindow.cpp
void CMainWindow::on_actionImport_triggered()
{
    // 擷取ASSIMP支援的導入格式
    Importer t_importer;
    std::string szOut;
    t_importer.GetExtensionList(szOut); // ASSIMP支援的導入格式
    // 篩選檔案格式
    QString t_assimp=tr("ASSIMP (") +QString::fromStdString(szOut) +tr(")");
    QString all_filter;
    all_filter+=t_assimp;
    // 擷取被打開的檔案路徑
    QString filename = QFileDialog::getOpenFileName(this,tr("open file"),"D:/models",all_filter);
    
    ...
}
           

其中

Assimp::Importer

的成員函數

GetExtensionList

原型如下,通過它擷取ASSIMP支援的所有檔案擴充名的完整清單,輸出到szOut中,比如

"*.3ds;*.obj;*.dae

使用

QFileDialog::getOpenFileName

函數顯示導入檔案框。

顯示結果:

基于OpenCASCADE自制三維模組化軟體(十一)使用ASSIMP導入導出一、導入模型二、導出模型項目倉庫
  1. 擷取模型内容

構造函數

// cmodel.cpp
// CModel::CModel
{
    ...
    
    //從外部檔案中加載模型
    Importer t_importer;
    const aiScene *t_scene=t_importer.ReadFile(_filepath.toStdString(),aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace);
    if(!t_scene || t_scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !t_scene->mRootNode) // if is Not Zero
    {
        qDebug() << "ERROR::ASSIMP:: " << t_importer.GetErrorString();
        return;
    }
    // 構造根節點及所有子節點
    m_process_nodes(t_scene->mRootNode, t_scene, true);
}
           

将模型的檔案路徑輸入到

Assimp::Importer

的成員函數

ReadFile

中,傳回場景

aiScene

結構體。把結構體放入

m_process_nodes

函數中處理場景的節點。

  1. 處理節點

擷取坐标資訊

// cmodel.cpp
// CModel::m_process_nodes
    aiMatrix4x4 t_matrix=_node->mTransformation;    // 擷取轉換坐标系
    // 擷取旋轉矩陣的縮放比例
    ai_real t_scaling_x = aiVector3D(t_matrix.a1,t_matrix.a2,t_matrix.a3).Length();
    ai_real t_scaling_y = aiVector3D(t_matrix.b1,t_matrix.b2,t_matrix.b3).Length();
    ai_real t_scaling_z = aiVector3D(t_matrix.c1,t_matrix.c2,t_matrix.c3).Length();

    // 從 aiMatrix4x4 轉換成 gp_Trsf 形式
    gp_Trsf t_transfer;
    t_transfer.SetValues(t_matrix.a1 / t_scaling_x, t_matrix.a2 / t_scaling_x, t_matrix.a3 / t_scaling_x, t_matrix.a4,
                         t_matrix.b1 / t_scaling_y, t_matrix.b2 / t_scaling_y, t_matrix.b3 / t_scaling_y, t_matrix.b4,
                         t_matrix.c1 / t_scaling_z, t_matrix.c2 / t_scaling_z, t_matrix.c3 / t_scaling_z, t_matrix.c4);
           

周遊節點的所有網格(Mesh)

// cmodel.cpp
// void CModel::m_process_nodes(const aiNode *_node, const aiScene *_scene, bool _is_root)
    for(unsigned int imesh = 0; imesh < _node->mNumMeshes; imesh++)
    {
        aiMesh* t_mesh = _scene->mMeshes[_node->mMeshes[imesh]];    // 擷取目前網格
        // 周遊網格的所有面
        for(unsigned int iface = 0; iface < t_mesh->mNumFaces; iface++)
        {
            t_mesh->mMaterialIndex;
            aiFace t_face = t_mesh->mFaces[iface];
            BRepBuilderAPI_MakePolygon t_polygon;
            // 周遊面的所有頂點
            for(unsigned int ivertex = 0; ivertex < t_face.mNumIndices; ivertex++)
            {
                // 轉換頂點儲存模式
                gp_Pnt t_pnt=gp_Pnt(t_mesh->mVertices[t_face.mIndices[ivertex]].x,
                                    t_mesh->mVertices[t_face.mIndices[ivertex]].y,
                                    t_mesh->mVertices[t_face.mIndices[ivertex]].z);
                t_polygon.Add(t_pnt);   // 添加頂點
            }
            t_polygon.Close();  // 閉合頂點
            t_topo_face = BRepBuilderAPI_MakeFace (t_polygon); // 通過閉合的線構造面
            if(!t_topo_face.IsNull())
            {
                t_build_tool.Add (t_compound, t_topo_face);  // 将面加入到複合體中
            }
        }
        //! 材質資訊
        aiMaterial* material = _scene->mMaterials[t_mesh->mMaterialIndex];   //通過索引擷取網格在環境中的材質
        t_occ_material = m_material_transfer(material); // 從ASSIMP格式轉換到OCC材質格式
    }
           

周遊子節點

// cmodel.cpp
// void CModel::m_process_nodes(const aiNode *_node, const aiScene *_scene, bool _is_root)
    for(unsigned int i = 0; i < _node->mNumChildren; i++)
    {
        m_process_nodes(_node->mChildren[i], _scene , t_is_next_root);   // 構造子節點
    }
           
  1. 擷取材質及轉換

上一節中使用了

m_material_transfer

函數将ASSIMP的

aiMaterial

結構體内容轉換到OpenCASCADE中的

Graphic3d_MaterialAspect

形式。

初始化OpenCASCADE材質

// cmodel.cpp
// Graphic3d_MaterialAspect CModel::m_material_transfer(aiMaterial *_material)
    Graphic3d_MaterialAspect t_result;
    t_result.SetMaterialType(Graphic3d_MATERIAL_PHYSIC);
    Quantity_Color t_occ_colors[Graphic3d_TypeOfReflection_NB];
    t_occ_colors[Graphic3d_TOR_AMBIENT]  = Quantity_Color (Graphic3d_Vec3 (0.2f, 0.2f, 0.2f));
    t_occ_colors[Graphic3d_TOR_DIFFUSE]  = Quantity_Color (Graphic3d_Vec3 (0.2f, 0.2f, 0.2f));
    t_occ_colors[Graphic3d_TOR_SPECULAR] = Quantity_Color (Graphic3d_Vec3 (1.0f, 1.0f, 1.0f));
    Standard_ShortReal t_occ_shininess = 0.039f;
           

擷取各種資料,包括名稱、環境光、漫反射、鏡面光、反光度

// cmodel.cpp
// Graphic3d_MaterialAspect CModel::m_material_transfer(aiMaterial *_material)
    aiString name;  // 材質名稱 原始資料
    if (AI_SUCCESS==aiGetMaterialString(_material,AI_MATKEY_NAME,&name))
    {
        t_result.SetMaterialName(name.C_Str());
    }
    // 環境光
    aiColor4D ambient;      // 環境光 原始資料
    if(AI_SUCCESS ==aiGetMaterialColor(_material, AI_MATKEY_COLOR_AMBIENT, &ambient))
    {
        t_occ_colors[Graphic3d_TOR_AMBIENT]=Quantity_Color(ambient.r,ambient.g,ambient.b,Quantity_TOC_RGB);
        t_result.SetAmbientColor(t_occ_colors[Graphic3d_TOR_AMBIENT]);
    }
    // 漫反射
    aiColor4D diffuse;      // 漫反射 原始資料
    if(AI_SUCCESS ==aiGetMaterialColor(_material, AI_MATKEY_COLOR_DIFFUSE, &diffuse))
    {
        t_occ_colors[Graphic3d_TOR_DIFFUSE]=Quantity_Color(diffuse.r,diffuse.g,diffuse.b,Quantity_TOC_RGB);
        t_result.SetDiffuseColor(t_occ_colors[Graphic3d_TOR_DIFFUSE]);
    }
    // 鏡面光
    aiColor4D specular;     // 鏡面光 原始資料
    if(AI_SUCCESS ==aiGetMaterialColor(_material, AI_MATKEY_COLOR_SPECULAR, &specular))
    {
        t_occ_colors[Graphic3d_TOR_SPECULAR]=Quantity_Color(specular.r,specular.g,specular.b,Quantity_TOC_RGB);
        t_result.SetSpecularColor(t_occ_colors[Graphic3d_TOR_SPECULAR]);
    }
    // 反光度
    float shininess;        // 反光度 原始資料
    if(AI_SUCCESS ==aiGetMaterialFloat(_material, AI_MATKEY_SHININESS, &shininess))
    {
        t_occ_shininess=shininess/128.0;    // 由OpenGL值轉換成VRML97
        // OCC的反光度表示方式隻接受0到1之間,否則報錯
        t_occ_shininess = t_occ_shininess<1.0 ? t_occ_shininess:1.0;
        t_occ_shininess = t_occ_shininess<0.0 ? 0.0:t_occ_shininess;
        t_result.SetShininess(t_occ_shininess); // 設定反光度
    }
           
  1. 導入模型結果

    打開菜單欄File->Import,彈出檔案對話框後選擇模型檔案并打開。

    基于OpenCASCADE自制三維模組化軟體(十一)使用ASSIMP導入導出一、導入模型二、導出模型項目倉庫

二、導出模型

  1. 打開導出對話框

首先判斷是否有模型被選擇,若無模型則提示框

// cmainwindow.cpp
// void CMainWindow::on_actionExport_triggered()
    if(m_3d_widget->m_get_context()->NbSelected() == 0)
    {
        // 無選擇則彈出提示框
        QMessageBox::warning(this,tr("Export Error"),tr("There is no object selected!"));
        return; // 不執行操作
    }
           

通過

Assimp::Exporter

GetExportFormatCount

函數擷取支援格式的數量,然後周遊及通過

GetExportFormatDescription

函數擷取所有格式的結構體,結構體内包含:①

id

唯一辨別;②

description

格式描述;③

fileExtension

檔案擴充名

另外通過哈希表儲存檔案過濾字元串與檔案格式id的對應關系,以在後面用作區分使用者選擇的檔案格式id

// cmainwindow.cpp
// void CMainWindow::on_actionExport_triggered()
    QString t_all_filter; // 所有檔案過濾器
    QHash<QString,const char *> t_hash_format;  // 格式過濾器與文檔描述哈希表
    Exporter t_export;  // 導出器
    for(int i=0; i<t_export.GetExportFormatCount(); i++)    // 周遊ASSIMP允許導出的格式
    {
        const aiExportFormatDesc *t_format_desc = t_export.GetExportFormatDescription(i);   // 擷取每個文檔描述
        // 文檔過濾器
        QString t_single_format = QString(t_format_desc->description)+QString("(*.%1)").arg(t_format_desc->fileExtension);
        t_hash_format.insert(t_single_format,t_format_desc->id);    // 插入格式過濾器與文檔描述哈希表
        t_all_filter += t_single_format;    // 添加單個文檔過濾到整體過濾器
        if(i != t_export.GetExportFormatCount()-1)  // 最後一個不添加分行
        {
            t_all_filter+=";;"; // 分行
        }
    }
           

打開儲存檔案對話框,通過傳回值

filename

獲得儲存的檔案名,并通過

t_selected_filter

擷取使用者選擇的過濾器

// cmainwindow.cpp
// void CMainWindow::on_actionExport_triggered()
    QString t_selected_filter; // 被選擇的過濾器
    // 打開檔案儲存提示框
    QString filename = QFileDialog::getSaveFileName(this,tr("Save"),".",t_all_filter,&t_selected_filter);
    if(filename.isEmpty())  // 若檔案名為空,則不執行操作
    {
        return; // 不執行操作
    }
           

在對話框點選儲存後,進行實質的檔案儲存操作

// cmainwindow.cpp
// void CMainWindow::on_actionExport_triggered()   
    // 導出模型檔案
    CModel::m_export_model(filename,t_hash_format.value(t_selected_filter),m_3d_widget->m_get_context());
           

運作結果:

基于OpenCASCADE自制三維模組化軟體(十一)使用ASSIMP導入導出一、導入模型二、導出模型項目倉庫
  1. 導出操作

初始化參數

// cmodel.cpp
// static bool m_export_model(QString _filename , const char *_format_id,Handle(AIS_InteractiveContext) _context);
    Exporter exporter;
    // 定義場景
    aiScene *t_scene = new aiScene();
    // 單一材質

    // 建立根節點
    t_scene->mRootNode=new aiNode();
    // 建立子節點
    int t_NumChildrenNode = _context->NbSelected();
    aiNode **t_node_list=new aiNode*[t_NumChildrenNode];

    t_scene->mNumMaterials=t_NumChildrenNode;
    t_scene->mMaterials = new aiMaterial*[t_NumChildrenNode];

    // 定義場景所有網格
    t_scene->mNumMeshes = t_NumChildrenNode;
    t_scene->mMeshes = new aiMesh*[t_NumChildrenNode];

    int t_index = 0;
           

周遊所有選擇的模型

// cmodel.cpp
// static bool m_export_model(QString _filename , const char *_format_id,Handle(AIS_InteractiveContext) _context);
    for ( _context->InitSelected(); _context->MoreSelected(); _context->NextSelected() )
    {
        aiNode *t_node = t_node_list[t_index] = new aiNode();
        t_node->mNumMeshes=1;   // 一個網格
        t_node->mNumChildren=0; // 無子節點
        t_node->mMeshes = new uint[1];  //一個網格位址
        t_node->mMeshes[0] = t_index; // 網格位址索引

        aiMesh* pMesh = t_scene->mMeshes[t_index] = new aiMesh(); // 建立位址0的網格
        pMesh->mMaterialIndex = t_index;  // 網格材質

        Standard_Integer aNbNodes = 0;
        Standard_Integer aNbTriangles = 0;

        Handle(AIS_InteractiveObject) obj = _context->SelectedInteractive();
        
        ......
    }
           

在以上周遊模型的循環内,添加材質參數到ASSIMP中

// cmodel.cpp
// static bool m_export_model(QString _filename , const char *_format_id,Handle(AIS_InteractiveContext) _context);
// for ( _context->InitSelected(); _context->MoreSelected(); _context->NextSelected() )
            Handle(AIS_Shape) ais_shape = Handle(AIS_Shape)::DownCast(obj);

            Graphic3d_MaterialAspect shpae_material(ais_shape->Material());

            aiMaterial* pMaterial = t_scene->mMaterials[t_index] = new aiMaterial();

            Quantity_Color amb=shpae_material.AmbientColor();
            aiColor4D ambient(amb.Red(),amb.Green(),amb.Blue(),1.0);
            pMaterial->AddProperty(&ambient,1,AI_MATKEY_COLOR_AMBIENT);


            Quantity_Color diff=shpae_material.DiffuseColor();
            aiColor4D diffuse(diff.Red(),diff.Green(),diff.Blue(),1.0);
            pMaterial->AddProperty(&diffuse,1,AI_MATKEY_COLOR_DIFFUSE);

            Quantity_Color spec=shpae_material.SpecularColor();
            aiColor4D specular(spec.Red(),spec.Green(),spec.Blue(),1.0);
            pMaterial->AddProperty(&specular,1,AI_MATKEY_COLOR_SPECULAR);

            Standard_ShortReal shin=shpae_material.Shininess();
            pMaterial->AddProperty(&shin,1,AI_MATKEY_SHININESS);
           

分别計算模型中的節點和三角面的數量

// cmodel.cpp
// static bool m_export_model(QString _filename , const char *_format_id,Handle(AIS_InteractiveContext) _context);
// for ( _context->InitSelected(); _context->MoreSelected(); _context->NextSelected() )
        TopoDS_Shape theShape = ais_shape->Shape();
        // calculate total number of the nodes and triangles
        for (TopExp_Explorer anExpSF (theShape, TopAbs_FACE); anExpSF.More(); anExpSF.Next())
        {
            TopLoc_Location aLoc;
            Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation (TopoDS::Face (anExpSF.Current()), aLoc);
            if (! aTriangulation.IsNull())
            {
                aNbNodes += aTriangulation->NbNodes ();
                aNbTriangles += aTriangulation->NbTriangles ();
            }
        }

        pMesh->mNumVertices = aNbNodes;
        pMesh->mNumFaces = aNbTriangles;
           

把所有三角面與定點資訊添加到ASSIMP中

// cmodel.cpp
// static bool m_export_model(QString _filename , const char *_format_id,Handle(AIS_InteractiveContext) _context);
// for ( _context->InitSelected(); _context->MoreSelected(); _context->NextSelected() )
        int index=0;
        int face_index=0;
        // fill temporary triangulation
        Standard_Integer aNodeOffset = 0;
        for (TopExp_Explorer anExpSF (theShape, TopAbs_FACE); anExpSF.More(); anExpSF.Next())
        {
            TopLoc_Location aLoc;
            Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation (TopoDS::Face (anExpSF.Current()), aLoc);

            const TColgp_Array1OfPnt& aNodes = aTriangulation->Nodes();
            const Poly_Array1OfTriangle& aTriangles = aTriangulation->Triangles();

            // copy nodes
            gp_Trsf aTrsf = aLoc.Transformation();
            for (Standard_Integer aNodeIter = aNodes.Lower(); aNodeIter <= aNodes.Upper(); ++aNodeIter)
            {
                gp_Pnt aPnt = aNodes (aNodeIter);
                aPnt.Transform (aTrsf);
                qDebug()<<"nodes "<<aPnt.X()<<aPnt.Y()<<aPnt.Z();
                vp[index].Set(aPnt.X(),aPnt.Y(),aPnt.Z());
                vn[index].Set(0.0,0.0,1.0);
                index++;
            }

            // copy triangles
            const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation();
            for (Standard_Integer aTriIter = aTriangles.Lower(); aTriIter <= aTriangles.Upper(); ++aTriIter)
            {
                Poly_Triangle aTri = aTriangles (aTriIter);

                Standard_Integer anId[3];
                aTri.Get (anId[0], anId[1], anId[2]);
                if (anOrientation == TopAbs_REVERSED)
                {
                    // Swap 1, 2.
                    Standard_Integer aTmpIdx = anId[1];
                    anId[1] = anId[2];
                    anId[2] = aTmpIdx;
                }
                // Update nodes according to the offset.
                anId[0] += aNodeOffset;
                anId[1] += aNodeOffset;
                anId[2] += aNodeOffset;
                aiFace& face = pMesh->mFaces[face_index++];
                face.mIndices = new unsigned int[face.mNumIndices = 3];
                face.mIndices[0]=anId[0]-1;
                face.mIndices[1]=anId[1]-1;
                face.mIndices[2]=anId[2]-1;
            }
            aNodeOffset += aNodes.Size();
        }

        t_index++;
           

完善

aiScene

結構體的内容,使用

Assimp::Exporter

Export

方法将

t_scene

對象的内容儲存到檔案中,檔案格式以

_format_id

參數決定

// cmodel.cpp
// static bool m_export_model(QString _filename , const char *_format_id,Handle(AIS_InteractiveContext) _context);
    // 根節點加入子節點
    t_scene->mRootNode->addChildren(t_NumChildrenNode,t_node_list);
    exporter.Export(t_scene , _format_id , _filename.toStdString());
           
  1. 導出模型檔案效果

    打開菜單欄File->Export,彈出檔案對話框後選擇檔案路徑、輸入檔案名并點選儲存。

    基于OpenCASCADE自制三維模組化軟體(十一)使用ASSIMP導入導出一、導入模型二、導出模型項目倉庫
    輸出後的檔案内容
    基于OpenCASCADE自制三維模組化軟體(十一)使用ASSIMP導入導出一、導入模型二、導出模型項目倉庫

項目倉庫

https://github.com/Jelatine/JellyCAD

繼續閱讀