occ[opencascade] 数据结构TopoDS_Shape 转 assimp 数据结构, assimp导出3D格式文件

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;
}
};

    

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何处惹尘埃~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值