OpenSceneGraph 笔记--如何导出三角形数据2

今天写了一个导出三角形的类,可以导出一个Group的所有三角形数据(包括Group的所有child),主要用于碰撞检测.比如有一个Group”自行车”,这个Group包含有子Group”前轮”和子Group”后轮”,子对象通过MatrixTransform与父对象相连.那么这个类可以将Group”自行车”包括”前轮”和”后轮”的三角形数据都导出到一个vector里面,方面用于碰撞检测.

代码片段如下:

struct GetVertex
{
void operator() (const osg::Vec3& v1,const osg::Vec3& v2,const osg::Vec3& v3, bool) const
{
osg::Vec3 v1New=v1*(*matrix);
osg::Vec3 v2New=v2*(*matrix);
osg::Vec3 v3New=v3*(*matrix);
vertexList->push_back(v1New);
vertexList->push_back(v2New);
vertexList->push_back(v3New);
}
osg::Vec3Array* vertexList;
osg::Matrix* matrix;
};

class VertexVisitor:
public osg::NodeVisitor
{
public:
VertexVisitor():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN){};

virtual void apply(osg::Geode& geode)
{
osg::NodePathList nodePathList=geode.getParentalNodePaths(stopAt);
osg::NodePath firstNodePath=*(nodePathList.begin());
osg::Matrix matrix=osg::computeLocalToWorld(firstNodePath);
osg::Geode::DrawableList drawableList=geode.getDrawableList();

osg::TriangleFunctor tf;
tf.vertexList=vertexList;
tf.matrix=&matrix;

for(osg::Geode::DrawableList::iterator itr=drawableList.begin();
itr
itr++)
{
(*itr)->accept(tf);
}
traverse(geode);
}
osg::Vec3Array* vertexList;
osg::Group* stopAt;
};

osg::Vec3Array* TriangleConvertor::Convert(osg::Group* group )
{
osg::Vec3Array* vertexList=new osg::Vec3Array;
VertexVisitor vertexVisitor;
vertexVisitor.vertexList=vertexList;
vertexVisitor.stopAt=group;
group->accept(vertexVisitor);
return vertexList;
}

思路是使用一个visitor来遍历Group下面所有的Geode,然后用osg::TriangleFunctor获取所有的三角形片.在获取的时候对每一个三角形进行矩阵变换,目的是将local坐标系的三角形数据转换成world坐标系的三角形.获取矩阵的方法是用osg::computeLocalToWorld这个方法.值得注意的是在寻找ParentPath的时候要设置haltTraversalAtNode这个参数为查询起始的Group.这是因为,如果group还有父对象的话,那么会得到所有的对象,而不是返回基于group所在的坐标系的三角形片数据.(可能说的有些绕……)

这样说明吧,举个例子层次结构为一下:

Group Root
MatrixTransform Position
MatrixTransform Bike
MatrixTransform FrontWheel
Geode 1
Geode 2
MatrixTransform RearWheel
Geode 3
Geode 4

注意一个MatrixTransform也是一个Group.这里我们需要得到Bike整个模型的三角形片,而且整个三角形片受到Position这个矩阵的影响.那么当遍历到Geode 1这里的时候,如果调用geode.getParentalNodePaths(),那么就会返回NodeParentList如下:

Root
Position
Bike
FrontWhell

这样的话就相当于要把所有的三角形转换成世界坐标系里面的值.不符合我们的要求.那么如果设置了haltTraversalAtNode=Bike,也就是说调用geode.getParentalNodePaths(Bike),那么NodeParentList如下:

Bike
FrontWheel

这样就满足了我们的要求.然后经过计算的矩阵就是相对于Bike的矩阵,经过转换的三角形片就是整个Bike的三角形片了.
最后整个三角形片通过Vec3Array返回.

太牛了!Google输入法!

驱动之家上面看到的消息,Google出了输入法!要知道做搜索的对于文字的驾驭能力是相当强悍的!到Google上面下载了输入法,安装后开始试用。

拿个几个常见的书名输入,一切ok!不用选字,一句话搞定。

我的自我简介输入一次,哇~~~几乎没有错的~~~~

震撼了哦。

想想我用输入法经历了智能abc输入发到微软拼音输入法到紫光拼音输入法,到拼音加加然后再回到紫光拼音。紫光拼音的整句输入能力很差,使得我养成了输入词组以后敲空格的习惯。而Google输入法强调的是整句输入,所以输入整句以后在断句是最好的选择!当然,还有些不习惯呢~

朋友们,卸载了其他输入法吧~~~支持Google

下载地址:http://tools.google.com/pinyin/

OpenSceneGraph 笔记--如何导出三角形数据

在OpenSceneGraph开发中,为了方便会经常使用到一些不是三角形片的数据,比如四边形等数据。例如画一个管子用四边形带比用三角形片好计算得多。比如现在我们要画一个由两个平面组成的面,我可以这样做:

