OSG编程指南<十>:OSG几何体的绘制

1、场景基本绘图类

  在 OSG 中创建几何体的方法比较简单,通常有 3 种处理几何体的手段:

  • 使用松散封装的OpenGL 绘图基元;
  • 使用 OSG 中的基本几何体;
  • 从文件中导入场景模型。

  使用松散封装的OpenGL 绘图基元绘制几何体具有很强的灵活性,但工作量通常非常大,当面对大型场景时,绘制几何体将是一项非常艰巨而富有挑战的工作,因此,通常还是采用读入外部模型的方法。

1.1 向量与数组类

  在 OSG 中定义了大量的类来保存数据,数据通常是以向量的形式来表示的,向量数据主要包括顶点坐标、纹理坐标、颜色和法线等。例如,定义 osg::Vec2 来保存纹理坐标;定义 osg::Vec3 来保存顶点坐标和法线坐标;定义 osg::Vec4 保存颜色的 RGBA 值。osg::Vec2、osg::Vec3 和 osg::Vec4 是分别用来保存向量的二维数组、三维数组和四维数组,这些类不仅能够保存各种数据,还提供了向量的基本运算机制,如加、减、乘、除四则元算、点积和单位化等相关的操作。
  在 OSG 中还定义了模板数组用来保存对象,例如,可以用顶点索引对象(osg::DrawElementsUInt)来保存顶点索引,用颜色索引(osg::TemplateIndexeArray)来保存颜色。但最常用的还是保存向量数据,如 osg::Vec3Array 来保存众多顶点坐标、osg::Vec2Array 保存纹理坐标等,这些模板数组都继承自 std::Vector,因此,它们具有向量的基本操作方法,例如,可以利用 push_back()添加一个元素,可以利用pop_back()删除一个元素,同样也可以使用 clear()删除所有的元素。

1.2 Drawable 类

  Drawable 类是一个纯基类,无法实例化。作为可绘制对象基类的 osg::Drawable 类,它派生了很多类,它的继承关系图如图 4-1 所示。
在这里插入图片描述
  从图 4-1 可以看出,由 osg::Drawable 派生的类有 9 个,分别是 osg::DrawPixels、osg::Geometry、osg::ShapeDrawable、osgParticle::ParticleSystem、osgParticle::PrecipitationEffect::PrecipitationDrawable、osgShadow::OccluderGeometry、osgShadow::ShadowVolumeGeometry、osgSim::ImpostorSprite 和 osgText::TextBase,其中,从 OSG 核心库派生出了 3 个类,分别是 osg::DrawPiexels 类(主要封装了 OpenGL中 glDrawPixels()的功能)、osg::Geometry 类(绘制几何体的类,应用比较灵活)和 osg::ShapeDrawable类(主要封装了一些已经定义好的几何体,不需要设置坐标即可直接调用,如长方体、正方体、球体
等)。其他的类中,有两个派生自粒子系统库,有两个派生自阴影库,还有两个分别派生自 osgSim 库和 osgText 文字库。

1.3 PrimitiveSet 类

  osg::PrimitiveSet 类继承自 osg::Object 虚基类,但它不具备一般场景中的特性。osg::PrimitiveSet 类的继承关系图如图 4-2 所示。
在这里插入图片描述
  该类主要松散封装了 OpenGL 的绘图基元,通过指定绘图基元来指定几何体顶点将采用哪一种或几种基元绘制。常用的绘图基元包括如下几种:

POINTS = GL_POINTS //绘制点
LINES = GL_LINES //绘制线
LINE_STRIP = GL_LINE_STRIP //绘制多段线
LINE_LOOP = GL_LINE_LOOP //绘制封闭线
TRIANGLES = GL_TRIANGLES //绘制一系列的三角形(不共用顶点)
TRIANGLE_STRIP = GL_TRIANGLE_STRIP //绘制一系列三角形(共用后面的两个顶点)
TRIANGLE_FAN = GL_TRIANGLE_FAN //绘制一系列三角形,顶点顺序与上一条语句绘制的三角形不同
QUADS = GL_QUADS //绘制四边形
QUAD_STRIP = GL_QUAD_STRIP //绘制一系列四边形
POLYGON = GL_POLYGON //绘制多边形

