这个星期基本上除了看完快速入门指导的第三章,应该就没做啥了,不知道明天开会说什么了,哈哈哈
3.2.3 NodeVisitors
节点访问
NodeVisitor is OSG’s implementation of the Visitor design pattern. In essence, NodeVisitor traverses a scene graph and calls a function for each visited node. This simple technique exists as a base class for many OSG operations, including the osgUtil::Optimizer, the osgUtil library’s geometry processing classes, and file output. OSG uses the osgUtil::UpdateVisitor class (derived from NodeVisitor) to perform the update traversal. In the preceding section, UpdateVisitor is the NodeVisitor that calls the NodeCallback::operator()() method. In summary, NodeVisitor classes are used throughout OSG.
NodeVisitor是OSG执行Visitor的设计模式。本质上来说,NodeVisitor遍历场景图形并且为每一它访问过的节点都调用一个函数。大多数的OSG操作都把这个基本技术座位基础类,其中包括osgUtil::Optimizer, osgUtil库文件的Geometry处理类已经文件输出。OSG运用osgUtil::UpdateVisitor类(继承自NodeVisitor)来执行更新遍历。在之前的章节中,UpdateVisitor是一个调用了NodeCallback::operator()()方法的NodeVisitor。总的来说,NodeVisitor类贯穿了整个OSG。
NodeVisitor is a base class that your application never instantiates directly. Your application can use any NodeVisitor supplied by OSG, and you can code your own class derived from NodeVisitor. NodeVisitor consists of several apply() methods overloaded for most major OSG node types. When a NodeVisitor traverses a scene graph, it calls the appropriate apply() method for each node that it visits. Your custom NodeVisitor overrides only the apply() methods for the node types requiring processing.
NodeVisitor是一个不需要程序直接初始化的基类。程序中,你可以使用所有OSG提供的NodeVisitor, 同时,你可以更改你从NodeVisitor继承过来的类。NodeVisitor包括了一些从大多数OSG节点类重载过来的apply()方法。当一个NodeVisitor遍历一个场景的时候,它通常会为它所访问的每一节点调用合适的apply()方法。自定义的NodeVisitor仅仅重写了需要处理的节点类的apply()方法。
After loading a scene graph from file, applications commonly search the loaded scene graph for nodes of interest. As an example, imagine a model of a robot arm containing articulations modeled with transformations at the joints. After loading this file from disk, an application might use a NodeVisitor to locate all the Transform nodes to enable animation. In this case, the application uses a custom NodeVisitor and overrides the apply(osg::Transform& )method. As this custom NodeVisitor traverses the scene graph, it executes the apply() method for each node that derives from Transform, and the application can perform the operations necessary to enable animation on that node, such as saving the node address in a list.
从文件中载入一个场景之后,程序通常会调查载入的场景图中。例如,一个具备细节的机器人的手臂,并且在结合处有变化。从磁盘载入文件之后,为了能够拥有动画效果,程序通常会是运用NodeVisitor来定位所有移动变化的节点。要达到这样的效果,程序需要自定义一个NodeVisitor并且重写apply(osg::Transform&)方法。当这个自定义的NodeVisitor遍历整个场景图的时候,它就会为每一个继承自Transform的节点执行apply()方法,同时,为了保证节点的动画效果,程序可以执行诸如把节点地址保存在一个list中的相应的操作。
If your NodeVisitor overrides multiple apply() methods, OSG calls the most specific apply() method for a given node. For example, Group derives from Node. If your NodeVisitor overrides apply( Node&) and apply( Group&), OSG calls apply(Group&) if it encounters a Group or any node derived from Group during the traversal. If OSG encounters a Geode, it calls apply(Node&) in this example, because Geode derives from Node, not from Group.
如果你的NodeVisitor重载了很多apply()方法,对于一个特定的节点,OSG会调用一个特定的apply()方法。例如,Group继承自Node。如果你的NodeVisitor重载了apply(Node&)和apply(Group&),那么在遍历的时候,如果遍历到的是一个Group或者其他继承自Group的节点,OSG将会调用apply(Group&)方法。如果遍历到的是一个Geode,则会调用apply(Node&)方法,因为Geode是继承自Node而不是Group.
Searching for a node with specific name is a simple and often useful operation. The code below shows how to implement a class called FindNamedNode. This class takes a string as a parameter to its constructor and stores the address of a node with a matching name.
寻找一个特定名字的节点,是一个简单并且常用的操作。以下的代码阐述了如何执行名为FindNamedNode的类。这个类的构造函数把一个字符串作为参数,并且会把对应的节点的地址保存下来。
Enable NodeVisitor Traversals
启用NodeVisitor遍历
The NodeVisitor base class, by default, disables traversal. In your derived class, you should initialize the base class with the enumerant NodeVisitor::TRAVERSE_ALL_CHILDREN to enable traversal. Otherwise, OSG won’t call any of your apply() methods.
NodeVisitor基类,默认的关闭了遍历功能。所以在你的类中,应该要用enumerant NodeVisitor::TRAVERSE_ALL_CHILDRENL来初始化基类,这样才能够启用遍历。否则,OSG将不能调用apply()方法。
Definition of the FindNamedNode class
FindNamedNode类的定义
This listing shows the declaration and definition of a simple NodeVisitor that finds a node with a specified name. The class, findNamedNode, is part of the FindNode example program.
这个列表给出了一个简单的可以搜寻特定名字的节点的NodeVisitor的声明和定义。FindNamdedNode类,是FindNode的一部分。
// Derive a class from NodeVisitor to find a node with a
// specific name.
class FindNamedNode: public osg::NodeVisitor
{
public:
FindNamedNode(const std::string& name)
: osg::NodeVisitor( // Traverse all children.
osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_name( name){}
//This method gets called for every node in the scene
//graph. Check each node to see if its name matches
//our target. If so, save the node’s address.
Virtual void apply( osg::Node& node)
{
if(node.getName()==_name)
_node = &node;
//keep traversing the rest of the scene graph.
traverse( node);
}
osg::Node* getNode(){ return _node.get();}
protected:
std::string _name;
osg::ref_ptr<osg::Node> _node;
};
The traverse() method is a member of NodeVisitor. This is different from, but similar to, the traverse() call in section 3.2.2 Callbacks, which is a member of NodeCallback. When traversing a scene graph, a NodeVisitor uses the following rules.
traverse()是NodeVisitor的成员方法。与3.2.2中的traverse()类似但又不同,它是NodeCallback的成员。当遍历一个场景图的时候,NodeVisitor使用以下的规则:
l A vanilla NodeVisitor configured to TRAVERSE_ALL_CHILDREN traverses its children.
l Custom NodeVisitor classes that override one or more apply() methods are responsible for calling NodeVisitor::traverse() to traverse a node’s children. This requirement allows your custom NodeVisitor to perform pre- and post- traversal operations, and stop traversal if necessary.
l When using callbacks executed by NodeVisitor classes, such as an update callback as described in the previous section, the NodeVisitor traverses the children of nodes without a callback. NodeVisitor doesn’t traverse the children of nodes with callbacks attached. Instead, the callback method operator()() is responsible for traversing children with a call NodeCallback::traverse() if the application requires traversal.
l 一个设置了TRAVERSE_ALL_CHILDREN的vanilla的NodeVisitor会遍历它的子节点(子类)。
l 自定的NodeVisitor类重写了一个或多个apply()方法,这将负责调用NodeVisitor::traverse()来遍历其所有的子节点。这样的要求使得自定义的NodeVisitor可以执行遍历前/后的操作,并且可以按需要终止遍历。
l 当使用NodeVisitor类的callback时,例如在之前的章节提到的更新回调,NodeVisitor会遍历它所有的子节点而不需要回调。NodeVisitor不会遍历附加有回调的子节点。相反的,回调方法operator()()通过调用NodeCallback::traverse()来遍历其子节点,如果需要的话。
To traverse your scene graph with a NodeVisitor, pass the NodeVisitor as a parameter to Node::accept(). You can call accept() on any node, and the NodeVisitor will traverse the scene graph starting at that node. To search an entire scene graph, call accept() on the root node.
用NodeVisitor来遍历一个场景图,会把NodeVisitor作为一个参数传递给Node::accept(). 在任何节点你都可以调用accpet(),同时,NodeVisitor会从当前节点遍历整个场景图。如果要搜寻整个场景图的话,需要在根节点调用accept()。
The FindNamedNode class above is part of FindNode example program. The FindNode example loads a scene graph from disk, finds a node with a specific name in the loaded scene graph, and modifies that node’s data before rendering it. The FindNode example works with the State example discussed in section 2.4.3 Example Code for Setting State, which outputs a scene graph to a file. When FindNode loads this file, it finds the MatrixTransform node with a StateSet configured for flat shading, and changes the state to smooth shading.
FindNamedNode类是FindNode例子程序的一部分。FindNode从磁盘上载入一个场景图,然后找到特定名字的节点,接着在渲染节点之前修改节点数据。FindNode经常与State(2.4.3中提到的最终输出场景图到一个文件的例子)共同协作。当FindNode载入文件之后,它会找到一个状态集设定为flat shading的MatrixTransform的节点,然后把其状态改为smooth shading。
3.2.4 Picking
点击
Most 3D applications require some form of picking functionality to allow the end user to select a portion of the displayed image. In its simplest form, the user positions the mouse over the displayed scene and clicks a mouse button. Internally, the application performs an operation to map the 2D xy mouse location to the corresponding scene graph node and stores the node address for future operations.
大多数的3D程序都要求实现用户可通过点击来选择显示图片中特定的部分。最简单的一种形式——用户把鼠标至于显示图片上,然后点击鼠标键。相应的,程序将会把而为的鼠标的落点对应联系到相应的场景图节点中去,同时,还会保存该节点的地址备用。
In essence, OSG-based applications perform two operations to implement picking.
本质上来说,基于OSG的程序通过两个操作来实现点击选择:
l Receive mouse events. The osgGA library provides event classes that allow applications to receive mouse events in a platform-independent manner.
l Determine which part of the scene graph is under the mouse cursor. The osgUtil library provides intersection classes that create a volume around the mouse xy location and allow you to intersect that volume with your scene graph. osgUtil returns a list of nodes intersected by the volume, which are sorted in front-to-back order.
l 接收鼠标事件。osgGA库提供了平台独立的在程序中接收鼠标事件的事件类
l 测定出在鼠标指针下的是场景图的哪一部分。osgUtil库提供了交互类,这个交互类将会创建一个位于鼠标xy位置附近的volume,并且用户可以通过volume与场景图交互操作。同时,osgUtil会范围一个经由volume交互操作的节点列表,改列表按照由前至后的顺序。
This section describes how to implement both of these operations.
接下来的章节讲述了如何执行以上两个操作
Capturing Mouse Events
捕获鼠标事件
As section 1.6.3 Components states, the osgGA library provides platform-independent GUI event support. The examples in this chapter use osgGA::TrackballManipulator for manipulating the view matrix. TrackballManipulator takes mouse events as input and modifies the viewer’s osg::Camera view matrix.
如1.6.3节中表述的,osgGA库提供了平台独立的GUI事件支持。在该小节中的例子是使用osgGA::TrackballManipulator来操作视图矩阵。TrackballMnipulator把鼠标事件作为输入,同时来修改viewer的osg::Camera的视图矩阵。
TrackballManipulator derives from osgGA::GUIEventHandler. GUIEventHandler is an abstract base class that your application doesn’t instantiate directly. Instead, applications derive classes from GUIEventHandler to perform operations based on GUI events. To perform mouse-based picking, derive a class from GUIEventHandler and override the GUIEventHandler::handle() method to receive mouse events. Create an instance of your new class and attach it to your application’s viewer.
TrachballManipulator继承自osgGA::GUIEventHandler. GUIEventHandler是一个抽象基类,在程序中不需要被直接初始化。相反的,程序通常都会继承一个GUIEventHandler类来执行基于GUI的事件。要执行鼠标点击选择,需要从GUIEventHandler继承一个类,同时重载GUIEventHandler::handle()方法来接受鼠标事件。创建一个这个新的类的实例,并且把它附加到程序的观察者上去。
The handle() method takes two parameters, an osgGA::GUIEventAdapter and an osgGA::GUIActionAdapter, as the following example shows:
virtual bool GUIEventHndler::handle(
const osgGA::GUIEventAdapter& ea,
osgGA::GUIActionAdapter& aa);
handle()方法有两个参数,一个osgGA::GUIEventAdapter和一个osgGA::GUIActionAdapter。
Your implementation of handle() receives GUI events including mouse events in the GUIEventAdapter. The GUIEventAdapter header file declares an EventType enumerant that allow your application to examine only events of interest, such as mouse events. Retrieve the event type with the GUIEventAdapter::getEventType() method.
执行handle()函数,它将会接收到诸如GUIEventAdapter中的鼠标事件等的这些GUI事件。GUIEventAdapter头文件声明了一个可枚举的EventType,这将使得你的程序可以只关注特定的事件,如鼠标事件。可以通过GUIEventAdapter::getEventType()方法来获得事件类型。
GUIActionAdapter is your application’s interface back to the GUI system. In the case of mouse picking, attach your picking GUIEventHandler to your viewer class. As a result, the GUIActionAdapter is your viewer class. This is so you can perform an intersection with your scene based on the current view.
GUIActionAdapter是程序的界面联系到GUI系统的后台支持。即,在鼠标点击选择的时候,把关于点击的GUIEventHandler附加给观察者。因此,GUIActionAdapter是你的观察者类。这使得你可以与当前视图取得交互操作。
Before rendering with your viewer, create an instance of your new GUIEventHandler class and attach it to your viewer with the Viewer::addEventHandler() method. As its name implies, viewers can have multiple event handlers, and Viewer adds your event handler to a list of possibly several event handlers. Viewer calls handle() for each GUI event until one of the handle() methods returns true.
在渲染观察者(viewer)之前,创建一个GUIEventHandler类的实例,并用Viewer::addEventHandler()方法把它附加到观察者之上。由它的名字可以推测,观察者可以有多个事件控制器,同时观察者会把事件控制器加入到一个列表中。观察者通过调用不同GUI事件的handle()直到其中一个handle()方法的返回值为真。
The code below contains a class called PickHandle that derives from GUIEventHandler. The implementation of the handle() method supports the PUSH, MOVE, and RELEASE mouse event types. It records the mouse xy location on PUSH and MOVE events, and if the mouse doesn’t move, the RELEASE event triggers a pick operation. If he pick succeeds, handle() returns true. It returns false in all other cases to allow other event handlers to examine the event.
下面的代码包含一个叫做PickHandle的类,这个类继承自GUIEventHandler. handle()方法的执行支持PUSH, MOVE和RELEASE等鼠标事件。它将记录PUSH和MOVE事件发生时鼠标的xy位置,而且,如果鼠标没有移动的话,RELEASE事件将会触发一个选择操作。如果点击选择成功,handle()的返回值为真。否则,它将返回“假”来促使其他的事件控制器来检查该事件。
The pick operation is performed on RELEASE only if the mouse doesn’t move. This allows mouse motion events to pass to other event handlers, such as a TrackballManipulator.
只有当鼠标没有移动的时候,才会执行点击选择操作。这使得鼠标事件可以被传入给其他的事件控制器,例如TrackballManipulator.
The PickHandler class
PickHandler类
To implement picking in OSG, use a subclass derived from osgGA::GUIEventHandler. This listing shows the PickHandler class from the Picking example program. The class defines two methods, one for receiving mouse events, and the other for implementing the pick operation on mouse release.
在OSG中执行点击选择,需要从osgGA::GUIEventHandler继承一个类。以下列出了一个Picking例子程序中的PickHandler类。这个类定义了两个方法,一个用来接收鼠标事件,另外一个用来当鼠标释放时执行点选操作。
//PickHandler – A GUIEventHandler that implements picking.
class PickHandler : public osgGA :: GUIEventHandler
{
public:
PickHandler() : _mX(0.), _mY(0.){}
bool handle( const osgGA::GUIEventAdapter& ea,
osgGA::GUIActionAdapter& aa)
{
osgViewer::Viewer* viewer =
dynamic_cast<osgViewer::Viewer*>(&aa);
if(!viewer)
return false;
switch(ea.getEventType())
{
case osgGA::GUIEventAdapter::PUSH:
case osgGA::GUIEventAdapter::MOVE:
{
//record mouse location for the button press
//and move events
_mX = ea.getX();
_mY = ea.getY();
return false;
}
case osgGA::GUIEventAdapter::RELEASE:
{
//if the mouse hasn’t moved since the last button press or move event,
//perform a pick. (Otherwise, the trackball manipulator will handle it.)
if (_mX == ea.getX() && _mY == ea.getY())
{
if (pick(ea.getXnormalized(),
ea.getYnormalized(), viewer))
return true;
}
return false;
}
default:
return false;
}
}
protected:
//store mouse xy location for button press & move events.
Float _mX, _mY;
//Perform a pick operation
bool pick( const double x, const double y,
osgViewer::Viewer* viewer)
{
if( !viewer->getSceneData())
//nothing to pick
return false;
double w(.05), h(.05);
osgUtil::PolytopeIntersector* picker =
new osgUtil::PolytopeIntersector(
osgUtil::Intersector::PROJECTION,
x-w, y-h, x+w, y+h);
osgUtil::IntersectionVisitor iv(picker);
viewer->getCamera()->accept(iv);
if(picker->containsIntersections())
{
Const osg::NodePath& nodePath =
picker->getFirstIntersection().nodePath;
unsigned int idx = nodePath.size();
while(idx--)
{
// Find the LAST MatrixTransform in the node path;
// this will be the MatrixTransform to attach our callback to.
osg::MatrixTransform* mt =
dynamic_cast<osg::MatrixTransofrm*>(
nodePath[idx]);
if (mt == NULL)
continue;
//if we get here, we just found a MatrixTansform in the nodePath.
if(_selectedNode.valid())
//clear the previous selected node’s callback to make it stop pinning.
_selectedNode->setUpdateCallback(NULL);
_selectedNode = mt;
_selectedNode->setUpdateCallback( new RoateCB);
break;
}
if (!_selectedNode.valid())
osg::notify()<<”Pick failed.”<<std::endl;
}
else if (_selectedNode.valid())
{
_selectedNode->setUpdateCallback(NULL);
_selectedNode = NULL;
}
return _selectedNode.valid();
}
} ;
int main(int argc, char **argv)
{
//create the view of the scene
osgViewer::Veiwer viewer;
viewer.setSceneData(createScene().get());
//add the pick handler
Viewer.addEventHandler(new PickHandler);
return viewer.run();
}
Listing above also shows the main() function of the Picking example program to illustrate using the Viewer::addEventHandler() method to attach an event handler to a viewer.
上面的代码中也给出了Picking例子程序中的main函数,阐述了如何通过Viewer::addEventHandler()方法为一个观察者附加一个事件控制器
In summary, to receive mouse events for picking, perform the following steps:
总的说来,要接收到点击选择的鼠标事件,需要按照以下步骤:
l Derive a class from GUIEventHandler. Override the handle() method.
l In handle(), examine the event type in the GUIEventAdapter parameter to select events of interest and perform any necessary operations. Return true to prevent other event handlers from receiving an event.
l Before rendering, create an instance of your event handler class and add it to your viewer with the addEventHandler() method. OSG passes your viewer to your handle() method as the GUIActionAdapter parameter.
l 创建一个GUIEventHandler的继承类.重载handle()方法
l 在handle()函数中,检查在GUIEventAdapter中的参数事件类型,使得可以正确选择特定的事件并作出必要的操作.返回一个真值,阻止其他事件控制器接收到事件
l 渲染之前,常见一个事件控制器类的实例,并通过addEventHandler()方法把其加入到观察者中.OSG会把观察者(viewer)作为参数传递给handle()方法
These techniques aren’t limited to mouse-based picking. Your application can implement classes similar to TrackballManipulator by receiving mouse events in the same manner. You can also receive keyboard events and implement operations in responses to key presses.
这些特性并非局限于鼠标点击选择.在程序中,可以通过执行类似于TrackballManipulator的类来接收鼠标事件.同时也可以接收键盘事件之后来执行相应的操作.
The following section completes the discussion of mouse-based picking by describing how to determine which part of your scene graph is under a user mouse press.
接下来的章节将会完成基于鼠标点击选择的讨论,它将会具体描述如何判断出鼠标具体选择的场景图的部分.
Intersections
交互操作
Think of mouse picking as shooting a ray from the mouse position into your scene. The part of the scene under the mouse has an intersection with that ray. Ray intersections don’t meet application picking requirements when the scene consists of line and point primitives, because mouse location round off prohibits exact mathematical intersection with such primitives. Furthermore, in a typical perspective rendering, ray intersection precision is inversely proportional to distance from the viewer.
把鼠标点击选择视为一束从鼠标位置发射到场景图的光线.场景图在鼠标位置处的部分可以被交互操作.而当场景包含很多光和线的元件时,光线交互操作不能运作,因为鼠标位置round off阻止了与这些元件的精确数学操作.此外,在一个典型的立体渲染中,光线交互的精度随着与viewer之间的距离的增加而降低.
Instead of a ray, OSG intersects with a pyramid volume called a polytope to overcome both issues. The pyramid has its apex at the viewpoint, and its central axis passes directly through the mouse location. It widens away from the viewpoint as a function of the field of view and application-controlled width parameters.
在OSG中可以代替光线的,是使用多面体(使用金字塔体积来交互操作)来解决这两个问题。金字塔在视点有它自己的定点、及一个直接穿过鼠标位置的中轴线。它从视点开始延展,并且由程序参数控制宽度。
OSG employs the inherent hierarchical nature of the scene graph to efficiently compute intersections on the host CPU, avoiding OpenGL’s often-sluggish selection feature. The osgUtil::IntersectionVisitor class derives from NodeVisitor and tests each Node bounding volume against the intersection volume, allowing it to skip subgraph traversals if the subgraph has no possibility of a child intersection.
OSG使用在宿主CPU上采用场景图固有的层级结构来有效的计算交互集,避免了在OpenGL中的缓慢的选择特点。osgUtil::IntersectionVisitor类继承自NodeVisitor,并且它会测试每个节点与交互体积所重叠的包围体积,如果子图没有交互重叠区的话,那么就可以直接跳过子图遍历。
IntersectionVisitor can be configured for intersection testing with several different geometric constructs including planes and line segments. Its constructor takes an osgUtil::Intersector as a parameter, which defines the pick geometry and performs the actual intersection testing. Intersector is a pure virtual base class that your application doesn’t instantiate. The osgUtil library derives several classes from Intersector to represent different geometric constructs, including the osgUtil::PolytopeIntersector, which is ideal for mouse-based picking.
可以通过设置IntersectrionVisitor来做到交互测试,包括不同的集合结构体(包括平面和线段分割)。它的构造函数取osgUtil::Intersector作为参数,这个参数定义了点击选择geometry及执行相应的交互测试。Intersector是一个不需要在程序中初始化的纯虚类。osgUtil库从Intersector继承了好几个类来表示不同的集合构造,其中包括osgUtil::PolytopeIntersector来实现基于鼠标的点击选择。
Some applications require picking individual vertices or polygons. Other applications simply need to know the parent Group or Transform node containing any selected osg::NodePath. NodePath is a std::vector<osg::Node> that represents the path through the node hierarchy leading from the root node down to a leaf node. If your application requires an intermediate group node, search the NodePath from back to front until you find a node that meets your application requirements.
一些程序要求可以点击选择个别的定点或多边形。一些程序则只需要知道parent Group或者包括了被选择的osg::NodePath的Transform节点。NodePath是一个标准的std::vector<osg::Node>,它可以表示从根节点到当前子节点层次的路径。如果你的程序中要求一个中间group节点,应该从后往前的搜寻NodePath直到你得到满足程序要求的节点。
In summary, to perform mouse-based picking in OSG, write your application to perform the following steps.
总的来说,如果要实现基于鼠标的点击选择,那么在OSG的程序里,需要包括以下几步:
l Create and configure a PolytopeIntersector using the normalized mouse location stored in the GUIEventAdapter.
l Create an IntersectionVisitor and pass the polytopeIntersector as a parameter in the constructor.
l Launch the IntersectionVisitor on your scene graph’s root node, usually through the Viewer Camera, as in the following code:
// ‘iv’ is an IntersectionVisitor
viewer->getCamera()->accept( iv );
l If the PolytopeIntersector contains intersections, obtain the NodePath and search it to find the node of the interest
l 创建并用存储在GUIEventAdapter中的标准化的鼠标位置设置一个polytopeIntersector
l 创建一个IntersectionVisitor并把polytopeIntersector作为参数传入构造函数
l 在场景图的根节点启用IntersectionVision,通常通过场景观察者摄像机来实现:
// ‘iv’ is an IntersectionVisitor
viewer->getCamera()->accept( iv );
l 如果PolytopeIntersector包含交互操作,就能得到NodePath并且会搜寻它直到找到特定的节点。
Listing before shows the PickHandler::pick() mthod from the Picking example program. The Picking example program creates a scene graph similar to the scene graph that the Callback program creates. However, the Picking scene graph uses a hierarchy of two MatrixTransform nodes, one to store a translation and the other to store a rotation. Upon a successful pick, the code searches the NodePath until it encounters the rotation MatrixTransform. It attaches an update callback to that node to dynamically rotate the child geometry.
之前讲述的代码演示了Picking例示程序中的PickHandler::pick()方法。Picking例示程序创建了一个与Callback程序中创建的场景图类似的场景图。不同的是,Picking场景图使用一个双MatirxTransfor节点的结构,一个用来存取平移,另外一个存储旋转。要完成一个点击选择,代码首先会搜寻NodePath,知道遇到旋转MatriTransform.它附加了一个更新回调到该节点上,借此实现了实时动态的旋转子几何体。
When you run the Picking example program, it displays two cows like the Callback example program. However, you can pick either cow, and the program rotates the selected cow in response.
当你运行Picking例示程序时,它将会显示两只cows,这与Callback例示程序类似。但是,这次你可以选择任意一个cow,同时,程序会旋转这个被选中的cow。