osg::Geode* geode=new osg::Geode;
osg::Geometry* polyGeom = new osg::Geometry;
osg::Vec3 myCoords[]=
{
osg::Vec3(0,1,0),
osg::Vec3(0,0,0),
osg::Vec3(1,1,0),
osg::Vec3(1,0,0),
osg::Vec3(2,1,0),
osg::Vec3(2,0,0)
};

int numCoords = sizeof(myCoords)/sizeof(osg::Vec3);
osg::Vec3Array* vertices = new osg::Vec3Array(numCoords,myCoords);
polyGeom->setVertexArray(vertices);
polyGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUAD_STRIP,0,numCoords));
geode->addDrawable(polyGeom);

这样就用6个点,用OpenGL提供的QUAD_STRIP方式画出了两个平面。
但是如果要把这个平面用于碰撞检测等技术,那么就需要把这六个点所表示的四边形带转换成三角形片才行。这些三角形定点如下:

0 1 0
0 0 0
1 1 0

0 0 0
1 0 0
1 1 0

1 1 0
1 0 0
2 1 0

1 0 0
2 0 0
2 1 0

可以看出两个平面由4个三角形组成,而且都是逆时针排列(朝向一致)。
以前我自己做过转换,但是感觉很麻烦。OpenSceneGraph的Example osggeometry中提供了一个printTriangles函数,它可以打印出一个drawable所有的三角形片,不管最初的数据结构如何:

struct NormalPrint
{
void operator() (const osg::Vec3& v1,const osg::Vec3& v2,const osg::Vec3& v3, bool) const
{
osg::Vec3 normal = (v2-v1)^(v3-v2);
normal.normalize();
std::cout << "t("<<<") ("<<<") ("<<<") "<<") normal ("<<<")"<
}
};

// decompose Drawable primtives into triangles, print out these triangles and computed normals.
void printTriangles(const std::string& name, osg::Drawable& drawable)
{
std::cout<<

osg::TriangleFunctor tf;
drawable.accept(tf);

std::cout<
}

核心的思想就是利用osg::TriangleFunctor这个模版。这个模版会让你重载()运算符,然后让Drawable去visit它。在这个过程中,所有原始的数据(不管是三角形片的,还是四边形的)都转换成了三角形片数据。

那么如何把三角形数据导出哪?只需要修改一下借助这个思路,将NormalPrint修改成我们需要的就对了。

struct GetVertex
{
void operator() (const osg::Vec3& v1,const osg::Vec3& v2,const osg::Vec3& v3, bool) const
{
vertexList->push_back(v1);
vertexList->push_back(v2);
vertexList->push_back(v3);
}

osg::Vec3Array* vertexList;

};

void getTriangles(osg::Drawable& drawable)
{
osg::TriangleFunctor tf;
tf.vertexList=new osg::Vec3Array;

drawable.accept(tf);

for(osg::Vec3Array::iterator itr=tf.vertexList->begin();
itr!=tf.vertexList->end();
itr++)
{
osg::Vec3 vertex=*itr;
std::cout<<
}

std::cout<
}

以下是完整的示例文件:

// PrimitiveSet.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include

struct GetVertex
{
void operator() (const osg::Vec3& v1,const osg::Vec3& v2,const osg::Vec3& v3, bool) const
{
vertexList->push_back(v1);
vertexList->push_back(v2);
vertexList->push_back(v3);
}

osg::Vec3Array* vertexList;

};

void getTriangles(osg::Drawable& drawable)
{
osg::TriangleFunctor tf;
tf.vertexList=new osg::Vec3Array;

drawable.accept(tf);

for(osg::Vec3Array::iterator itr=tf.vertexList->begin();
itr!=tf.vertexList->end();
itr++)
{
osg::Vec3 vertex=*itr;
std::cout<<
}

std::cout<
}

osg::Node* createGeode()
{
osg::Geode* geode=new osg::Geode;
osg::Geometry* polyGeom = new osg::Geometry;
osg::Vec3 myCoords[]=
{
osg::Vec3(0,1,0),
osg::Vec3(0,0,0),
osg::Vec3(1,1,0),
osg::Vec3(1,0,0),
osg::Vec3(2,1,0),
osg::Vec3(2,0,0)
};

int numCoords = sizeof(myCoords)/sizeof(osg::Vec3);
osg::Vec3Array* vertices = new osg::Vec3Array(numCoords,myCoords);
polyGeom->setVertexArray(vertices);
polyGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUAD_STRIP,0,numCoords));
geode->addDrawable(polyGeom);
getTriangles(*polyGeom);
return geode;
}