从 osg::PrimitiveSet 类的继承关系图可以看出,它的派生类主要有如下 3 个:

  • osg::DrawArrays 类。继承自 osg::PrimitiveSet,它封装了glDrawArrays()顶点数组绘图命令,用于指定顶点和绘图基元。
  • osg::DrawElements 类。它又派生出 3 个子类,分别是 osg::DrawElementsUByte、osg::DrawElementsUShort和osg::DrawElementsUInt,封装了 glDrawElements()的指令,可以起索引的作用,在后面的示例中会用到。
  • osg::DrawArrayLengths 类。它的主要作用是多次绘制,即多次调用glDrawArrays(),且每次均使用不同的长度和索引范围,在绘制过程中用得不是很多。

DrawArrays 的基本用法如下:

osg::DrawArrays::DrawArrays( GLenum mode, GLint first,GLsizei count );
/*参数说明:第一个参数是指定的绘图基元,即前面所列举的常见绘图基元;第二个参数是指绘制几何体的第一个
顶点数在指定顶点的位置数;第三个参数是使用的顶点的总数*/

  还有一点值得注意的是,虽然 osg::PrimitiveSet 类提供与 OpenGL 一样的顶点机制,但是在内部渲染上还是有一定区别的。根据渲染环境的不同,渲染的方式也是不一样的,可能会采用顶点、顶点数组、显示列表或者 glBegin()/glEnd()来渲染几何体,继承自 Drawable 类的对象(如 Geometry)在默认条件下将使用显示列表。其中,osg::Drawable::setUseDisplayList(false)用于手动禁止使用显示列表。还有一种比较特殊的情况,如果设置BIND_PER_PRIMITIVE 绑定方式,那么OSG将采用glBegin()/glEnd()函数进行渲染。因为在设置使用绑定方式为 BIND_PER_PRIMITIVE 后,它就为每个独立的几何图元设置一种绑定属性。

2、基本几何体的绘制

  OSG 创建的场景和对象是由简单的图元(我们把构成 3D 对象的构件称为图元)按照一定的方式排列和组合而成的,OSG 中的所有图元都是一维或二维对象,包括单个的点、直线和复杂的多边形。

2.1 几何体类 osg::Geometry

  它的主要作用是对指定绘制几何体的顶点数及对数据的解析,主要提供了如下 3大类方法:

1)指定向量数据。就是以前所涉及的顶点数据、纹理坐标及颜色等一系列向量数据,可以通过下面的几个函数来实现:

void setVertexArray(Array*array) //设置顶点数组
void setVertexData(const ArrayData&arrayData) //设置顶点数组数据
void setVertexIndices(IndexArray*array) //设置顶点索引数组
void setNormalArray(Array*array) //设置法线数组
void setNormalData(const ArrayData&arrayData) //设置法线数组数据
void setNormalIndices(IndexArray*array) //设置法线索引数组
void setColorArray(Array*array) //设置颜色数组
void setColorData(const ArrayData&arrayData) //设置颜色数组数据
void setColorIndices(IndexArray*array) //设置颜色索引数组
void setTexCoordArray(unsigned int unit,Array*)
//设置纹理坐标数组,第一个参数是纹理单元,第二个是纹理坐标数组
void setTexCoordData(unsigned int index, const ArrayData &arrayData)
//设置纹理坐标数组数据,第一个参数是纹理单元,第二个是纹理坐标数组数据
void setTexCoordIndices(unsigned int unit, IndexArray *)
//设置纹理坐标索引数组,第一个参数是纹理单元,第二个是纹理索引坐标数组

2)设置绑定方式。数据绑定主要有两项,即法线及颜色,可以通过下面的两个函数来实现:

void setNormalBinding(AttributeBinding ab) //设置法线绑定方式
void setColorBinding(AttributeBinding ab) //设置颜色绑定方式

绑定方式主要有下面几种:

BIND_OFF //不启用绑定
BIND_OVERALL //绑定全部的顶点
BIND_PER_PRIMITIVE_SET //单个绘图基元绑定
BIND_PER_PRIMITIVE //单个独立的绘图基元绑定
BIND_PER_VERTEX //单个顶点绑定

3)数据解析。当在指定了各种向量数据和绑定方式之后,采用何种方式来渲染几何体就是最为关键的。不同的方式下,渲染出来的图形是不一样的,即使效果一样,可能面数或内部机制等也是有区别的。数据解析主要通过如下函数来指定:

