以前做的鼠标框选和点选系统虽然实现了基本的功能,但是使用起来感觉总是那么别扭,如果一棵大树里面有一棵小树的话,那么这棵小树是永远选不到的,因为OGRE的射线查询是根据物体的包围盒来判断的。如果想使点选系统更接近用户的意图的话,我开始考虑了以下几个方案:
1.射线与模型的三角面进行精确检测。(效率太低)
2.按ctrl键点模型,选中第一个被查到的物体,如果不松手在点一下,选中第二个被查到的物体,以此类推,这样用户可以选到想选的物体。(程序实现和用户操作都会很复杂)
3.做筛选列表,用户把想选到的物体加入筛选列表,这样不想选择的物体就不做检测。(更复杂)
以上三种不是效率太低,就是实现和操作起来过于复杂。
后来我终于想到个又简单,又操作方便的方案,那就是永远选择包围盒最小的物体,具观察,包围盒越大的物体越容易被选到,所以用户不需要为包围盒大的物体考虑如何选他,稍微换一换角度,总是可以选到的,但是包围盒越小的物体就越难被选中,大部分是跟本无法被单独选中的。所以一条射线查询查询到的所有物体中,最应该被选中的是包围盒最小的物体。于是我按照取小优先的原则试了试,效果还不错,很人性化。
部分源码:
//跟据用户框选的范围大小,判断是框选还是点选
if ((right - left) * (bottom - top) < 0.0001)
{
Ray selectRay = m_pCamera->getCameraToViewportRay(left, top);
m_pRaySceneQuery->setRay(selectRay);
m_pRaySceneQuery->setSortByDistance(true);
RaySceneQueryResult result = m_pRaySceneQuery->execute();
deselectObjects();
RaySceneQueryResult::iterator itr;
MovableObject* pObject=NULL;
for ( itr = result.begin(); itr != result.end(); itr++ )
{
//筛选掉一些非物体结点
if (CheckSubString("/Object/" , itr->movable->getName()))
{
//是不是第一个物体
if (pObject==NULL)
{
pObject=itr->movable;
}
else //如果不是第一个物体,那么判断它与之前的物体包围盒,那个更小
{
float fCurrent=itr->movable->getParentSceneNode()->_getWorldAABB().getSize().length();
float fLast=pObject->getParentSceneNode()->_getWorldAABB().getSize().length();
if(fCurrent
{
pObject=itr->movable;
}
}
}
}
if (pObject!=NULL)
{
selectObject(pObject); //选中这个物体
}