int _tmain(int argc, _TCHAR* argv[])
{
//Set up viewer
osgViewer::Viewer viewer;
osg::ref_ptr traits=new osg::GraphicsContext::Traits;
traits->x=200;
traits->y=200;
traits->width=800;
traits->height=600;
traits->windowDecoration=true;
traits->doubleBuffer=true;
traits->sharedContext=0;

osg::ref_ptr gc=osg::GraphicsContext::createGraphicsContext(traits.get());
osg::ref_ptr camera=new osg::Camera;
//osg::Camera camera=new osg::Camera;
camera->setGraphicsContext(gc.get());
camera->setViewport(new osg::Viewport(0,0,traits->width,traits->height));
camera->setDrawBuffer(GL_BACK);
camera->setReadBuffer(GL_BACK);
osgGA::TrackballManipulator* tm=new osgGA::TrackballManipulator;

viewer.setCameraManipulator(tm);

viewer.addSlave(camera.get());

//Set up root node
osg::ref_ptr root=new osg::Group;

root->addChild(createGeode());

//Start show!
viewer.setSceneData(root.get());
viewer.realize();

while(!viewer.done())
{
viewer.frame();
}
}

OpenSceneGraph 笔记--世界如此之美好!

在用OpenSceneGraph之前我费尽心思成功的把3DS模型里面的层次关系导入到我的程序中,学到了不少的东西。昨天在研究OpenSceneGraph的3DS插件的时候发现插件并没有我想象当中那么完美,在调入文件的时候丢失了所有的层次结构,这令我很懊恼。在尝试了修改3DS插件以后,觉得要解决这个问题还需要重新写一个新的插件。当然,这会带来巨大的工作量,不合算。

今天在吃饭的时候突然想到了OpenSceneGraph自己有一个数据结构,扩展名叫.osg,而且有3DS MAX的导出插件,是不是这个数据结构能够提供更多的信息呢?

带着试一试的心理下载了OSGExp插件,晕,第一次安装竟然不行,提示找不到3DS MAX~~想想我装的3DS MAX 9是中文版的,是不是要原版的才能用哦。遂安装了英文原版的。呵呵,这下就对了。

从3DS MAX里面打开做的机床的MAX模型文件,然后导出,很快的就导出了一个osg文件。用osgDirector一看,哇!强悍,所有的层次都保留,层与层之间的变换矩阵也很好用!特别是之前很懊恼的3DS模型里面的pivot属性也越过了。

最后我在自己的程序里面试了试,很方便的就能够让某些部件绕着轴旋转或者做平移变换,而代码却只有十多行!

想起之前我自己动手写的一个很简单的SceneGraph实现,都花费了我很多精力,处理用lib3ds导入的模型的矩阵变换。然而现在OpenSceneGraph让一切变得很美好,这下就有精力专注于上层程序开发了!

也肯定之前的工作没有白费,他们让我熟悉了矩阵的变换以及一个SceneGraph的原理,这些都为现在学习OpenSceneGraph奠定了基础!OpenSceneGraph带给我的只是更多更强大的功能,还有很长的路要走!

另:所有可以导出为osg格式的文件都最好导出成osg,这样会更有利于开发!

OpenSceneGraph 笔记--窗体模式运行

1.使用osgViewer::Viewer代替原来的osgProducer::Viewer
2.先熟悉设计模式,比如最常用的Visitor设计模式。要不然看不懂程序不说,而且更写不出程序。
3.窗体模式运行参考Example osgWindows,在这个例子中重点在于:

osg::ref_ptr traits = new osg::GraphicsContext::Traits;
traits->x = 640;
traits->y = 0;
traits->width = 640;
traits->height = 480;
traits->windowDecoration = true;
traits->doubleBuffer = true;
traits->sharedContext = 0;

osg::ref_ptr gc = osg::GraphicsContext::createGraphicsContext(traits.get());

osg::ref_ptr camera = new osg::Camera;
camera->setGraphicsContext(gc.get());
camera->setViewport(new osg::Viewport(0,0, traits->width, traits->height));
GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
camera->setDrawBuffer(buffer);
camera->setReadBuffer(buffer);

// add this slave camra to the viewer, with a shift right of the projection matrix
viewer.addSlave(camera.get(), osg::Matrixd::translate(-1.0,0.0,0.0), osg::Matrixd());

另外Example osgkeyboardmouse也提到了另外一种方式:

// create the window to draw to.
osg::ref_ptr traits = new osg::GraphicsContext::Traits;
traits->x = 200;
traits->y = 200;
traits->width = 800;
traits->height = 600;
traits->windowDecoration = true;
traits->doubleBuffer = true;
traits->sharedContext = 0;

osg::ref_ptr gc = osg::GraphicsContext::createGraphicsContext(traits.get());
osgViewer::GraphicsWindow* gw = dynamic_cast(gc.get());
if (!gw)
{
osg::notify(osg::NOTICE)<<"Error: unable to create graphics window."<
return 1;
}

gw->realize();
gw->makeCurrent();

// create the view of the scene.
osgViewer::SimpleViewer viewer;
viewer.setSceneData(loadedModel.get());

viewer.setEventQueue(gw->getEventQueue());
viewer.getEventQueue()->windowResize(traits->x,traits->y,traits->width,traits->height);

TODO:
1.3DS导入插件还不够完善,需要自己写。
2.更深入的了解OSG的机制。