bool addPrimitiveSet(PrimitiveSet*primitiveset)
/*参数说明:osg::PrimitiveSet 是无法初始化的虚基类,因此这里主要是调用它的子类来指定数据渲染,最常用的就是
前面介绍的 osg::DrawArrays,用法比较简单,初始化一个对象实例,参数说明见前面 osg::DrawArrays 类*/

通过前面的讲述可知,绘制并渲染几何体主要有如下 3 大步骤:
1)创建各种向量数据,如顶点、纹理坐标、颜色和法线等。需要注意的是,添加顶点数据时主要按照逆时针顺序添加,以确保背面剔除(backface culling)的正确(后面还会有介绍)。
2)实例化一个几何体对象(osg::Geometry),设置顶点坐标数组、纹理坐标数组、颜色数组、法线数组、绑定方式及数据解析。
3)加入叶节点绘制并渲染。

2.2 示例

在这里插入图片描述

2.3 示例源码

#include <osgViewer/Viewer>

#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>

#include <osgDB/ReadFile>
#include <osgDB/WriteFile>

#include <osgUtil/Optimizer>

#include <osgViewer/ViewerEventHandlers> //事件监听
#include <osgGA/StateSetManipulator> //事件响应类,对渲染状态进行控制

#pragma comment(lib, "OpenThreadsd.lib")
#pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDBd.lib")
#pragma comment(lib, "osgUtild.lib")
#pragma comment(lib, "osgGAd.lib")
#pragma comment(lib, "osgViewerd.lib")
#pragma comment(lib, "osgTextd.lib")

//创建一个四边形节点
osg::ref_ptr<osg::Node> createQuad()
{
	//创建一个叶节点对象
	osg::ref_ptr<osg::Geode> geode = new osg::Geode();

	//创建一个几何体对象
	osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();

	//创建顶点数组
	osg::ref_ptr<osg::Vec3Array> aryVertex = new osg::Vec3Array();

	//添加数据
	aryVertex->push_back(osg::Vec3(0.0f, 0.0f, 0.0f));
	aryVertex->push_back(osg::Vec3(1.0f, 0.0f, 0.0f));
	aryVertex->push_back(osg::Vec3(1.0f, 0.0f, 1.0f));
	aryVertex->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));
	aryVertex->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));

	//设置顶点数据
	geom->setVertexArray(aryVertex.get());

	//创建四边形顶点索引数组,指定绘图基元为四边形,注意添加顺序
	osg::ref_ptr<osg::DrawElementsUInt> quad = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
	//添加数据
	quad->push_back(0);
	quad->push_back(1);
	quad->push_back(2);
	quad->push_back(3);

	//添加到几何体
	geom->addPrimitiveSet(quad.get());

	//创建三角形顶点索引数组,指定绘图基元为三角形,注意添加顺序
	osg::ref_ptr<osg::DrawElementsUInt> triangle = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, 0);
	//添加数据
	triangle->push_back(4);
	triangle->push_back(0);
	triangle->push_back(3);

	//添加到几何体
	geom->addPrimitiveSet(triangle.get());

	//创建颜色数组
	osg::ref_ptr<osg::Vec4Array> vecColor = new osg::Vec4Array();

	//添加数据
	vecColor->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f));
	vecColor->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
	vecColor->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
	vecColor->push_back(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f));
	vecColor->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));

	//设置颜色数组
	geom->setColorArray(vecColor.get());

	//创建颜色索引数组
	//osg::TemplateIndexArray<unsigned int ,osg::Array::UIntArrayType,4,4>* colorIndex = new osg::TemplateIndexArray<unsigned int ,osg::Array::UIntArrayType,4,4>();
	//添加数据,注意添加数据顺序与顶点一一对应
	//colorIndex->push_back(0);
	//colorIndex->push_back(1);
	//colorIndex->push_back(2);
	//colorIndex->push_back(3);
	//colorIndex->push_back(2);
	//设置颜色索引数组
	//geom->setColorIndices(colorIndex);

	//设置颜色的绑定方式为单个顶点
	geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);

	//创建法线数组
	osg::ref_ptr<osg::Vec3Array> vecNormal = new osg::Vec3Array();

	//添加法线
	vecNormal->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));

	//设置法线数组
	geom->setNormalArray(vecNormal.get());

	//设置法线的绑定方式为全部顶点
	geom->setNormalBinding(osg::Geometry::BIND_OVERALL);

	//添加到叶节点
	geode->addDrawable(geom.get());

	return geode.get();
}


