从几何到显示还是比较麻烦的,需要将几何对象转换成渲染对象,涉及几何建模、离散化、图形显示,阅读本文需了解一些基本的OCC、VTK编程
一、几何体显示基本流程
FastCAE几何内核使用的是OCC,显示渲染用的VTK,那么就存在将OCC建模后的几何对象变成VTK支持类型的数据这个过程。
FastCAE在用OCC创建完模型之后,会生成一个Geometry::GeometrySet对象,这个对象包含最原始的OCC中TopoDS_Shape实例对象。创建完成后都会发出信号emit showSet(set);
,这个信号会触发将TopoDS_Shape变成VTK显示对象。这个段代码是在void GeometryViewProvider::showGeoSet(Geometry::GeometrySet *set, bool render)
函数中实现。
二、几何对象变成显示对象过程
几何体是由点、线、面构成的,进行显示的时候是分别提取这些数据,进行单独显示的。也就是说我们看到的一个立方体等几何对象是由三个VTK显示对象Actor拼起来的。这个函数代码如下:
void GeometryViewProvider::showGeoSet(Geometry::GeometrySet *set, bool render)
{QList<vtkPolyData *> viewPolys = _viewData->transferToPoly(set); // 对几何体进行点、线、面的拆分vtkPolyData *facePoly = viewPolys.at(0); // 面的多边形数据vtkPolyData *edgePoly = viewPolys.at(1); // 边的数据集vtkPolyData *pointPoly = viewPolys.at(2); // 点数据集GeoViewObj viewObj;if (facePoly != nullptr) // 创建显示面的Actor{vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();mapper->SetInputData(facePoly);actor->SetMapper(mapper);bool vis = set->isVisible();bool show = Setting::BusAPI::instance()->getGraphOption()->isShowGeoSurface();actor->SetVisibility(show && vis); // 设置是否显示面actor->SetPickable(false); // 不可鼠标拾取?哪里打开呢?actor->GetProperty()->SetRepresentationToSurface(); // 这个函数不调用也没看到啥影响_preWindow->AppendActor(actor, ModuleBase::D3, false); // 将面添加到场景中viewObj._faceObj = QPair<vtkActor *, vtkPolyData *>(actor, facePoly);}// 创建显示边的Actorif (edgePoly != nullptr) {vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();mapper->SetInputData(edgePoly);actor->SetMapper(mapper);bool vis = set->isVisible();bool show = Setting::BusAPI::instance()->getGraphOption()->isShowGeoEdge();actor->SetVisibility(show && vis);actor->SetPickable(false);actor->GetProperty()->SetRepresentationToWireframe();// actor->GetProperty()->EdgeVisibilityOn();float width = Setting::BusAPI::instance()->getGraphOption()->getGeoCurveWidth();actor->GetProperty()->SetLineWidth(width);_preWindow->AppendActor(actor, ModuleBase::D3, false);viewObj._edgeObj = QPair<vtkActor *, vtkPolyData *>(actor, edgePoly);}// 创建显示点的Actorif (pointPoly != nullptr){vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();float size = Setting::BusAPI::instance()->getGraphOption()->getGeoPointSize();mapper->SetInputData(pointPoly);actor->SetMapper(mapper);bool vis = set->isVisible();bool show = Setting::BusAPI::instance()->getGraphOption()->isShowGeoPoint();actor->SetVisibility(show && vis);actor->SetPickable(false);actor->GetProperty()->SetRepresentationToPoints();actor->GetProperty()->SetPointSize(size);_preWindow->AppendActor(actor, ModuleBase::D3, false);viewObj._pointObj = QPair<vtkActor *, vtkPolyData *>(actor, pointPoly);}_geoViewHash.insert(set, viewObj);if (render)_preWindow->resetCamera();
}
三、几何面数据的提取
几何体面、边、点转换为vtkPolyData对象的方式类似,都在函数GeometryViewData::transferToPoly()
中,这里只分析面数据的提取。
/* 提取几何表面的多边形数据集 */
vtkPolyData *GeometryViewData::transferFace(Geometry::GeometrySet *gset)
{TopoDS_Shape *shape = gset->getShape(); // 获取OCC的ShapeTopExp_Explorer faceExp(*shape, TopAbs_FACE); // 这个类可访问Shape的拓扑关系QList<Handle(TopoDS_TShape)> tshapelist; // 放置已访问的Face对象,防止重复访问vtkSmartPointer<vtkAppendPolyData> appendFilter = vtkSmartPointer<vtkAppendPolyData>::New();vtkPolyData *polyData = vtkPolyData::New();int beg = 0;for (int index = 0; faceExp.More(); faceExp.Next(), ++index) // 开始遍历面{const TopoDS_Shape &s = faceExp.Current();Handle(TopoDS_TShape) ts = s.TShape();if (tshapelist.contains(ts)) // 防止重复访问,会存在这种情况吗?continue;tshapelist.append(ts);IVtkOCC_Shape::Handle aShapeImpl = new IVtkOCC_Shape(s); // OCC提供IVtkOCC_Shape类vtkSmartPointer<IVtkTools_ShapeDataSource> DS = vtkSmartPointer<IVtkTools_ShapeDataSource>::New(); // OCC提供的VTK数据源DS->SetShape(aShapeImpl); vtkSmartPointer<vtkCleanPolyData> cleanFilter = vtkSmartPointer<vtkCleanPolyData>::New();cleanFilter->SetInputConnection(DS->GetOutputPort());cleanFilter->Update();vtkSmartPointer<vtkPolyData> tpolys = vtkSmartPointer<vtkPolyData>::New();vtkPolyData *tpolydata = cleanFilter->GetOutput();const int np = tpolydata->GetNumberOfPoints(); // 点的数量const int nc = tpolydata->GetNumberOfCells(); // cell的数量vtkPoints *points = vtkPoints::New();for (int i = 0; i < np; i++) // 提取几何点数据{double *coor = tpolydata->GetPoint(i);points->InsertNextPoint(coor);}tpolys->SetPoints(points); // 设置几何点数据vtkCellArray *cells = vtkCellArray::New();for (int i = 0; i < nc; ++i){vtkCell *cell = tpolydata->GetCell(i);vtkIdList *ceid = cell->GetPointIds();if (ceid->GetNumberOfIds() == 3) // 只提取三角形,这里获取的cell包含点、线、三角形,一个立方体最后应该12个三角形{vtkTriangle *triangle = vtkTriangle::New();triangle->DeepCopy(cell); // 有DeepCopy接口cells->InsertNextCell(triangle);}}tpolys->SetPolys(cells); // 设置拓扑多边形数据集的cell// 法线数据?貌似打开与否不影响显示效果vtkSmartPointer<vtkPolyDataNormals> normals = vtkSmartPointer<vtkPolyDataNormals>::New();normals->SetInputData(tpolys);normals->FlipNormalsOn();normals->Update();vtkPolyData *facePoly = normals->GetOutput();const int ncell = facePoly->GetNumberOfCells();if (ncell < 1)continue;GeometryViewObject *obj = new GeometryViewObject(GeometryViewObject::Face, beg, beg + ncell - 1, ts);beg += ncell;appendFilter->AddInputData(facePoly); // 追加到appendFilter中auto setViewObj = this->getGeosetObj(gset);setViewObj->appendFaceViewObj(index, ts, obj);}appendFilter->Update();polyData->DeepCopy(appendFilter->GetOutput());auto setViewObj = this->getGeosetObj(gset);setViewObj->setFacePoly(polyData); // 保存面多边形数据const int npc = polyData->GetNumberOfCells();if (npc < 1)return nullptr;return polyData;
}
这里要注意几个类:
- TopExp_Explorer: OCC提供的遍历几何体拓扑结构的类
- IVtkOCC_Shape、IVtkTools_ShapeDataSource:这俩类可以将面变成VTK的cell,包含面上的点、边、三角形
- vtkPolyDataNormals: 看类名猜测是生成法线的,但是不用这类显示也没啥问题
- vtkAppendPolyData:可以将各个面的数据追加在一起
最后来张图吧: