参考这位大哥的博客https://blog.csdn.net/qq_31709249/article/details/94357183
以下是我的理解以及改动之后的代码
我先展示一下最终main函数中实现的核心代码
osgViewer::Viewer viewer;
osg::ref_ptr<osg::Group> root = new osg::Group;
osg::ref_ptr<osgDB::Options> options = new osgDB::Options("noRotation");
for (size_t i = 0; i<fileNames.size(); i++)
{
string name = fileNames[i];
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(name);
string modelName = Utility::getFileNameFromPath(name);
PositionVisitor visitor = PositionVisitor(modelName,textSize);
node->accept(visitor);
root->addChild(visitor.createRandomColorOsgNode(i));
}
//root->addChild(Utility::createCoorAxis(textSize));
cout<<"num children"<<root->getNumChildren()<<endl;
viewer.setSceneData(root);
viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
viewer.addEventHandler(new osgViewer::StatsHandler);
viewer.setUpViewOnSingleScreen(1);//这里是单屏幕显示
viewer.run();
首先将osgb文件读取到node中 name为osgb文件绝对路径
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(name);
然后自己编写Positionvisitor类,继承于NodeVisitor,相当于访问器,用于访问一个node中drawable的所有Geom
PositionVisitor visitor = PositionVisitor(modelName,textSize);
以下为Positionvisitor.h
class PositionVisitor
:public osg::NodeVisitor
{
protected:
vector<Geom*> allGeom;
osg::Vec4 geomColor;
string modelName;
int textSize;//提示文字的大小
osg::BoundingBox boundingBox;
public:
virtual void apply(osg::Geode& node) override;
void dealTriangleInfo(ModelAttributeFunctor attributeFunctor,osg::TriangleIndexFunctor<TriangleIndex> indexFunctor);//处理访问器得到的信息,构建三角形关系
osg::ref_ptr<osg::Node> createOsgNode(osg::Vec4 color,int order);//根据指定的颜色,将geom中的数据创建成osg节点
osg::ref_ptr<osg::Node> createRandomColorOsgNode(int order);//将geom中的数据创建成osg节点,颜色随机
osg::ref_ptr<osgText::Text> createTipText(short direction);//创建提示文字
osg::ref_ptr<osgText::Text> createCenterText(osg::BoundingBox);//创建geom的中心点
PositionVisitor(string ModelName);
PositionVisitor(string ModelName, int TextSize);
~PositionVisitor();
};
上面提到的Geom 如下,存储了顶点,三角形等信息,之后我再解释geom是什么
class Geom
{
public:
vector<Vertex*> vertices;
vector<Triangle*> triangles;
osg::BoundingBox boundingBox;
bool isTwoTriangleNeighbor(int triangle1Index,int triangle2Index);
osg::ref_ptr<osg::Geode> createOsgNode(osg::Vec4 color);
osg::ref_ptr<osg::Geode> Geom::createOsgNode_Point(osg::Vec4 color);
osg::ref_ptr<osg::Geode> Geom::createOsgNode_Triangle(osg::Vec4 color);
Geom();
~Geom();
};
然后node的accept函数可以调用visitor(基类NodeVisitor)的apply函数
node->accept(visitor);
我们自己重写apply函数 通过node得到drawable(一个osgb文件对应一个drawable),然后drawable的accept函数可以调用ModelAttributeFunctor(基类osg::Drawable::AttributeFunctor)的apply函数,得到顶点、法向量、纹理坐标信息。三角形也同理,自己写一个dealTriangleInfo函数,处理三角形数据
void PositionVisitor::apply(osg::Geode& node)
{
for (size_t i=0;i<node.getNumDrawables();i++)
{
osg::ref_ptr<osg::Drawable> drawable = node.getDrawable(i);
ModelAttributeFunctor functor;
drawable->accept(functor);
osg::TriangleIndexFunctor<TriangleIndex> triangleIndex;
drawable->accept(triangleIndex);
dealTriangleInfo(functor, triangleIndex);
}
}
上面所说到的ModelAttributeFunctor如下 ,它也相当于一个访问器,访问drawable的顶点、法向量、纹理坐标信息
class ModelAttributeFunctor
:public osg::Drawable::AttributeFunctor
{
public:
osg::ref_ptr<osg::Vec3Array> vertexList;
osg::ref_ptr<osg::Vec3Array> normalList;
osg::ref_ptr<osg::Vec2Array> textCoordList;
virtual void apply(osg::Drawable::AttributeType, unsigned, osg::Vec2*) override;
virtual void apply(osg::Drawable::AttributeType, unsigned, osg::Vec3*) override;
ModelAttributeFunctor();
~ModelAttributeFunctor();
};
他的apply函数比较简单,如下,给成员变量附上值
void ModelAttributeFunctor::apply(osg::Drawable::AttributeType type, unsigned size, osg::Vec2* front)
{
if (type==osg::Drawable::TEXTURE_COORDS_0)
{
cout << "textcoor" << endl;
for (unsigned i=0;i<size;i++)
{
textCoordList->push_back(*(front + i));
}
}
}
void ModelAttributeFunctor::apply(osg::Drawable::AttributeType type, unsigned size, osg::Vec3* front)
{
if (type == osg::Drawable::VERTICES)
{
cout << "vertice" << endl;
for (unsigned i = 0; i<size; i++)
{
vertexList->push_back(*(front + i));
}
}
else if (type == osg::Drawable::NORMALS)
{
cout << "normals" << endl;
for (unsigned i = 0; i<size; i++)
{
normalList->push_back(*(front + i));
}
}
}
TriangleIndex也大同小异 存储了每一个三角形三个点在顶点列表中的下标,存下标而不是三角形对应的每个点是为了节省空间
class TriangleIndex
{
public:
osg::ref_ptr<osg::UIntArray> indexs;
int triangleNum;
TriangleIndex();
~TriangleIndex();
void operator()(const unsigned int& v1, const unsigned int& v2, const unsigned int& v3);
};
dealTriangleInfo函数如下,实际上他是用ModelAttributeFunctor访问器得到的顶点法向量等信息给我们自定义的Geom赋值了,不仅仅是triangle。 最后把geom添加到positionvisitor管理的allgeom向量中
void PositionVisitor::dealTriangleInfo(ModelAttributeFunctor attributeFunctor, osg::TriangleIndexFunctor<TriangleIndex> indexFunctor)
{
Geom *geom = new Geom;
//cout<<attributeFunctor.textCoordList->size() << endl;
if (attributeFunctor.textCoordList->size()!=0
&&attributeFunctor.textCoordList->size()!=attributeFunctor.vertexList->size())
{
cout << "纹理坐标和顶点数量不匹配" << endl;
return;
}
//处理顶点信息
for (size_t i=0;i<attributeFunctor.vertexList->size();i++)
{
Vertex* vertex=new Vertex;
vertex->coor = attributeFunctor.vertexList->at(i);
vertex->index = i;
if (attributeFunctor.normalList!=NULL&&attributeFunctor.normalList->size()>i)
vertex->normal = attributeFunctor.normalList->at(i);
if (i< attributeFunctor.textCoordList->size())
vertex->texCoor = attributeFunctor.textCoordList->at(i);
geom->vertices.push_back(vertex);
}
//处理三角形信息
for (int i=0;i<indexFunctor.triangleNum;i++)
{
Triangle* triangle=new Triangle;
triangle->index = i;
triangle->vertexIndexs[0] = indexFunctor.indexs->at(i * 3);
triangle->vertexIndexs[1] = indexFunctor.indexs->at(i * 3+1);
triangle->vertexIndexs[2] = indexFunctor.indexs->at(i * 3+2);
//计算法向量
osg::Vec3 edge1 = geom->vertices.at(triangle->vertexIndexs[1])->coor - geom->vertices.at(triangle->vertexIndexs[0])->coor;
osg::Vec3 edge2 = geom->vertices.at(triangle->vertexIndexs[2])->coor - geom->vertices.at(triangle->vertexIndexs[0])->coor;
osg::Vec3 triangleNormal = edge1^edge2;
triangleNormal.normalize();
triangle->normal = triangleNormal;
geom->triangles.push_back(triangle);
}
allGeom.push_back(geom);
}
这样我们就讲一个osgb文件里面所有元数据存到了我们自定义的Geom中
最后我们调用createRandomColorOsgNode函数创建osg::Geode 然后把它给root(osg::group)
root->addChild(visitor.createRandomColorOsgNode(i));
创建Geode代码如下 用osg::group管理osg::Geode
osg::ref_ptr<osg::Node> PositionVisitor::createRandomColorOsgNode(int order)
{
//创建一个随机颜色
osg::Vec4 color = osg::Vec4(rand()%10*0.1, rand() % 10 * 0.1, rand() % 10 * 0.1, 1.0f);
this->geomColor = color;
return createOsgNode(color,order);
}
osg::ref_ptr<osg::Node> PositionVisitor::createOsgNode(osg::Vec4 color, int order)
{
this->geomColor = color;
short direction = order % 4;
osg::ref_ptr<osg::Group> result = new osg::Group;
if (allGeom.size()>0&&allGeom.size()==1)
{
cout << "createOsgNode" << endl;
osg::ref_ptr<osg::Geode> geode= allGeom[0]->createOsgNode(color);
this->boundingBox = geode->getBoundingBox();
result->addChild(geode);
}
else
{
for (Geom* geom : allGeom) {
osg::ref_ptr<osg::Geode> geode = geom->createOsgNode(color);
result->addChild(geode);
}
osg::ComputeBoundsVisitor boundsVisitor;
result->accept(boundsVisitor);
this->boundingBox = boundsVisitor.getBoundingBox();
}
result->addChild(createTipText(direction));
return result;
}
将geom成员变量的数据传给osg::Geometry,设定图元为三角形,然后把geometry传给geode(geode->addDrawable(geometry)),在最外层,geode是传给root(osg::group)的
osg::ref_ptr<osg::Geode> Geom::createOsgNode(osg::Vec4 color)
{
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
//顶点、法向量
osg::ref_ptr<osg::Vec3Array> vertexArray = new osg::Vec3Array;
osg::ref_ptr<osg::Vec3Array> normalArray = new osg::Vec3Array;
osg::ref_ptr<osg::Vec4Array> colorArray = new osg::Vec4Array;
osg::ref_ptr<osg::Vec2Array> textCoorArray = new osg::Vec2Array;
//索引
osg::ref_ptr<osg::DrawElementsUInt> indexs = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, 0);
for (Triangle* triangle : triangles)
{
indexs->push_back(triangle->vertexIndexs[0]);
indexs->push_back(triangle->vertexIndexs[1]);
indexs->push_back(triangle->vertexIndexs[2]);
//顶点的法向量数据全是0,由于一个三角形是2d的图形,所以我直接让一个三角形的三个顶点的法向量数据等于三角形的法向量
vertices[triangle->vertexIndexs[0]]->normal = triangle->normal;
vertices[triangle->vertexIndexs[1]]->normal = triangle->normal;
vertices[triangle->vertexIndexs[2]]->normal = triangle->normal;
}
for (Vertex* vertex : vertices)
{
vertexArray->push_back(vertex->coor);
normalArray->push_back(vertex->normal);
textCoorArray->push_back(vertex->texCoor);
}
//颜色
colorArray->push_back(color);
cout <<"triangles:"<< triangles.size() << endl;
cout << "vertices:"<<vertices.size() << endl;
geometry->setVertexArray(vertexArray);
geometry->setNormalArray(normalArray, osg::Array::BIND_PER_VERTEX);
geometry->setColorArray(colorArray, osg::Array::BIND_OVERALL);
geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
geometry->addPrimitiveSet(indexs);
geometry->setTexCoordArray(0,textCoorArray);
geode->addDrawable(geometry);
return geode;
}
上面需要注意的是,一开始画出来的模型是黑色的,设置了颜色也不行。后来发现通过ModelAttribute访问到的顶点法向量的数据都是(0,0,0),可能因为顶点没法确定法向量吧。因为最终画模型的图元是三角形,所以我通过三角形的三个点计算出来两条边,然后让这两条边做叉乘得到三角形的法向量,然后让三角形的法向量作为这个三角形三个顶点的法向量。
以上完成了Geode的创建和赋值,最后把管理Geode的root传给viewer,画出模型
viewer.setSceneData(root);
viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
viewer.addEventHandler(new osgViewer::StatsHandler);
viewer.setUpViewOnSingleScreen(1);//这里是单屏幕显示
viewer.run();
所以一个Geom就是网格中的一格
补充:以点云形式渲染模型
osg::ref_ptr<osg::Geode> Geom::createOsgNode_Point(osg::Vec4 color) {
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
osg::ref_ptr<osg::Vec3Array> vertexArray = new osg::Vec3Array;
osg::ref_ptr<osg::Vec3Array> normalArray = new osg::Vec3Array;
osg::ref_ptr<osg::Vec4Array> colorArray = new osg::Vec4Array;
//顶点的法向量数据全是0,由于一个三角形是2d的图形,所以我直接让一个三角形的三个顶点的法向量数据等于三角形的法向量
for (Triangle* triangle : triangles)
{
vertices[triangle->vertexIndexs[0]]->normal = triangle->normal;
vertices[triangle->vertexIndexs[1]]->normal = triangle->normal;
vertices[triangle->vertexIndexs[2]]->normal = triangle->normal;
}
for (Vertex* vertex : vertices)
{
vertexArray->push_back(vertex->coor);
normalArray->push_back(vertex->normal);
}
//颜色
colorArray->push_back(color);
cout << "triangles:" << triangles.size() << endl;
cout << "vertices:" << vertices.size() << endl;
geometry->setVertexArray(vertexArray);
geometry->setNormalArray(normalArray, osg::Array::BIND_PER_VERTEX);
geometry->setColorArray(colorArray, osg::Array::BIND_OVERALL);
geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
//将geom显示成点
osg::ref_ptr<osg::PrimitiveSet> primitiveSet = new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, vertices.size());
geometry->addPrimitiveSet(primitiveSet);
osg::StateSet* stateSet = geometry->getOrCreateStateSet();
osg::Point* pointSize = new osg::Point;
pointSize->setSize(4.0);
stateSet->setAttribute(pointSize);
geometry->addPrimitiveSet(primitiveSet);
geode->addDrawable(geometry);
return geode;
}