int main()
{
	//创建Viewer对象,场景浏览器
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();

	osg::ref_ptr<osg::Group> root = new osg::Group();

	//添加到场景
	root->addChild(createQuad());

	//优化场景数据
	osgUtil::Optimizer optimizer;
	optimizer.optimize(root.get());

	viewer->setSceneData(root.get());

	//切换网格模式,方便比较
	viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));
	viewer->addEventHandler(new osgViewer::StatsHandler());//实现状态信息统计
	viewer->addEventHandler(new osgViewer::WindowSizeHandler());

	viewer->setUpViewInWindow(400,400,1000,800);

	viewer->run();

	return 0;
}

3、OSG预定义几何体

3.1 osg::Shape 类

  osg::Shape 类直接继承自 osg::Object 基类,继承关系图如图 4-6 所示。
在这里插入图片描述
  osg::Shape 类是各种内嵌几何体的基类,它不但可用于剔除和碰撞检测,还可用于生成预定义的几何体对象。常用的内嵌几何体包括如下几种:

osg::Box //正方体
osg::Capsule //太空舱
osg::Cone //椎体
osg::Cylinder //柱体
osg::HeightField //高度图
osg::InfinitePlane //无限平面
osg::Sphere //球体
osg::TriangleMesh //三角片

3.2 osg::ShapeDrawable 类

  在 OSG 中内嵌预定义的几何体,如果渲染这些内嵌的几何体,就必须将其与 osg::Drawable 关联。实际应用中,可以使用 osg::Drawable 类的派生类 osg::ShapeDrawable来完成这个功能。osg::ShapeDrawable 类在前面已经讲到,它派生自osg::Drawable 类。由于它继承自 osg::Drawable 类,所以它的实例需要被添加到叶节点中才能被实例绘制。
在 osg::ShapeDrawable 类的构造函数中提供了关联 osg::Shape 的方法:

ShapeDrawable(Shape*shape, TessellationHints *hints=0)
//第一个参数为 shape,第二个参数默认下不细化

3.3 网格化类

  网格化类(osg::TessellationHints)直接继承自 osg::Object 基类。osg::TessellationHints 类的主要作用是设置预定义几何体对象的精细程度,精细程度越高,表示其细分越详细,但对于不同的预定义几何体对象它的作用是不一样的,例如:

 Box(四棱柱):网格化类对于四棱柱没有意义。
 Capsule(太空舱):太空舱分 3 个部分,上下半球部分和圆柱侧面部分,默认圆柱侧面被细分。
 Cone(圆锥):直接细分。
 Cylinder(柱体):直接细分。
 Sphere(球):直接细分。

目前,osg::TessellationHints 类并不完整,部分类成员函数还没有实现,具体可以参看源码。在内嵌几何体对象中,默认的情况下,网格化类的精细度为 0,表示预定义的几何体此时按照原顶点默认绘制,不做任何细化处理。

3.4 示例

在这里插入图片描述

3.5 示例源码

#include <windows.h>

#include <osgViewer/Viewer>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/Switch>
#include <osg/Billboard>
#include <osg/Texture2D>
#include <osg/Image>
#include <osg/Vec3>
#include <osg/Vec2>
#include <osg/PositionAttitudeTransform>
#include <osg/MatrixTransform>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgUtil/Optimizer>
#include <osg/PagedLOD>
#include <osgSim/Impostor>
#include <osgViewer/ViewerEventHandlers> //事件监听
#include <osgGA/StateSetManipulator> //事件响应类,对渲染状态进行控制
#include <osgUtil/Simplifier> //简化几何体
#include <osg/OccluderNode>
#include <osg/StateSet>
#include <osg/ConvexPlanarOccluder>
#include <osg/BoundingBox>
#include <osg/BoundingSphere>
#include <osgUtil/Optimizer>
#include <iostream>
#include <osg/ShapeDrawable>

#pragma comment(lib, "OpenThreadsd.lib")
#pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDBd.lib")
#pragma comment(lib, "osgUtild.lib")
#pragma comment(lib, "osgGAd.lib")
#pragma comment(lib, "osgViewerd.lib")
#pragma comment(lib, "osgTextd.lib")
#pragma comment(lib, "osgSimd.lib")
#pragma comment(lib, "osgFXd.lib")

