OpenSceneGraph中创建一个大小不变的小球
OpenSceneGraph中创建一个大小不变的小球
核心思想是利用osg::AutoTransform类。具体用法可以看osg自带的例子autotrans
功能验证代码,就不要纠结内存泄露不泄露的问题了,直接裸指针搞起。
核心功能在Build_No_Scaling()
函数中。
不过有一个问题就是,为什么小球在缩放过程中会失去光照效果呢?尤其是场景变小(物体放大)的过程中。有知道的留言说一下吧。
直接上代码把
// OpenSceneGraph library.
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osgGA/StateSetManipulator>
#include <osgViewer/ViewerEventHandlers>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/ShapeDrawable>
#include <osg/LineWidth>
#include <osg/MatrixTransform>
#include <osgFX/Scribe>
#include <osgUtil/SmoothingVisitor>
//下面两个头文件设置点和线宽
#include <osg/Point>
#include <osg/AutoTransform>
#include <osg/Light>
#pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDBd.lib")
#pragma comment(lib, "osgGAd.lib")
#pragma comment(lib, "osgViewerd.lib")
#pragma comment(lib, "osgFXd.lib")
const osgFX::Scribe * one_scribe = NULL;
// 返回一个不缩放的小球. 但是为什么缩小的时候,才有光照呢?
osg::Node* Build_No_Scaling(float x, float y, float z)
{
// 7. add a sphere
osg::Sphere* cylinder = new osg::Sphere(osg::Vec3f(x, y, z), 6.0f);
osg::ShapeDrawable * drawable = new osg::ShapeDrawable(cylinder);
drawable->setColor(osg::Vec4f(1.0, 0.5, 0.5, 0.0));
osg::Geode* geode = new osg::Geode;
geode ->addDrawable(drawable);
// 这里可以让圆球不缩放.
osg::AutoTransform* at = new osg::AutoTransform;
at->addChild(geode);
at->setMinimumScale(0.0);
at->setMaximumScale(FLT_MAX);
at->setAutoRotateMode(osg::AutoTransform::ROTATE_TO_SCREEN);
//at->setAutoRotateMode(osg::AutoTransform::NO_ROTATION);
at->setAutoScaleToScreen(true);
at->setScale(osg::Vec3f(1, 1, 1));
at->setPosition(osg::Vec3f(x,y,z));
return at;
}
osg::Node* BuildScene(void)
{
osg::ref_ptr<osg::Group> root = new osg::Group();
osg::Cylinder* cylinder = new osg::Cylinder(osg::Vec3f(8.0, 0.0, 0.0), 10.0f, 25.0f);
osg::ShapeDrawable * drawable = new osg::ShapeDrawable(cylinder);
drawable->setColor(osg::Vec4f(1.0, 0.5, 0.5, 0.0));
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode ->addDrawable(drawable);
root->addChild(geode);
// 增加各种小球
root->addChild(Build_No_Scaling(0, 0, 20));
root->addChild(Build_No_Scaling(0, 20, 20));
root->addChild(Build_No_Scaling(20, 0, 20));
root->addChild(Build_No_Scaling(20, 20, 0));
return root.release();
}
class RotateGlider : public osgGA::GUIEventHandler
{
public:
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
osgViewer::Viewer* vw = dynamic_cast<osgViewer::Viewer*>(&aa);
if (vw) {
//printf("key down : %d !\n", ea.getKey());
osg::MatrixTransform* mt = dynamic_cast<osg::MatrixTransform*>(vw->getSceneData());
if (mt) {
printf("key down -2: %d !\n", ea.getKey());
static float i = 0.1f;
switch (ea.getEventType()) {
case osgGA::GUIEventAdapter::KEYDOWN:
{
if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left) {
mt->setMatrix(osg::Matrix::rotate(i, osg::Vec3(0.f, 0.f, 1.f)));
i += 0.0f;
}
else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right) {
mt->setMatrix(osg::Matrix::rotate(i, osg::Vec3(0.f, 0.f, 1.f)));
i -= 0.1f;
}
}
break;
default: break;
}
}
}
return false;
}
};
class CPickHandler :public osgGA::GUIEventHandler
{
public:
CPickHandler(osgViewer::Viewer* viewer) :mViewer(viewer){}
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
switch (ea.getEventType())
{
case(osgGA::GUIEventAdapter::PUSH):
if (ea.getButton() == 1)
{
Pick(ea.getX(), ea.getY());
}
return true;
}
return false;
}
protected:
void Pick(float x, float y)
{
// 1、创建一个线段的碰撞检测结果集.
osgUtil::LineSegmentIntersector::Intersections intersections;
//
// 2、使用computeIntersections计算点击到了哪些模型,结果存放在上面定义的碰撞检测结果集里。
//
if (mViewer->computeIntersections(x, y, intersections))
{
printf("number-of-intersections:%d\n", intersections.size());
int i = 0;
// 3、使用迭代器取出这些模型。取出的结果是一个NodePath类对象,遍历该NodePath对象可以找到是否单击到了目标节点。
//
for (osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin(); hitr != intersections.end(); ++hitr)
{
printf("\tthe-%d-st intersection\n", ++i);
//4、将单击到的节点全部取出,进行判断 .
// 行为1.
// 行为2 选中的物体高亮. 高亮的策略就是删掉原来的对象node,而用一个新的node替代
// 当然要求nodepath中至少有2个点.
//
// 上面是网上的做法,奇怪的是,为什么不直接替代选中drawable对象的渲染模式呢?
//
const size_t np = hitr->nodePath.size();
if ( np >= 2) {
osg::Node* const node = hitr->nodePath[np-1];
osg::Group* parent = dynamic_cast<osg::Group*>(hitr->nodePath[np-2]);
printf("nodepath len:%d nodename:%s parent name:%s\n", np,
node->getName().c_str(), parent->getName().c_str());
//
// Scribe 是个 特效对象,将一个node构建好之后,放到scribe中去,然后渲染的时候
// 把scribe 加入到 root树(以前是直接加入node)就可以提供特效了.
//
osgFX::Scribe* parentAsScribe = NULL;
parentAsScribe = dynamic_cast<osgFX::Scribe*>(parent);
/*if (parent->isSameKindAs(one_scribe) ) {
printf("you clicked a node WITH wire frame!\n");
}*/
if (!parentAsScribe)
{
//如果对象选择到,高亮显示, 就是新建一个Scribe对象替换原来的node对象.
printf("you clicked a node WITHOUT wire frame!\n");
osg::ref_ptr<osgFX::Scribe> scribe = new osgFX::Scribe();
if (one_scribe == NULL) one_scribe = scribe.get();
scribe->setName("newly created scribe object");
scribe->addChild(node);
parent->replaceChild(node,scribe);
} else {
//如果没有选择到,则移除高亮显示的对象.
osg::Node::ParentList parentList = parentAsScribe->getParents();
printf("you clicked a node WITH wire frame num.parents:%d!\n", parentList.size());
for(osg::Node::ParentList::iterator itr=parentList.begin();
itr!=parentList.end();
++itr)
{
(*itr)->replaceChild(parentAsScribe, node);
//break;
}
}
}
break; // 一个选择会有多个命中,只处理一个.
}
}
}
osgViewer::Viewer* mViewer;
};
int main(void)
{
osgViewer::Viewer myViewer;
myViewer.getCamera()->setCullingMode(
myViewer.getCamera()->getCullingMode() &
~osg::CullSettings::SMALL_FEATURE_CULLING);
/*
这里还是有点奇怪.难道每棵树的根上都是mt对象?那么内置的handler是怎么处理的?
*/
osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform;
mt->addChild(BuildScene());
myViewer.setSceneData(mt);
myViewer.addEventHandler(new CPickHandler(&myViewer));
return myViewer.run();
}