第39节 显示模型的法线

本节内容

经常的我们想看看模型的法线对不对,如本节下图所示:
在这里插入图片描述
我们就想把模型的法线绘制出来。本节就介绍这个小功能。

本节代码

本节资源在如下链接/文件中的附件/按章节编号目录中:
【击此打开网盘资源链接】

也可以在本文末获取

思路

遍历所有的Geode,然后将其里面所有的Geometry的,凡是有法线的,都给生成法线。法线的起点是该顶点,方向是法线的方向,长度可以根据包围盒的半径大小来。

具体实现

1、我们遍历模型的所有的Geode

class BuildNormal : public osg::NodeVisitor
{
public:
	BuildNormal() :osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}

	virtual void apply(osg::Geode& gnode)
	{
		在这里做事情
	}
}

2、遍历gnode中所有的Geometry

	virtual void apply(osg::Geode& gnode)
	{
		//先看其里有多少个geometry,给每个geometry的每个顶点的法线生成一条实体线,再添加到gnode中
		//因为添加之后gnode的子结点就会增多,为了避免给法线的实体线再生成法线,因此这里先获取
		int geomNum = gnode.getNumDrawables();
		for (int i = 0; i < geomNum; i++)
		{
			osg::Geometry* geom = gnode.getDrawable(i)->asGeometry();
			if (nullptr != geom)
			{
			   //在这里搞事情
			}
		}
		traverse(gnode);
	}

3、如果geom有逐顶点绑定的法线则申请几何体开始绘制:

			if (nullptr != geom)
			{
				//先判断有法线而且是逐顶点绑定
				osg::Vec3Array* normal = dynamic_cast<osg::Vec3Array*>(geom->getNormalArray());
				if ((nullptr != normal) && (geom->getNormalBinding() == osg::Array::BIND_PER_VERTEX))
				{
					//给法线生成Geometry
					osg::Geometry* geomNormalLine = new osg::Geometry();
					gnode.addDrawable(geomNormalLine);
					//关闭光照
					geomNormalLine->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);

					//申请顶点
					osg::Vec3Array* nVertex = new osg::Vec3Array;
					geomNormalLine->setVertexArray(nVertex);
					//申请颜色数组
					osg::Vec4Array* nColor = new osg::Vec4Array;
					geomNormalLine->setColorArray(nColor, osg::Array::BIND_PER_VERTEX);

					//为每个顶点生成一条线
					{
						osg::Vec3Array* vertex = dynamic_cast<osg::Vec3Array*>(geom->getVertexArray());
						if (nullptr != vertex)
						{
							for (int j = 0; j < vertex->size(); j++)
							{
								//压入当前顶点为第一个顶点
								nVertex->push_back(vertex->at(j));
								//压入颜色
								osg::Vec3 colorTemp = vertex->at(j);
								colorTemp.normalize();
								nColor->push_back(osg::Vec4(colorTemp.x(), colorTemp.y(), colorTemp.z(), 1.0));

								//得到法线
								osg::Vec3 normalTemp = normal->at(j);
								nColor->push_back(osg::Vec4(normalTemp.x(), normalTemp.y(), normalTemp.z(), 1.0));

								//得到顶点,为在法线方向上延长当前包围盒的半径的0.1倍
								float n = gnode.getBound().radius() * 0.1;
								nVertex->push_back(vertex->at(j) + normalTemp * n);
							}
						}
					}
					geomNormalLine->addPrimitiveSet(new osg::DrawArrays(GL_LINES, 0, nVertex->size()));
				}
			}

注意

为什么要把绘制的geometry加到与法线所在的geometry同一个gnode当中,原因是这个gnode可能经过了平移旋转等操作,如果在根结点单独申请gnode,这样的操作复制起来就很麻烦。

以下为全部代码

// c.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/NodeVisitor>

//为模型生成法线,法线放在同一个几何体当中
class BuildNormal : public osg::NodeVisitor
{
public:
	BuildNormal() :osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}

	virtual void apply(osg::Geode& gnode)
	{
		//先看其里有多少个geometry,给每个geometry的每个顶点的法线生成一条实体线,再添加到gnode中
		//因为添加之后gnode的子结点就会增多,为了避免给法线的实体线再生成法线,因此这里先获取
		int geomNum = gnode.getNumDrawables();
		for (int i = 0; i < geomNum; i++)
		{
			osg::Geometry* geom = gnode.getDrawable(i)->asGeometry();
			if (nullptr != geom)
			{
				//先判断有法线而且是逐顶点绑定
				osg::Vec3Array* normal = dynamic_cast<osg::Vec3Array*>(geom->getNormalArray());
				if ((nullptr != normal) && (geom->getNormalBinding() == osg::Array::BIND_PER_VERTEX))
				{
					//给法线生成Geometry
					osg::Geometry* geomNormalLine = new osg::Geometry();
					gnode.addDrawable(geomNormalLine);
					//关闭光照
					geomNormalLine->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);

					//申请顶点
					osg::Vec3Array* nVertex = new osg::Vec3Array;
					geomNormalLine->setVertexArray(nVertex);
					//申请颜色数组
					osg::Vec4Array* nColor = new osg::Vec4Array;
					geomNormalLine->setColorArray(nColor, osg::Array::BIND_PER_VERTEX);

					//为每个顶点生成一条线
					{
						osg::Vec3Array* vertex = dynamic_cast<osg::Vec3Array*>(geom->getVertexArray());
						if (nullptr != vertex)
						{
							for (int j = 0; j < vertex->size(); j++)
							{
								//压入当前顶点为第一个顶点
								nVertex->push_back(vertex->at(j));
								//压入颜色
								osg::Vec3 colorTemp = vertex->at(j);
								colorTemp.normalize();
								nColor->push_back(osg::Vec4(colorTemp.x(), colorTemp.y(), colorTemp.z(), 1.0));

								//得到法线
								osg::Vec3 normalTemp = normal->at(j);
								nColor->push_back(osg::Vec4(normalTemp.x(), normalTemp.y(), normalTemp.z(), 1.0));

								//得到顶点,为在法线方向上延长当前包围盒的半径的0.1倍
								float n = gnode.getBound().radius() * 0.1;
								nVertex->push_back(vertex->at(j) + normalTemp * n);
							}
						}
					}
					geomNormalLine->addPrimitiveSet(new osg::DrawArrays(GL_LINES, 0, nVertex->size()));
				}
			}

		}
		traverse(gnode);
	}
};


int main()
{
	osgViewer::Viewer viewer;

	//读一个模型
	osg::Node* pNode = osgDB::readNodeFile("glider.osg");

	//为其生成法线
	BuildNormal bn;
	pNode->accept(bn);

	viewer.setSceneData(pNode);
	viewer.run();
	return 0;
}

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片