//绘制多个预定义的几何体
osg::ref_ptr<osg::Geode> createShape()
{
	//创建一个叶节点
	osg::ref_ptr<osg::Geode> geode = new osg::Geode();

	//设置半径和高度
	float radius = 0.8f;
	float height = 1.0f;

	//创建精细度对象,精细度越高,细分就越多
	osg::ref_ptr<osg::TessellationHints> hints = new osg::TessellationHints;
	//设置精细度为 0.5f
	hints->setDetailRatio(0.5f);

	//添加一个球体,第一个参数是预定义几何体对象,第二个是精细度,默认为 0
	geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0f, 0.0f, 0.0f), radius), hints.
		get()));
	//添加一个正方体
	geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(2.0f, 0.0f, 0.0f), 2 * radius), hints.
		get()));
	//添加一个圆锥
	geode->addDrawable(new osg::ShapeDrawable(new osg::Cone(osg::Vec3(4.0f, 0.0f, 0.0f), radius, height), hints.
		get()));
	//添加一个圆柱体
	geode->addDrawable(new osg::ShapeDrawable(new osg::Cylinder(osg::Vec3(6.0f, 0.0f, 0.0f), radius, height),
		hints.get()));
	//添加一个太空舱
	geode->addDrawable(new osg::ShapeDrawable(new osg::Capsule(osg::Vec3(8.0f, 0.0f, 0.0f), radius, height),
		hints.get()));

	return geode.get();
}


int main()
{
	//创建Viewer对象,场景浏览器
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();

	osg::ref_ptr<osg::Group> root = new osg::Group();

	//添加到场景
	root->addChild(createShape());

	//优化场景数据
	osgUtil::Optimizer optimizer;
	optimizer.optimize(root.get());

	viewer->setSceneData(root.get());

	//切换网格模式,方便比较
	viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));
	viewer->addEventHandler(new osgViewer::StatsHandler());//实现状态信息统计
	viewer->addEventHandler(new osgViewer::WindowSizeHandler());

	viewer->setUpViewInWindow(400,400,1000,800);

	viewer->run();

	return 0;
}

4、多边形分格化

4.1 定义

  如果读者对 OpenGL 有一定了解的话,应该知道 OpenGL 为了快速渲染多边形,只能直接显示简单的凸多边形。所谓简单的凸多边形,就是多边形上任意两点的连线上的点依属于该多边形。对凹多边形或者自交叉多边形的渲染结果将不确定。下面列举一些需要分格化的多边形,如图 4-10 所示。
在这里插入图片描述

  为了正确显示凹多边形或者自交叉多边形,就必须把它们分解为简单的凸多边形,这种做法就称为多边形的分格化。OSG 是对底层 OpenGL API 的封装,所以它同样只能直接显示简单的凸多边形,对于凹多边形或者自交叉多边形,渲染也是不确定的。
  在 OSG 中提供了一个多边形分格化的类 osgUtil::Tessellator,它继承自 osg::Referenced 类,继承关系图如图 4-11 所示。
在 OSG 中进行多边形分格化渲染需要如下 3 个步骤:
(1)创建多边形分格化对象。
(2)设置分格化对象的类型,通常有下面 3 种类型:

TESS_TYPE_GEOMETRY, //分格化几何体
TESS_TYPE_DRAWABLE, //分格化几何体中的 Drawable(如多边形、三角形、四边形等)
TESS_TYPE_POLYGONS //只分格化几何体中的多边形

(3)根据计算的环绕数指定相应的环绕规则。

1.环绕数
在《OpenGL 编程指南》第 5 版中曾指出“对于一条简单的轮廓线,每个点的环绕数就是环绕这个点的所有轮廓线的代数和(用一个有符号的整数表示,求和规则是:逆时针环绕的轮廓线为正,顺时针环绕的轮廓线为负)。这个过程把一个有符号的整数数值与平面上的每个顶点相关联。注意,对于区域中的所有点,它们的环绕数都是相同的”。图 4-12 为轮廓线与环绕数的计算方法,读者可以通过此图理解环绕数及如何计算环绕数。
在这里插入图片描述

2.环绕规则
如果一个区域的环绕数属于环绕规则所选择的类型,那么它就是它的内部区域。通常,环绕规则把具有奇数和非零环绕数的区域定义为内部区域。环绕规则主要是针对环绕数来确定的。
几种常用的环绕规则如下:

