射线使用-碰撞检测
我们首先实现对鼠标右键的响应是他能够自由查看
然后实现摄像机不能穿越地面
最后单击左键增加实体,并能在单机状态下移动实体
首先,一如既往,提供程序运行的代码框架
#include
<CEGUI/CEGUISystem.h>
#include
<CEGUI/CEGUISchemeManager.h>
#include <OgreCEGUIRenderer.h>
#include "ExampleApplication.h"
class MouseQueryListener : public ExampleFrameListener, public
OIS::MouseListener
{
public:
protected:
};
class MouseQueryApplication : public ExampleApplication
{
protected:
public:
protected:
};
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM ==
OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine,
INT)
#else
int main(int argc, char **argv)
#endif
{
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#else
#endif
}
运行结果应当是一片漆黑
一、创建场景(createScene()添加代码)
既然我们建立了基本的世界空间,那么就要打开光标。打开光标,要使用CEGUI函数调用。
创建一个OgreCEGUIRenderer,然后创建系统对象并将刚创建的Renderer传给它。
创建mGUIRenderer时必须以最后一个参数告诉CEGUI你要用那个场景管理器
你可以运行一下你的程序,看到有天,有地,还有一个不会动的图形化光标
——————————————————————————————————————————————
二、将鼠标右键绑定到“鼠标观察”模式
MouseQueryListener的构造函数中添加代码:
//为了MouseQueryListener能收到鼠标事件,我们必须把它注册为一个鼠标监听器。
//用场景管理器的一个调用创建一个RaySceneQuery对象
//创建一个RaySceneQuery,以后我们就必须销毁它。
~MouseQueryListener()析构函数增加代码:
mouseMoved中增加代码:
mousePressed中增加代码:
mouseReleased中增加代码:
试一下你的摄像机是否能受右键控制吧!
____________________________________________________________________________
三、地形碰撞检测
frameStarted中增加代码(删除原来的return语句):
//因为ExampleFrameListener的frameStarted成员函数移动摄像机,并且在此发生后我们需要在函数中安排
//我们的剩余行动。我们的目标及时找到摄像机的当前位置,并沿着它向地面发射一条射线。这被称为射线
//场景查询,它会告诉我们我们下面的地面的高度。得到了摄像机的当前位置后,我们需要创建一条射线。
//这条射线有一个起点(射线开始的地方),和一个方向(此处y轴负方向)。
//begin方法获得迭代器的第一个元素。如果result.begin() == result.end(),那么无返回结果
//保证至少返回一个查询结果(itr
// Get the results, set the camera height
//worldFragment结构包含有在变量singleIntersection(一个Vector3)中射线击中地面的位置。
//我们要得到地面的高度,依靠将这个向量的Y值赋值给一个本地变量。
//一旦我们有了高度,我们就要检查摄像机是否低于这一高度,如果低于这一高度,
//那么我们要将摄像机向上移动至地面高度。
//注意,我们实际将摄像机多移动了10个单位。这样保证我们不能由于太靠近地面而看穿地面。
运行一下,看你还能否“入地”!
_______________________________________________________________________________________
在mousePressed()中的if语句添加代码:
//
Camera::getCameraToViewpointRay;一个将屏幕上的点击(X和Y坐标)转换成一条可供RaySceneQuery对象使用的射线的好用函数
在mousemoved()的if(mLMouseDown){}中添加代码:
if (mLMouseDown)
Ogre中的碰撞检测(完整版)
原创 韩举
http://www.hjpdiy.com
请注明转载地址
注意此例子需要Ogre 0.15 新版本RC的代码制作中
Ogre是开源的封装了DirectX和OpenGL的3D引擎
Ogre的下载和安装见http://www.uipower.com/bbs/dispbbs.asp?boardID=24&ID=225&page=1
此主题相关图片如下:
下载代码
http://www.uipower.com/bbs/dispbbs.asp?boardid=24&id=233
Ogre采用树桩管理场景中的各种"元素"(摄像机、灯光、物体等),所有的东西都挂在"树"上,不在"树"上的东西不会被渲染。
Ogre::SceneManager就是"树"的管理者,Ogre::SceneNode是从SceneManager中创建的(当然BSP和8*树的管理也和这两个类有关,这暂时不讨论)。
AABB(轴对齐包围盒)
这个东西是碰撞检测的基础(怎么总想起JJYY呢),和它类似的还有OBB(有向包围盒),由于OBB创建复杂,所以Ogre采用了AABB。
最简单的碰撞检测:
通 过Ogre::SceneNode::_getWorldAABB()可以取得这个叶子节点的AABB(Ogre::AxisAlignedBox), Ogre::AxisAlignedBox封装了对AABB的支持,该类的成员函数Ogre::AxisAlignedBox::intersects ()可以判断一个AABB和"球体、点、面以及其他面"的相交情况(碰撞情况)。
m_SphereNode树的叶子,挂了一个"球"
m_CubeNode树的叶子,挂了一个"正方体"
AxisAlignedBox spbox=m_SphereNode->_getWorldAABB();
AxisAlignedBox cbbox=m_CubeNode->_getWorldAABB();
if(spbox.intersects(cbbox))
{
//相交
}
区域查询:
简单的讲就是,查询某一区域中有什么东西,分为AABB、球体、面查询。
//创建一个球体查询,这里的100是m_SphereNode挂着的那个球体的半径
SphereSceneQuery * pQuery=m_SceneMgr->createSphereQuery(Sphere(m_SphereNode->getPosition(),100));
//执行这个查询
SceneQueryResult QResult=pQuery->execute();
//遍历查询列表找出范围内的物体
for (std::list<MovableObject*>::iterator iter = QResult.movables.begin(); iter != QResult.movables.end();++iter)
{
MovableObject* pObject=static_cast<MovableObject*>(*iter);
if(pObject)
{
if(pObject->getMovableType()=="Entity")
{
Entity* ent = static_cast<Entity*>(pObject);
//这里简化了操作,由于只有一个"球体"和一个"正方体",
//所以只判断了球体和正方体的相交
if(ent->getName()=="cube")
{
//改变位置防止物体重叠
vtl=-vtl;
m_SphereNode->translate(vtl);
break;
}
}
}
}
相交查询
遍历所有的对象,找到一对一对的相交物体(废话呀,相交当然至少两个物体)。
//创建相交检测
IntersectionSceneQuery* pISQuery=m_SceneMgr->createIntersectionQuery();
//执行查询
IntersectionSceneQueryResult QResult=pISQuery->execute();
//遍历查询列表找出两个相交的物体
for (SceneQueryMovableIntersectionList::iterator iter = QResult.movables2movables.begin();
iter != QResult.movables2movables.end();++iter)
{
SceneQueryMovableObjectPair pObject=static_cast<SceneQueryMovableObjectPair>(*iter);
//if(pObject)
{
String strFirst=pObject.first->getName();
String strSecond=pObject.second->getName();
//下面加入你自己的两个物体相交判断代码,或者简单的用AABB的判断方法,
}
}
原创 韩举
http://www.hjpdiy.com
请注明转载地址
updateRay.setOrigin(mCamera->getPosition());
Ray的方向为垂直向下
updateRay.setDirection(Vector3::NEGATIVE_UNIT_Y);
这样就找出了与地面的交点。从而可以确定高度,既而就可以调整摄像机的高度,模拟出颠簸的感觉。
RaySceneQueryResult::iterator i = qryResult.begin();
if (i != qryResult.end() && i->worldFragment)
{
SceneQuery::WorldFragment* wf = i->worldFragment;
mCamera->setPosition(mCamera->getPosition().x,
i->worldFragment->singleIntersection.y + 10,
mCamera->getPosition().z);
}
"
Demo_Terrain 中的射线碰撞似乎是只能在非默认的 tree.. 场景中才能使用。见下面来自
http://blog.csdn.net/yeazer0/archive/2009/12/08/4960892.aspx
的文章.
--------------------------------------------------
OGRE碰撞检测之射线查询地形
//场景射线类:场景射线查询,全局指针变量,
RaySceneQuery* raySceneQuery = 0;
//在侦听器"FrameListener"类(继承于ExampleFrameListener)中,
//在侦渲染"FreameRendering"中执行场景射线一些列动作。
//射线类 静态:用于更新射线。
static Ray updateRay;
//设置射线起点。
updateRay.setOrigin(mCamera->getPosition());
//设置射线方向。
updateRay.setDirection(Vector3::NEGATIVE_UNIT_Y);
//将updateRay射线设置为场景查询射线。
raySceneQuery->setRay(updateRay);
//typedef std::vector<RaySceneQueryResultEntry> RaySceneQueryResult
//执行场景射线查询,并将结果存于qryResult容器中。
RaySceneQueryResult& qryResult = raySceneQuery->execute();
//遍历qryResult容器中的射线查询。
RaySceneQueryResult::iterator i = qryResult.begin();
if (i != qryResult.end() && i->worldFragment)
{ //把摄像机定在地形10个单位高的地方(y值)。
mCamera->setPosition(mCamera->getPosition().x,
i->worldFragment->singleIntersection.y + 10,
mCamera->getPosition().z);
}
//应用"Application"类中析构函数中,删除射线场景查询
delete raySceneQuery;
//在创建场景函数createScene中,定义raySceneQuery,定义为场景中的场景射线查询,
//射线起点为相机原点,方向Y轴负方向,即垂直向下。
raySceneQuery = mSceneMgr->createRayQuery(
Ray(mCamera->getPosition(), Vector3::NEGATIVE_UNIT_Y));
//选择场景管理器,这里我们需要使用地形场景管理器
//场景管理器置于根节点
//TerrainSceneManager在terrainscenemanager.h中定义的一个类
//多场景管理器能支持场景管理器把自己的类型注册为字符串ID
//(比如“OctreeSceneManager”或“TerrainSceneManager”)。
//这样你就可以使用迭代的方法在管理器中找到自己所需要的具体实现,
//亦或者可以简单的使用最后一个被注册进来的管理器类型。
//Ogre自身提供两个场景管理器类型:OctreeSceneManager和TerrainSceneManager。
//OctreeSceneManager是一个通用的场景管理器。TerrainSceneManager是
//一个为高度场场景优化的场景管理器。
mSceneMgr = mRoot->createSceneManager("TerrainSceneManager");
//在创建场景函数createScene中
//将地形置于场景中,地形数据存储于.cfg文件中(默认为terrain.cfg)
//地形数据的制作,通过高度图heightmap的运用。下面语句等同mSceneMgr -> setWorldGeometry("myterrain.cfg");
string terrain_cfg("myterrain.cfg");
mSceneMgr -> setWorldGeometry(terrain_cfg);