文章目錄
- 一、導入模型
- 二、導出模型
- 項目倉庫
一、導入模型
-
打開導入對話框
首先從
中擷取ASSIMP支援導入的檔案格式,然後用Assimp::Importer
函數彈出檔案對話框。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
函數顯示導入檔案框。
顯示結果:
- 擷取模型内容
構造函數
// 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
函數中處理場景的節點。
- 處理節點
擷取坐标資訊
// 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); // 構造子節點
}
- 擷取材質及轉換
上一節中使用了
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); // 設定反光度
}
-
導入模型結果
打開菜單欄File->Import,彈出檔案對話框後選擇模型檔案并打開。
二、導出模型
- 打開導出對話框
首先判斷是否有模型被選擇,若無模型則提示框
// 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());
運作結果:
- 導出操作
初始化參數
// 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());
-
導出模型檔案效果
打開菜單欄File->Export,彈出檔案對話框後選擇檔案路徑、輸入檔案名并點選儲存。
輸出後的檔案内容
項目倉庫
https://github.com/Jelatine/JellyCAD