TESS_WINDING_ODD=GLU_TESS_WINDING_ODD //环绕数为奇数
TESS_WINDING_NONZERO=GLU_TESS_WINDING_NONZERO //环绕数为非零数
TESS_WINDING_POSITIVE=GLU_TESS_WINDING_POSITIVE //环绕数为正数
TESS_WINDING_NEGATIVE=GLU_TESS_WINDING_NEGATIVE //环绕数为负数
TESS_WINDING_ABS_GEQ_TWO=GLU_TESS_WINDING_ABS_GEQ_TWO //环绕数为绝对值大于或等于 2

4.2 示例

在这里插入图片描述

4.3 示例源码

#include <windows.h>

#include <osgViewer/Viewer>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/Switch>
#include <osg/Billboard>
#include <osg/Texture2D>
#include <osg/Image>
#include <osg/Vec3>
#include <osg/Vec2>
#include <osg/PositionAttitudeTransform>
#include <osg/MatrixTransform>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgUtil/Optimizer>
#include <osg/PagedLOD>
#include <osgSim/Impostor>
#include <osgViewer/ViewerEventHandlers> //事件监听
#include <osgGA/StateSetManipulator> //事件响应类,对渲染状态进行控制
#include <osgUtil/Simplifier> //简化几何体
#include <osg/OccluderNode>
#include <osg/StateSet>
#include <osg/ConvexPlanarOccluder>
#include <osg/BoundingBox>
#include <osg/BoundingSphere>
#include <osgUtil/Optimizer>
#include <iostream>
#include <osg/ShapeDrawable>
#include <osgUtil/Tessellator>

#pragma comment(lib, "OpenThreadsd.lib")
#pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDBd.lib")
#pragma comment(lib, "osgUtild.lib")
#pragma comment(lib, "osgGAd.lib")
#pragma comment(lib, "osgViewerd.lib")
#pragma comment(lib, "osgTextd.lib")
#pragma comment(lib, "osgSimd.lib")
#pragma comment(lib, "osgFXd.lib")

//使用分格化绘制凹多边形
osg::ref_ptr<osg::Geode> tesslatorGeometry()
{
	osg::ref_ptr<osg::Geode> geode = new osg::Geode();

	osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();
	geode->addDrawable(geom.get());

	//以下是一些顶点数据
	//墙
	const float wall[5][3] =
	{ { 2200.0f, 0.0f, 1130.0f },
	{ 2600.0f, 0.0f, 1130.0f },
	{ 2600.0f, 0.0f, 1340.0f },
	{ 2400.0f, 0.0f, 1440.0f },
	{ 2200.0f, 0.0f, 1340.0f } };

	//门
	const float door[4][3] =
	{ { 2360.0f, 0.0f, 1130.0f },
	{ 2440.0f, 0.0f, 1130.0f },
	{ 2440.0f, 0.0f, 1230.0f },
	{ 2360.0f, 0.0f, 1230.0f } };

	//四扇窗户
	const float windows[16][3] =
	{ { 2240.0f, 0.0f, 1180.0f },
	{ 2330.0f, 0.0f,1180.0f },
	{ 2330.0f, 0.0f, 1220.0f },
	{ 2240.0f, 0.0f, 1220.0f },
	{ 2460.0f, 0.0f, 1180.0f },
	{ 2560.0f, 0.0f, 1180.0f },
	{ 2560.0f, 0.0f, 1220.0f },
	{ 2460.0f, 0.0f, 1220.0f },
	{ 2240.0f, 0.0f, 1280.0f },
	{ 2330.0f, 0.0f, 1280.0f },
	{ 2330.0f, 0.0f, 1320.0f },
	{ 2240.0f, 0.0f, 1320.0f },
	{ 2460.0f, 0.0f, 1280.0f },
	{ 2560.0f, 0.0f, 1280.0f },
	{ 2560.0f, 0.0f, 1320.0f },
	{ 2460.0f, 0.0f, 1320.0f } };

	//设置顶点数据
	osg::ref_ptr<osg::Vec3Array> coords = new osg::Vec3Array();
	geom->setVertexArray(coords.get());

	//设置法线
	osg::ref_ptr<osg::Vec3Array> normal = new osg::Vec3Array();
	normal->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));
	geom->setNormalArray(normal.get());
	geom->setNormalBinding(osg::Geometry::BIND_OVERALL);

	//添加墙
	for (int i = 0; i < 5; i++)
	{
		coords->push_back(osg::Vec3(wall[i][0], wall[i][1], wall[i][2]));

	}
	geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, 0, 5));


	//添加门
	for (int i = 0; i < 4; i++)
	{
		coords->push_back(osg::Vec3(door[i][0], door[i][1], door[i][2]));
	}

	//添加窗
	for (int i = 0; i < 16; i++)
	{
		coords->push_back(osg::Vec3(windows[i][0], windows[i][1], windows[i][2]));
	}
	geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 5, 20));

	//创建分格化对象
	osg::ref_ptr<osgUtil::Tessellator> tscx = new osgUtil::Tessellator();
	//设置分格类型为几何体
	tscx->setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
	//设置只显示轮廓线为 false,这里还需要填充
	tscx->setBoundaryOnly(false);
	//设置环绕规则
	tscx->setWindingType(osgUtil::Tessellator::TESS_WINDING_ODD);

	//使用分格化
	tscx->retessellatePolygons(*(geom.get()));

	return geode.get();
}

