//Common.h
#ifndef _COMMON_H_
#define _COMMON_H_
#indef _DEBUG
#pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDBd.lib")
#pragma comment(lib, "osgViewerd.lib")
#pragma comment(lib, "OoenThreadsd.lib")
#else
#pragma comment(lib, "osg.lib")
#pragma comment(lib, "osgDB.lib")
#pragma comment(lib, "osgdViewer.lib")
#pragma comment(lib, "OoenThreads.lib")
#endif
#endif
有一个引用指向该资源,该资源就不应该被释放。所以,大多数的 OSG 对象都有一个内部的计数器用来计算有多少个指针指向它。当没有指针指向该资源(同样,可以称做对象)时,它的计数器会变为 0,当计算器为 0 时就可以放掉对象。幸运的是,我们程序员不需要手工的来维护这些引用计数:这就是超级指针为什么存在的原因。在 OSG 当中超级指针被一个称为 osg::ref_ptr<>的类模版来实现。无论在什么时候,只要该对象被引用,则在 osg::ref_ptr<>当中的计数器会自动增加, 通过这种方式实现了资源的自动管理,当不需要使用它时,它会在不久后释放,在申请它时会自动分配空间。
int main(){
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
osg::ref_ptr<osg::Node> node = new osg::Node;
node = osgDB::readNodeFile("glider.osg");
viewer->setSceneData(node);
return viewer->run();
}
之前通过主函数main进行视图管理(osgViewer),利用osgDB进行模型读写,将读写的模型放在结点指针中,再将结点指针放在视图中,最后运行视图(run)从而达到显示模型的目的;
现在增加一个事件管理(osgGA),基于事件的小应用(EventHandler);
1.运行glider模型
2.点击‘h’按键,显示文字,如下图
#include
#include
//这两个是有关事件管理的头文件
//添加帮助事件
viewer->addEventHandler(new osgViewer::HelpHandler);
此时运行项目,点击h后会出现文字信息,但却只有几行,这是因为osg在帮助事件的类中设置了相应显示文字的行数,可根据需要更改,这里
//模拟一个事件类,相应单击h
class PrintName : public osgGA::GUIEventHandler
{
public:
virtual void getUsage(osg::ApplicationUsage& usage) const {
usage.addKeyboardMouseBinding("lk", "Hello!");//显示文字的最后一行内容
}
};
viewer->addEventHandler(new osgViewer::HelpHandler); //显示帮助文档的事件
viewer->addEventHandler(new PrintName);//显示我们新建的事件类
还有事件类如下:
viewer->addEventHandler(new osgViewer::StatsHandler);
viewer->addEventHandler(new osgViewer::WindowSizeHandler);
viewer->addEventHandler(new osgViewer::ScreenCaptureHandler);
//申请一个定时器类
osg::Timer * timer = new osg::Timer;
osg::Timer_t start_time = 0;
osg::Timer_t end_time = 0;
//得到一个tick值为多少second
std::cout << timer->getSecondsPerTick() << std::endl;
start_time = timer->tick();
//读取模型
node = osgDB::readNodeFile("glider.osg");
end_time = timer->tick();
std::cout << "读取模型时间为: " << timer->delta_s(start_time, end_time) << std::endl;
注意,在申请定时器类时,不能使用超级指针:
osg::ref_ptr<osg::Timer> timer = new osg::Timer; //错误示范,有些类不支持超级指针
第二种计算读取模型时间的方法:
start_time = timer->tick();
timer->setStartTick();
//读取模型
node = osgDB::readNodeFile("glider.osg");
std::cout << "方法一读取模型时间为:" <<timer->time_s()<<std::endl;
end_time = timer->tick();
std::cout << "方法二读取模型时间为: " << timer->delta_s(start_time, end_time) << std::endl;
并且预计方法一时间比方法二短一点,因为方法二多执行了几条语句,所以这样可以计算出中间语句的执行时间。如果在计算程序效率时,可以根据此方法查询是哪条语句耗时太多,从而进行优化或者将其功能删掉。
方法三:利用viewer自带的elapsedTime()函数得出start_time和end_time
float start_time = 0;
float end_time = 0;
start_time = viewer=>elapsedTime();
node = osgDB::readNodeFile("cow.osg");
end_time = viewer->elapsedTime();
std::cout<<"方法三读取模型时间为: "<<end_time-start_time<<std::endl;
//操作器的头文件
#include
osg::Timer_t start_frame_time = 0;
osg::Timer_t end_frame_time = 0;
//计算帧速
int counts = 0;
//加操作器
viewer->setCameraManipulator(new osgGA::TrackballManipulator);
while (!viewer->done()) {
if (counts == 0) {
start_frame_time = timer->tick();
}
counts++;
viewer->frame();
if (counts == 3) {
counts = 0;
end_frame_time = timer->tick();
std::cout << "当前帧速为: " << 3 / (timer->delta_s(start_frame_time, end_frame_time)) << std::endl;
}
}
return 0;
其中加了个counts变量,因为一帧一帧查看会因为模型静止等原因而与实际帧速不相符,所以我们每三帧取一次值,保证准确性
这是绘制自带的长方体,对其大小进行设置,在贴一些纹理,达到所需效果
#include
#include
#include
#include
#include //增加图元类
#include //增加材质,让图形更有是体感,更好看
#include
#include //贴纹理
#include //贴纹理
#include
osg::ref_ptr<osg::Geode> CreateBox() {
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
osg::ref_ptr<osg::TessellationHints> hints = new osg::TessellationHints; //设置精度类
osg::ref_ptr<osg::ShapeDrawable> shape = new osg::ShapeDrawable(
new osg::Box(osg::Vec3(0.0,0.0,0.0),1.0,10.0,10.0),hints.get()); //建立一个图元
osg::ref_ptr<osg::Material> material = new osg::Material;
osg::ref_ptr<osg::Texture2D> texture2D = new osg::Texture2D;
osg::ref_ptr<osg::Image> image;
shape->setColor(osg::Vec4(0.5, 0.5, 0.5, 0.1)); //设置颜色
hints->setDetailRatio(0.5); //设置精度
//设置材质
material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.0, 1.0, 1.0, 0.5));
material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.0, 1.0, 1.0, 0.5)); //混合光
material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(1.0, 1.0, 1.0, 0.5)); //反射光
material->setShininess(osg::Material::FRONT_AND_BACK, 6.0); //反射面积,一个点还是一个面
//设置纹理
image = osgDB::readImageFile("Images/whitemetal_diffuse.jpg");
if (image.valid()) { //如果纹理图片是可用的
texture2D->setImage(image.get()); //则获取该图片
}
geode->getOrCreateStateSet()->setAttributeAndModes(material.get(), osg::StateAttribute::ON); //将所有质感设置添加到图元中
geode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON); //打开深度测试
geode->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); //设置透明度
geode->getOrCreateStateSet()->setTextureAttributeAndModes(0,texture2D.get(),osg::StateAttribute::ON); //添加纹理
geode->addDrawable(shape.get());
return geode;
}
int main() {
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
viewer->setSceneData(CreateBox());
//viewer->setSceneData(osgDB::readNodeFile("glider.osg"));
return viewer->run();
}
接下来自行绘制图形,建立四个点,逆时针相连,得出一个长方形,在为其设置颜色,线宽,以及透明度
/创建简单图元
osg::ref_ptr<osg::Node> CreateSimple() {
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
//申请一些顶点
osg::ref_ptr<osg::Vec3Array> coords = new osg::Vec3Array;
//申请一些颜色
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
//申请法向量
osg::ref_ptr<osg::Vec3Array> norms = new osg::Vec3Array;
//申请线宽
osg::ref_ptr<osg::LineWidth> width = new osg::LineWidth;
geode->addDrawable(geom.get());
//打开透明度
geode->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
//设置线宽
width->setWidth(15.0);
geode->getOrCreateStateSet()->setAttributeAndModes(width.get(), osg::StateAttribute::ON);
//设置顶点
geom->setVertexArray(coords.get());
//设置顶点的关联方式
geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::Mode::LINE_LOOP, 0, 4));
//绘制四边形,共四个顶点
//设置顶点颜色
geom->setColorArray(colors.get());
geom->setColorBinding(osg::Geometry::AttributeBinding::BIND_PER_VERTEX); //设置绑定方式
//设置法向量
geom->setNormalArray(norms.get());
geom->setNormalBinding(osg::Geometry::AttributeBinding::BIND_PER_VERTEX);
//设置顶点;逆时针绘制,x,y,z
coords->push_back(osg::Vec3(-10.0, 5.0,-10.0));//左下点
coords->push_back(osg::Vec3(10.0, 5.0, -10.0)); //右下点
coords->push_back(osg::Vec3(10.0, 5.0, 10.0)); //右上点
coords->push_back(osg::Vec3(-10.0, 5.0, 10.0)); //左上点
//颜色
colors->push_back(osg::Vec4f(1.0, 0.0, 0.0, 0.5));
colors->push_back(osg::Vec4f(0.0, 1.0, 0.0, 0.5));
colors->push_back(osg::Vec4f(0.0, 0.0, 1.0, 0.5));
colors->push_back(osg::Vec4f(1.0, 1.0, 0.0, 0.5));
//压入一个法向量
norms->push_back(osg::Vec3(0.0, -1.0, 0.0));
return geode;
}
int main() {
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
//为了突出四边形透明度,加一个参照物“滑翔机”
osg::ref_ptr<osg::Group> group = new osg::Group;
group->addChild(osgDB::readNodeFile("glider.osg"));
group->addChild(CreateSimple());
viewer->setSceneData(group.get());
return viewer->run();
}
注意:因为设置了线宽,所以透明度并不明显,将线宽去掉,透明度更为明显,而且为了突出透明效果,加一个滑翔机模型辅助,注意渲染顺序,先加滑翔机,后加长方形。如果顺序颠倒,则滑翔机会覆盖长方形,无法得到透明效果。
#include
#include
#include
#include
#include
#include //矩阵平移头文件(类)
//Matrix Operation
osg::ref_ptr<osg::Node> MatrixOperation() {
osg::ref_ptr<osg::Group> group = new osg::Group; //提供展示多个图元的组
osg::ref_ptr<osg::MatrixTransform> max = new osg::MatrixTransform; //定义一个可供平移的图元对象
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("glider.osg");
max->addChild(osgDB::readNodeFile("glider.osg")); //该图元为小飞机
max->setMatrix(osg::Matrix::translate(-1.5, 0.0, 1.5)); //向左平移五个单位
group->addChild(node);
group->addChild(max);
return group;
}
int main() {
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
viewer->setSceneData(MatrixOperation().get());
return viewer->run();
}
#include //旋转
//Matrix Operation
osg::ref_ptr<osg::Node> MatrixOperation() {
...
max->setMatrix(osg::Matrix::translate(-1.5, 0.0, 1.5)); //向左平移五个单位
max->setUpdateCallback(new osg::AnimationPathCallback(
osg::Vec3(0.0, 0.0, 0.0), osg::Z_AXIS, 1.0));
group->addChild(node);
group->addChild(max);
return group;
}
此时便会出现问题,运行后会发现,设置旋转的小飞机在“原点”围绕z轴旋转,旋转没有问题,但位置却不对,因为上一步已经将该飞机平移至(-1.5, 0.0, 1.5),此时平移效果没有了。
原因: 旋转的Callback会刷掉同级的其他变换矩阵(具体不详细解释,因为内部原因我也不明白emmm,简单理解就是覆盖了其他同级的矩阵,因此上面的平移矩阵无效了);同级的意思是:对类对象max(代码中定义的对象)进行的操作,有代码可知,我们对max进行了三个操作:1.加载图元小飞机;2.平移;3.旋转Callback;所以Callback覆盖了“同级”的平移矩阵;
解决代码如下:
sg::ref_ptr<osg::Node> MatrixOperation() {
osg::ref_ptr<osg::Group> group = new osg::Group; //提供展示多个图元的组
osg::ref_ptr<osg::MatrixTransform> max = new osg::MatrixTransform; //定义一个可供平移的图元对象
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("glider.osg");
osg::ref_ptr<osg::MatrixTransform> max2 = new osg::MatrixTransform;
max2->addChild(osgDB::readNodeFile("glider.osg")); //该图元为小飞机
max2->setMatrix(osg::Matrix::translate(-1.5, 0.0, 1.5)); //向左平移五个单位
max->setUpdateCallback(new osg::AnimationPathCallback(
osg::Vec3(0.0, 0.0, 0.0), osg::Z_AXIS, 1.0));
max->addChild(max2.get());
group->addChild(node);
group->addChild(max);
return group;
}
将加载图元和平移操作放在一个对象B,旋转操作放在对象A,对象A中加入对象B,使得平移和旋转操作不在“同一级”(对象A【根结点】下有两个节点:旋转节点+对象B节点;对象B节点下有两个节点:加载图元+平移。因此不再同一层);
下面主要是对一个模型进行平移修改,然后将结果保存为新的模型;
void ExportANode() {
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("cow.osg");
osg::ref_ptr<osg::MatrixTransform> max = new osg::MatrixTransform;
max->addChild(node);
max->setMatrix(osg::Matrix::translate(50.0, 0.0, 0.0));
osgDB::ReaderWriter::WriteResult result =
osgDB::Registry::instance()->writeNode(*max.get(), "TrCow.osg",
osgDB::Registry::instance()->getOptions());
if (result.success()) {
osg::notify(osg::NOTICE) << "Write Node Success" << std::endl;
}
}