【FastCAE源码阅读3】几何模型显示:从OCC对象到VTK对象

从几何到显示还是比较麻烦的,需要将几何对象转换成渲染对象,涉及几何建模、面的三角化、图形渲染等学科,阅读本文需了解一些基本的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); // 将面actor添加到场景中
		viewObj._faceObj = QPair<vtkActor *, vtkPolyData *>(actor, facePoly);
	}

	// 创建显示边的Actor
	if (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);
	}

	// 创建显示点的Actor
	if (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的Shape
	TopExp_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;
}

这里要注意几个类:

  1. TopExp_Explorer: OCC提供的遍历几何体拓扑结构的类;
  2. IVtkOCC_Shape、IVtkTools_ShapeDataSource:这俩类可以将面三角化,并变成VTK的cell,注意cell包含面上的点、边、三角形,所以提取面的时候只提取了三角形,这俩类同样是OCC提供的;
  3. vtkPolyDataNormals: 看类名猜测是生成法线的,但是不用这类显示也没啥问题;
  4. vtkAppendPolyData:可以将各个面的数据追加在一起。

四、面数据的最终数据结构

OCC对象在转换过程中,会生成成一系列对象,用于后续的拾取高亮等功能。FastCAE中有个组件叫GeometryViewProvider,其中的字段_viewData(类型 GeometryViewData)保存这些数据。GeometryViewData类中有哈希表,key是几何对象,value是GeoSetViewObject。GeoSetViewObject又有hash表_faceObjHash、_faceViewObjs。以立方体为例,_faceViewObjs保存6个面对应的GeometryViewObject对象,每个GeometryViewObject对象记录这这个面对应的cell范围及所有面的多边形数据集vtkPloyData。这个数据集在GeoSetViewObject也有引用(_facePoly字段),也是面Actor中的数据集,一个体的所有面组成一个Actor。最终形成如下的一种数据结构:
在这里插入图片描述

边和点的数据与面类似,这里就不再分析了。

最后来张图吧:
在这里插入图片描述

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值