int main()
{
	//创建Viewer对象,场景浏览器
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();

	osg::ref_ptr<osg::Group> root = new osg::Group();

	//添加到场景
	root->addChild(tesslatorGeometry());

	//优化场景数据
	osgUtil::Optimizer optimizer;
	optimizer.optimize(root.get());

	viewer->setSceneData(root.get());

	//切换网格模式,方便比较
	viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));
	viewer->addEventHandler(new osgViewer::StatsHandler());//实现状态信息统计
	viewer->addEventHandler(new osgViewer::WindowSizeHandler());

	viewer->setUpViewInWindow(400,400,1000,800);

	viewer->run();

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/183893.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker Swarm总结+service创建和部署、overlay网络以及Raft算法(2/3)

博主介绍&#xff1a;Java领域优质创作者,博客之星城市赛道TOP20、专注于前端流行技术框架、Java后端技术领域、项目实战运维以及GIS地理信息领域。 &#x1f345;文末获取源码下载地址&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb;…

在vscode中添加代码提示

添加配置 run->add_configuration 添加头文件路径 在c_cpp_properties.json中添加头文件路径 效果

PS 计数工具 基础使用方式讲解

上文PS 注释工具 基础使用方法讲解 中 我们讲了注释工具 解析来 我们来看这个计数工具 这里 我们换一张图像 如果 我要你数清楚 这个图上有几个咖啡豆 你能数清楚吗&#xff1f; 哈哈 其实也不难 不是特别大 但是 例如很多 且无规则物品时 我们可能就会数乱 左上角属性的话 我…

APP软件外包开发流程

外包APP软件项目的开发流程可以分为以下几个主要阶段&#xff0c;在整个流程中&#xff0c;沟通和合作是至关重要的&#xff0c;确保开发团队和客户之间有良好的沟通渠道&#xff0c;及时解决问题&#xff0c;保证项目按时交付。北京木奇移动技术有限公司&#xff0c;专业的软件…

时间敏感网络TSN的车载设计实践: 802.1Qbv协议

▎概述 IEEE 802.1Qbv[1]是TSN系列协议中备受关注的技术之一&#xff0c;如图1所示&#xff0c;它定义了一种时间感知整形器&#xff08;Time Aware Shaper&#xff0c;TAS&#xff09;&#xff0c;支持Qbv协议的交换机可以按照配置好的门控列表来打开/关闭交换机出口队列&…

前端编码技巧须知

前端开发中可能会使用到以下软件&#xff0c;它们各自具有不同的作用&#xff1a; 代码编辑器&#xff1a;例如Sublime Text、Atom、Visual Studio Code等&#xff0c;用于编写和编辑HTML、CSS和JavaScript等前端代码。网页浏览器&#xff1a;例如Chrome、Firefox、Safari等&a…

“关爱零距离.情暖老人心”主题活动

为提高社区老年人的生活质量&#xff0c;促进邻里间的互动与友谊&#xff0c;以及弘扬尊老爱幼的社区精神&#xff0c;11月21日山东省潍坊市金阳公益服务中心、重庆市潼南区同悦社会工作服务中心在潼南区桂林街道东风社区共同在潼南区桂林街道东风社区举办了“关爱零距离.情暖老…

EMG肌肉电信号处理合集(三)

本文主要展示常见的肌电信号预处理的实现&#xff0c;开发环境为matlab。 目录 1 肌电信号低通&#xff0c;高通&#xff0c;带通滤波 2 去除DC 0阶偏置&#xff0c;1阶偏置 3 全波整流 4 信号降采样 5 linear envolope / butterworth 低通滤波器 1 肌电信号低通&#xf…

