assimp支持很多3D格式文件的相互转换,是非常好的一个三方库,但是occ数据结构转assimp,目前网上基本没有,我工作中需要这个功能,绞尽脑汁搞了2周,终于弄出来了,分享一下:
主要是下面几点:
1. occ 数据结构遍历出,顶点,法向量
2. 三角面片化后,每个三角面片都要有法向量,不然在3D视图中转动图形,不会随着光照出现阴影
3. assimp的数据结构也需要注意,还是比较麻烦的
下面是我写的一个接口:
//精度
static double st_theLinDeflection = 1e-3;
static double st_theAngDeflection = 0.5;
//内部数据结构定义
struct shape_data
{
TopoDS_Shape *ptrShape; /* occ 数据结构 */
std::string externParam; /* 图形额外参数 */
QString shape_name = ""; /* 图形名称如立方体 */
std::string group = "NA"; /* 图形所属组 */
QColor color; /* 图形的颜色 */
std::atomic_bool bDelete {false}; /* 图形是否被删除 */
QString Uuid; /* 图形唯一标识ID */
};
//下面标志用于assimp导出,可以压缩导出的文件,使其在网页上操作流畅
unsigned int stpp =
aiProcess_CalcTangentSpace | /* 计算导入网格的切线和bitangents */
aiProcess_GenSmoothNormals | /*为网格中的所有顶点生成平滑法线 */
// aiProcess_JoinIdenticalVertices | /* 识别和连接所有导入网格中的相同顶点数据集*/
aiProcess_LimitBoneWeights |
aiProcess_RemoveRedundantMaterials |
aiProcess_SplitLargeMeshes |
aiProcess_Triangulate |
aiProcess_GenUVCoords |
aiProcess_SortByPType |
aiProcess_FindDegenerates |
aiProcess_FindInvalidData|
aiProcess_FindInstances |
aiProcess_ValidateDataStructure |
aiProcess_OptimizeMeshes|
aiProcess_PreTransformVertices |
0;
int _export_( std::vector<shape_data*> &hoge, const char *filename, QString type = "gltf2")
{//默认gltf格式导出
Assimp::Exporter exporter;
aiScene t_scene;
t_scene.mRootNode=new aiNode();
int t_NumChildrenNode = hoge.size();
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];
for (int t_index = 0; t_index < t_NumChildrenNode; t_index++)
{
if (hoge.at(t_index) == nullptr || hoge.at(t_index)->ptrShape->IsNull())
continue;
shape_data *ptr = hoge.at(t_index);
aiNode *t_node = t_node_list[t_index] = new aiNode();
t_node->mName = aiString(std::string(hoge[t_index]->jsonMsg));
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;
//设置材质
aiMaterial* pMaterial = t_scene.mMaterials[t_index] = new aiMaterial();
int r, g, b, a = 0;
ptr->color.getRgb(&r, &g, &b, &a);
float rgbRed = r/255.0;
float rgbGreen = g/255.0;
float rgbBlue = b/255.0;
//表面环境色
//aiColor4D ambient(0.05, 0.05, 0.05, 1.0);
aiColor4D ambient(rgbRed, rgbGreen, rgbBlue, 1.0);
pMaterial->AddProperty(&ambient, 1, AI_MATKEY_COLOR_AMBIENT);
pMaterial->AddProperty(&ambient,1,AI_MATKEY_COLOR_DIFFUSE);
//表面高光颜色
aiColor4D sp(1, 1, 1, 1);
pMaterial->AddProperty(&sp,1,AI_MATKEY_COLOR_SPECULAR);
TopoDS_Shape theShape = *ptr->ptrShape;
if (theShape.IsNull()) {
continue;
}
//判断图元是否是空的图元
Bnd_Box box;
box.SetGap(0.01); //边界距离
BRepBndLib ret;
ret.Add(theShape, box, Standard_True);
if (box.IsVoid())
{
continue;
}
// clean any previous triangulation
BRepTools::Clean(theShape);
ShapeFix_Shape fixer(theShape);
fixer.Perform();
theShape = fixer.Shape();
BRepMesh_IncrementalMesh(theShape, st_theLinDeflection, true, st_theAngDeflection, false);
// 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;
aiVector3D* vp,*vn = 0;
vp = pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
vn = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
pMesh->mFaces = new aiFace[pMesh->mNumFaces];
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);
if (aTriangulation.IsNull())
continue;
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);
vp[index].Set(aPnt.X(),aPnt.Y(),aPnt.Z());
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]);
gp_Pnt aPnt1 = aNodes(anId[0]);
gp_Pnt aPnt2 = aNodes(anId[1]);
gp_Pnt aPnt3 = aNodes(anId[2]);
gp_XYZ vector12(aPnt2.XYZ() - aPnt1.XYZ());
gp_XYZ vector13(aPnt3.XYZ() - aPnt1.XYZ());
gp_XYZ normal123 = vector13.Crossed(vector12);
Standard_Real rModulus = normal123.Modulus();
if (rModulus > gp::Resolution())
{
normal123.Normalize();
}
else
{
normal123.SetCoord(0, 0, 0);
}
if (anOrientation != TopAbs_REVERSED)
{
normal123.Reverse();
}
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;
//填充法线信息
vn[anId[0] -1].Set(normal123.X(), normal123.Y(), normal123.Z());
vn[anId[1] -1].Set(normal123.X(), normal123.Y(), normal123.Z());
vn[anId[2] -1].Set(normal123.X(), normal123.Y(), normal123.Z());
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_scene.mRootNode->addChildren(t_NumChildrenNode,t_node_list);
try {
aiReturn ret;
if (type == GLTF_SUPPORT)
{
aiFileIO *pfile = nullptr;
ret = aiExportSceneEx(&t_scene, GLTF_SUPPORT, std::string(filename).c_str(), pfile,
stpp);
} else if (type == "obj")
{
ret = aiExportSceneEx(&t_scene, "obj", std::string(filename).c_str(), 0,stpp);
}
switch(ret)
{
case aiReturn_SUCCESS:
return 1;
break;
case aiReturn_FAILURE:
break;
case aiReturn_OUTOFMEMORY:
break;
case _AI_ENFORCE_ENUM_SIZE:
break;
}
}
catch (...) {
return 0;
}
return 0;
}
下面是我用chatGPT帮写的,但是我没有测试
#include <OpenCascade/TopoDS_Shape.hxx>
#include <OpenCascade/BRep_Tool.hxx>
#include <OpenCascade/TopExp.hxx>
#include <OpenCascade/TopLoc_Location.hxx>
#include <OpenCascade/GProp_GProps.hxx>
#include <OpenCascade/BRepGProp.hxx>
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <iostream>
class TopoDS_Shape_To_AssimpScene
{
public:
static aiScene* convert(const TopoDS_Shape& shape)
{
aiScene* scene = new aiScene();
// 创建一个新的mesh,并将其添加到aiScene
aiMesh* mesh = new aiMesh();
scene->mNumMeshes = 1;
scene->mMeshes = new aiMesh*[1];
scene->mMeshes[0] = mesh;
// 计算顶点数量
int vertexCount = 0;
for (TopExp_Explorer expVertex(shape, TopAbs_VERTEX); expVertex.More(); expVertex.Next())
{
vertexCount++;
}
// 分配顶点数组空间
mesh->mNumVertices = vertexCount;
mesh->mVertices = new aiVector3D[vertexCount];
// 填充顶点数组
int iVertex = 0;
for (TopExp_Explorer expVertex(shape, TopAbs_VERTEX); expVertex.More(); expVertex.Next(), iVertex++)
{
TopoDS_Vertex vertex = TopoDS::Vertex(expVertex.Current());
gp_Pnt pnt = BRep_Tool::Pnt(vertex);
mesh->mVertices[iVertex].x = pnt.X();
mesh->mVertices[iVertex].y = pnt.Y();
mesh->mVertices[iVertex].z = pnt.Z();
}
// 计算法线数量
int normalCount = 0;
for (TopExp_Explorer expFace(shape, TopAbs_FACE); expFace.More(); expFace.Next())
{
normalCount++;
}
// 分配法线数组空
mesh->mNumNormals = normalCount;
mesh->mNormals = new aiVector3D[normalCount];
// 填充法线数组
int iNormal = 0;
for (TopExp_Explorer expFace(shape, TopAbs_FACE); expFace.More(); expFace.Next(), iNormal++)
{
TopoDS_Face face = TopoDS::Face(expFace.Current());
gp_Dir normal = BRep_Tool::Normal(face, gp_Pnt(0,0,0));
mesh->mNormals[iNormal].x = normal.X();
mesh->mNormals[iNormal].y = normal.Y();
mesh->mNormals[iNormal].z = normal.Z();
}
return scene;
}
};