【elementui】el-popover在列表里循环使用,取消的doClose无效解决办法

目录 一、需求效果二、代码详情html方法接口 一、需求效果 在使用elementui的Popover 弹出框时&#xff0c;需求是在table列表里使用&#xff0c;循环出来&#xff0c;无法取消。 二、代码详情 html <el-table-column v-if"checkPermission([admin,user:resetPass…

DevExpress中文教程 - 如何在macOS和Linux (CTP)上创建、修改报表(下)

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表。 DevExpress Reports — 跨平台报表组件&#x…

TOGAF®9持证人员专属升级福利—标准第10版认证过渡路径学习项目来啦!

TOGAF标准的含金量有多高&#xff1f; 是这些企业的优先选择 ▼ DXC Technology, Fujitsu, HCL Technologies, 华为, IBM, Intel, OpenText, Philips, Amazon, Apple, Capgemini, CISCO, Deloitte, HP, Microsoft, Oracle, SAP&#xff0c;国航&#xff0c;东航&#xff0c;工…

AI“胡说八道”?怎么解?

原创 | 文 BFT机器人 01 引言 近年来&#xff0c;人工智能产业迅猛发展&#xff0c;大型语言模型GPT-4发展势头强劲&#xff0c;OpenAI推出ChatGPT、微软推出Bing、马斯克推出“最好的聊天机器人Grok”……科技巨头纷纷入局AI领域&#xff0c;引入人工智能作为办公工具的行业…

java项目之品牌银饰售卖平台(ssm+vue)

项目简介 主要功能包括首页、个人中心、用户管理、促销活动管理、饰品管理、我的收藏管理、系统管理、订单管理等。管理员模块: 管理员可以查询、编辑、管理每个用户的信息和系统管理员自己的信息&#xff0c;同时还可以编辑、修改、查询用户账户和密码&#xff0c;以及对系统…

如何从 C# 制作报表到 FastReport Cloud

众所周知&#xff0c;我们的世界在不断发展&#xff0c;新技术几乎每天都会出现。如今&#xff0c;不再需要在办公室内建立整个基础设施、雇用人员来监控设备、处理该设备出现的问题和其他困难。 如今&#xff0c;越来越多的服务提供业务云解决方案&#xff0c;例如FastReport…

GEE:梯度提升树(Gradient Boosting Tree)分类教程(样本制作、特征添加、训练、精度、参数优化、贡献度、统计面积)

作者:CSDN @ _养乐多_ 本文将介绍在Google Earth Engine (GEE)平台上进行梯度提升树(Gradient Boosting Tree)分类的方法和代码,其中包括制作样本点教程(本地、在线和本地在线混合制作样本点,合并样本点等),加入特征变量(各种指数、纹理特征、时间序列特征、物候特征…

gitea仓库镜像同步至gitlab

1、参考文档&#xff1a;仓库镜像 | Gitea Documentation 2、错误一&#xff1a;账号密码错误问题 解决方法&#xff1a; 出现以上错误为第三步用户名&#xff08;Oauth2应用名称&#xff09;或者密码&#xff08;Gitlab个人访问令牌&#xff09;错误。 1&#xff09;如下图1…

NX二次开发UF_CURVE_add_faces_ocf_data 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_add_faces_ocf_data Defined in: uf_curve.h int UF_CURVE_add_faces_ocf_data(tag_t face_tag, UF_CURVE_ocf_data_p_t uf_offset_data ) overview 概述 Add a face col…

安索夫矩阵(ANSOFF)

&#x1f449;安索夫矩阵是策略管理之父安索夫博士于1957年提出的营销策略模型&#xff0c;该模型以“产品”和“市场”作为两大基本面&#xff0c;提出了4种不同组合下的营销策略&#xff0c;是营销分析中应用最广泛的工具之一。其主要逻辑是通过选择4种不同的成长性策略来实现…

基于springboot实现冬奥会科普平台系统【项目源码+论文说明】

基于SpringBoot实现冬奥会科普平台系统演示 摘要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理平台应运而生&…

在arm 64 环境下使用halcon算法

背景&#xff1a; halcon&#xff0c;机器视觉领域神一样得存在&#xff0c;在windows上&#xff0c;应用得特别多&#xff0c; 但是arm环境下使用得很少。那如何在arm下使用halcon呢。按照官方说明&#xff0c;arm下只提供了运行时环境&#xff0c;并且需要使用价值一万多人民…