一、框架设计
对于OSG的移动控制我们有两种方案,第一种通过viewer下的setCameraManipulator来自行设计控制脚本。这就需要编写一个类来继承osgGA::CameraManipulator。在这个类中我们可以将坦克的各种属性加入,包括了坦克的初始位置,坦克的旋转角度,相机的初始位置,相机的旋转角度,地形节点(方便以后做地形跟随,碰撞检测)。
二、相机跟随和坦克控制
载入坦克:
osg::MatrixTransform* CreateMT()
{
osg::MatrixTransform* mt = new osg::MatrixTransform;
osg::Node* tankBody = osgDB::readNodeFile("../work/tankm1.ive");
osg::Node* tankTurret = osgDB::readNodeFile("../work/turretm1.ive");
osg::Node* tankGun = osgDB::readNodeFile("../work/gunm1.flt");
osg::PositionAttitudeTransform* patbutton = new osg::PositionAttitudeTransform;
patbutton->setPosition(osg::Vec3(0.0, 0.0, 0));
patbutton->addChild(tankBody);
osg::PositionAttitudeTransform* pathead = new osg::PositionAttitudeTransform;
pathead->setPosition(osg::Vec3(0.0, 0.0, 0.1));
pathead->addChild(tankTurret);
osg::PositionAttitudeTransform* patgun = new osg::PositionAttitudeTransform;
patgun->setPosition(osg::Vec3(0.0, 0.0, 0.2));
patgun->addChild(tankGun);
osg::PositionAttitudeTransform* head = new osg::PositionAttitudeTransform;
head->addChild(pathead);
head->addChild(patgun);
mt->addChild(patbutton);
mt->addChild(head);
#pragma region 坦克尾烟粒子
osgParticle::ParticleSystem *dustParticleSystem = new osgParticle::ParticleSystem;
// 设置材质,emmisive和光照
dustParticleSystem->setDefaultAttributes("../work/dust2.rgb", false, false);
// 粒子系统继承自 Drawable 它可以作为geode的子节点
osg::Geode *geode = new osg::Geode;
mt->addChild(geode);
geode->addDrawable(dustParticleSystem);
// 加入‘updater’来帮助每一帧的管理
osgParticle::ParticleSystemUpdater *dustSystemUpdater = new osgParticle::ParticleSystemUpdater;
// 将updater和例子系统联系起来
dustSystemUpdater->addParticleSystem(dustParticleSystem);
// 把updater加入到root中
mt->addChild(dustSystemUpdater);
// 创建一个被我们粒子系统使用的粒子 并且定义它的一些属性
osgParticle::Particle smokeParticle;
smokeParticle.setSizeRange(osgParticle::rangef(0.01, 10.0)); // meters
smokeParticle.setLifeTime(2); // seconds
smokeParticle.setMass(0.01); // 重量 kg
// 制作我们默认的粒子
dustParticleSystem->setDefaultParticleTemplate(smokeParticle);
//创建一个默认的发射器 包括默认counter,placer和shooter
osgParticle::ModularEmitter *emitter = new osgParticle::ModularEmitter;
// 把emitter和粒子系统关联
emitter->setParticleSystem(dustParticleSystem);
// 获得一个emitter的counter句柄 每一帧将会加入不同数量的粒子
osgParticle::RandomRateCounter *dustRate = static_cast<osgParticle::RandomRateCounter *>(emitter->getCounter());
dustRate->setRateRange(2, 3); // generate 5 to 10 particles per second
// 定义placer ,创建和初始化一个 multi-segment placer
osgParticle::MultiSegmentPlacer* lineSegment = new osgParticle::MultiSegmentPlacer();
// Add vertices to our placer. This defines line seqments along which our particles will
// originate. (If we co-locate a tank and this emitter, this will result in dust particles
// originating from a line extending below and behind the tank model.)
lineSegment->addVertex(0, 0, -1);
lineSegment->addVertex(0, -1, -1);
lineSegment->addVertex(0, -8, 0);
// Use this placer for our emitter
emitter->setPlacer(lineSegment);
// 定义shooter, 创建和初始化一个 radial shooter
osgParticle::RadialShooter* smokeShooter = new osgParticle::RadialShooter();
// Set properties of this shooter
smokeShooter->setThetaRange(0.0, 3.14159 / 2); // radians, relative to Z axis.
smokeShooter->setInitialSpeedRange(50, 100); // meters/second
// Use this shooter for our emitter
emitter->setShooter(smokeShooter);
// 创建一个program来加入到粒子系统 Create a modular program and attach it to our particle system
osgParticle::ModularProgram *moveDustInAir = new osgParticle::ModularProgram;
moveDustInAir->setParticleSystem(dustParticleSystem);
// 创建一个operator来模拟重力
osgParticle::AccelOperator *accelUp = new osgParticle::AccelOperator;
accelUp->setToGravity(-1); // scale factor for normal acceleration due to gravity.
moveDustInAir->addOperator(accelUp);
// 计算空气阻力 Add an operator to our program to calculate the friction of air.
osgParticle::FluidFrictionOperator *airFriction = new osgParticle::FluidFrictionOperator;
airFriction->setFluidToAir();
//airFriction->setFluidDensity(1.2929/*air*/*5.0f);
moveDustInAir->addOperator(airFriction);
// Finally, add the program to the scene
mt->addChild(moveDustInAir);
#pragma endregion
mt->addChild(emitter);
mt->setMatrix(osg::Matrix::translate(0, 0, 0));
return mt;
}
相机的位置m_vPosition每次加上delta步长,Peng函数是碰撞检测的函数后面会详细介绍。height是碰撞检测能够检测到的地形的最低点。
void ChangePosition(osg::Vec3 delta)
{
float height = -1000.0f;
//m_vPosition = osg::Vec3(m_vPosition.x(), m_vPosition.y(), 3);
if (height = Peng(tankPosition + delta)) {
m_vPosition += delta;
m_vPosition = osg::Vec3(m_vPosition.x(), m_vPosition.y(), height + 11);
}
}
同理,坦克的位置更新也是
void ChangePositionTank(osg::Vec3 delta)
{
osg::Matrix mat;
float height = -1000.0f;
mat.makeRotate(tankRotation._v[0], osg::Vec3(1.0f, 0.0f, 0.0f),
tankRotation._v[1], osg::Vec3(0.0f, 1.0f, 0.0f),
tankRotation._v[2], osg::Vec3(0.0f, 0.0f, 1.0f));
if (height = Peng(tankPosition + delta)) {
//std::cout << "height" << height << std::endl;
tankPosition += delta;
tankPosition = osg::Vec3(tankPosition.x(), tankPosition.y(), height+1);
mt->setMatrix(mat*osg::Matrixd::translate(tankPosition));
}
}
三、地形跟随、碰撞检测
在坦克的中心点放置一条线,上方200m,下方1000m,如图所示。
float Peng(osg::Vec3 pos) {
osg::Vec3 start = osg::Vec3(pos.x(), pos.y(), pos.z() + 200);
osg::Vec3 end = osg::Vec3(pos.x(), pos.y(), pos.z() - 1000);
osgUtil::IntersectionVisitor* iv = new osgUtil::IntersectionVisitor;
osgUtil::LineSegmentIntersector* ls = new osgUtil::LineSegmentIntersector(start, end);
osgUtil::LineSegmentIntersector::Intersections intersections;
float height = -1000.0;
iv->setIntersector(ls);
TerrainNode->accept(*iv);
//如果有交点
if (ls->containsIntersections()) {
//取出交点
intersections = ls->getIntersections();
osgUtil::LineSegmentIntersector::Intersections::iterator iter = intersections.begin();
height = iter->getWorldIntersectPoint().z();
for (; iter != intersections.end(); iter++) {
if (height < iter->getWorldIntersectPoint().z()) {
height = iter->getWorldIntersectPoint().z();
}
}
return height;
}
else {
//不许走
std::cout << "不许走" << std::endl;
return 0;
}
四、按键检测(handle函数重写)
KEYDOWN表示键盘的按键按下,同理KEYUP表示键盘按键抬起。类似的还有MOVE表示鼠标的移动,PUSH表示鼠标的按下
bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us) {
switch (ea.getEventType()) {
case osgGA::GUIEventAdapter::KEYDOWN:
{
if (ea.getKey() == 'w' || ea.getKey() == 'W'|| ea.getKey()== 0xFF52)
{
ChangePosition(osg::Vec3(m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), m_fMoveSpeed*sinf(osg::PI_2 + m_vRotation[2]), 0));
ChangePositionTank(osg::Vec3(m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), m_fMoveSpeed*sinf(osg::PI_2 + m_vRotation[2]), 0));
}
else if (ea.getKey() == 's' || ea.getKey() == 'S'||ea.getKey() == 0xFF54)
{
ChangePosition(osg::Vec3(-m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), -m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation[2]), 0));
ChangePositionTank(osg::Vec3(-m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), -m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation[2]), 0));
}
else if (ea.getKey() == 'a' || ea.getKey() == 'A' || ea.getKey() == 0xFF51) //左旋转
{
float temp = m_vRotation[2];
m_vRotation[2] += osg::DegreesToRadians(m_fAngle);
ChangeRotation(m_fAngle);
ChangePosition(osg::Vec3(m_fRotateSpeed * (sinf(m_vRotation[2]) - sin(temp)), m_fRotateSpeed * ((1 - cosf(m_vRotation[2])) - (1 - cos(temp))), 0));
}
else if (ea.getKey() == 'd' || ea.getKey() == 'D'||ea.getKey() == 0xFF53) //右旋转
{
float temp = m_vRotation[2];
m_vRotation[2] -= osg::DegreesToRadians(m_fAngle);
ChangeRotation(-m_fAngle);
ChangePosition(osg::Vec3(m_fRotateSpeed * (sinf(m_vRotation[2]) - sin(temp)), m_fRotateSpeed * ((1 - cosf(m_vRotation[2])) - (1 - cos(temp))), 0));
}
else {
}
return false;
}
最后总的类函数如下
class Follow: public osgGA::CameraManipulator {
public:
Follow(osgViewer::Viewer* vv,osg::Node* terrain,osg::Group* root,osg::Camera* mastercam)
{
m_fRotateSpeed = 40;
master = mastercam;
rootNode = root;
TerrainNode = terrain;
m_vPosition = osg::Vec3(0, -40, 21);
m_vRotation = osg::Vec3(osg::PI_2-0.15f, 0, 0);
m_fMoveSpeed = 2.0;
m_fAngle = 2.5;
tankPosition = osg::Vec3(0, 0, 11);
tankRotation = osg::Vec3(0, 0, 0);
mt = CreateMT();
mt->setMatrix(osg::Matrixd::translate(tankPosition));
viewer = vv;
osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface();
wsi->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0), windowwidth, windowheight);
if (viewer)
{
viewer->getSceneData()->asGroup()->addChild(mt);
}
}
virtual void setByMatrix(const osg::Matrix& matrix) {
}
virtual void setByInverseMatrix(const osg::Matrix& matrix)
{
}
virtual osg::Matrixd getMatrix() const
{
osg::Matrixd mat;
mat.makeRotate(m_vRotation.x(),osg::Vec3(1,0,0),
m_vRotation.y(),osg::Vec3(0,1,0),
m_vRotation.z(),osg::Vec3(0,0,1));
return mat * osg::Matrixd::translate(m_vPosition);
}
virtual osg::Matrixd getInverseMatrix() const
{
osg::Matrixd mat;
mat.makeRotate(m_vRotation.x(), osg::Vec3(1, 0, 0),
m_vRotation.y(), osg::Vec3(0, 1, 0),
m_vRotation.z(), osg::Vec3(0, 0, 1));
return osg::Matrixd::inverse(mat * osg::Matrixd::translate(m_vPosition));
}
void ChangePosition(osg::Vec3 delta)
{
float height = -1000.0f;
//m_vPosition = osg::Vec3(m_vPosition.x(), m_vPosition.y(), 3);
if (height = Peng(tankPosition + delta)) {
m_vPosition += delta;
m_vPosition = osg::Vec3(m_vPosition.x(), m_vPosition.y(), height + 11);
}
}
void ChangeRotation(float angle) {
tankRotation[2] += osg::DegreesToRadians(angle);
osg::Matrix mat;
mat.makeRotate(tankRotation._v[0], osg::Vec3(1.0f, 0.0f, 0.0f),
tankRotation._v[1], osg::Vec3(0.0f, 1.0f, 0.0f),
tankRotation._v[2], osg::Vec3(0.0f, 0.0f, 1.0f));
mt->setMatrix(mat*osg::Matrixd::translate(tankPosition));
}
void ChangePositionTank(osg::Vec3 delta)
{
osg::Matrix mat;
float height = -1000.0f;
mat.makeRotate(tankRotation._v[0], osg::Vec3(1.0f, 0.0f, 0.0f),
tankRotation._v[1], osg::Vec3(0.0f, 1.0f, 0.0f),
tankRotation._v[2], osg::Vec3(0.0f, 0.0f, 1.0f));
if (height = Peng(tankPosition + delta)) {
//std::cout << "height" << height << std::endl;
tankPosition += delta;
tankPosition = osg::Vec3(tankPosition.x(), tankPosition.y(), height+1);
mt->setMatrix(mat*osg::Matrixd::translate(tankPosition));
}
}
//地形碰撞检测 地形跟随
float Peng(osg::Vec3 pos) {
osg::Vec3 start = osg::Vec3(pos.x(), pos.y(), pos.z() + 200);
osg::Vec3 end = osg::Vec3(pos.x(), pos.y(), pos.z() - 1000);
osgUtil::IntersectionVisitor* iv = new osgUtil::IntersectionVisitor;
osgUtil::LineSegmentIntersector* ls = new osgUtil::LineSegmentIntersector(start, end);
osgUtil::LineSegmentIntersector::Intersections intersections;
float height = -1000.0;
iv->setIntersector(ls);
TerrainNode->accept(*iv);
//如果有交点
if (ls->containsIntersections()) {
//取出交点
intersections = ls->getIntersections();
osgUtil::LineSegmentIntersector::Intersections::iterator iter = intersections.begin();
height = iter->getWorldIntersectPoint().z();
for (; iter != intersections.end(); iter++) {
if (height < iter->getWorldIntersectPoint().z()) {
height = iter->getWorldIntersectPoint().z();
}
}
return height;
}
else {
//不许走
std::cout << "不许走" << std::endl;
return 0;
}
}
bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us) {
switch (ea.getEventType()) {
case osgGA::GUIEventAdapter::KEYDOWN:
{
if (ea.getKey() == 'w' || ea.getKey() == 'W'|| ea.getKey()== 0xFF52)
{
ChangePosition(osg::Vec3(m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), m_fMoveSpeed*sinf(osg::PI_2 + m_vRotation[2]), 0));
ChangePositionTank(osg::Vec3(m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), m_fMoveSpeed*sinf(osg::PI_2 + m_vRotation[2]), 0));
}
else if (ea.getKey() == 's' || ea.getKey() == 'S'||ea.getKey() == 0xFF54)
{
ChangePosition(osg::Vec3(-m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), -m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation[2]), 0));
ChangePositionTank(osg::Vec3(-m_fMoveSpeed * cosf(osg::PI_2 + m_vRotation[2]), -m_fMoveSpeed * sinf(osg::PI_2 + m_vRotation[2]), 0));
}
else if (ea.getKey() == 'a' || ea.getKey() == 'A' || ea.getKey() == 0xFF51) //左旋转
{
float temp = m_vRotation[2];
m_vRotation[2] += osg::DegreesToRadians(m_fAngle);
ChangeRotation(m_fAngle);
ChangePosition(osg::Vec3(m_fRotateSpeed * (sinf(m_vRotation[2]) - sin(temp)), m_fRotateSpeed * ((1 - cosf(m_vRotation[2])) - (1 - cos(temp))), 0));
}
else if (ea.getKey() == 'd' || ea.getKey() == 'D'||ea.getKey() == 0xFF53) //右旋转
{
float temp = m_vRotation[2];
m_vRotation[2] -= osg::DegreesToRadians(m_fAngle);
ChangeRotation(-m_fAngle);
ChangePosition(osg::Vec3(m_fRotateSpeed * (sinf(m_vRotation[2]) - sin(temp)), m_fRotateSpeed * ((1 - cosf(m_vRotation[2])) - (1 - cos(temp))), 0));
}
else {
}
return false;
}
}
osg::MatrixTransform* CreateMT()
{
osg::MatrixTransform* mt = new osg::MatrixTransform;
osg::Node* tankBody = osgDB::readNodeFile("../work/tankm1.ive");
osg::Node* tankTurret = osgDB::readNodeFile("../work/turretm1.ive");
osg::Node* tankGun = osgDB::readNodeFile("../work/gunm1.flt");
osg::PositionAttitudeTransform* patbutton = new osg::PositionAttitudeTransform;
patbutton->setPosition(osg::Vec3(0.0, 0.0, 0));
patbutton->addChild(tankBody);
osg::PositionAttitudeTransform* pathead = new osg::PositionAttitudeTransform;
pathead->setPosition(osg::Vec3(0.0, 0.0, 0.1));
pathead->addChild(tankTurret);
osg::PositionAttitudeTransform* patgun = new osg::PositionAttitudeTransform;
patgun->setPosition(osg::Vec3(0.0, 0.0, 0.2));
patgun->addChild(tankGun);
osg::PositionAttitudeTransform* head = new osg::PositionAttitudeTransform;
head->addChild(pathead);
head->addChild(patgun);
mt->addChild(patbutton);
mt->addChild(head);
#pragma region 坦克尾烟粒子
osgParticle::ParticleSystem *dustParticleSystem = new osgParticle::ParticleSystem;
// 设置材质,emmisive和光照
dustParticleSystem->setDefaultAttributes("../work/dust2.rgb", false, false);
// 粒子系统继承自 Drawable 它可以作为geode的子节点
osg::Geode *geode = new osg::Geode;
mt->addChild(geode);
geode->addDrawable(dustParticleSystem);
// 加入‘updater’来帮助每一帧的管理
osgParticle::ParticleSystemUpdater *dustSystemUpdater = new osgParticle::ParticleSystemUpdater;
// 将updater和例子系统联系起来
dustSystemUpdater->addParticleSystem(dustParticleSystem);
// 把updater加入到root中
mt->addChild(dustSystemUpdater);
// 创建一个被我们粒子系统使用的粒子 并且定义它的一些属性
osgParticle::Particle smokeParticle;
smokeParticle.setSizeRange(osgParticle::rangef(0.01, 10.0)); // meters
smokeParticle.setLifeTime(2); // seconds
smokeParticle.setMass(0.01); // 重量 kg
// 制作我们默认的粒子
dustParticleSystem->setDefaultParticleTemplate(smokeParticle);
//创建一个默认的发射器 包括默认counter,placer和shooter
osgParticle::ModularEmitter *emitter = new osgParticle::ModularEmitter;
// 把emitter和粒子系统关联
emitter->setParticleSystem(dustParticleSystem);
// 获得一个emitter的counter句柄 每一帧将会加入不同数量的粒子
osgParticle::RandomRateCounter *dustRate = static_cast<osgParticle::RandomRateCounter *>(emitter->getCounter());
dustRate->setRateRange(2, 3); // generate 5 to 10 particles per second
// 定义placer ,创建和初始化一个 multi-segment placer
osgParticle::MultiSegmentPlacer* lineSegment = new osgParticle::MultiSegmentPlacer();
// Add vertices to our placer. This defines line seqments along which our particles will
// originate. (If we co-locate a tank and this emitter, this will result in dust particles
// originating from a line extending below and behind the tank model.)
lineSegment->addVertex(0, 0, -1);
lineSegment->addVertex(0, -1, -1);
lineSegment->addVertex(0, -8, 0);
// Use this placer for our emitter
emitter->setPlacer(lineSegment);
// 定义shooter, 创建和初始化一个 radial shooter
osgParticle::RadialShooter* smokeShooter = new osgParticle::RadialShooter();
// Set properties of this shooter
smokeShooter->setThetaRange(0.0, 3.14159 / 2); // radians, relative to Z axis.
smokeShooter->setInitialSpeedRange(50, 100); // meters/second
// Use this shooter for our emitter
emitter->setShooter(smokeShooter);
// 创建一个program来加入到粒子系统 Create a modular program and attach it to our particle system
osgParticle::ModularProgram *moveDustInAir = new osgParticle::ModularProgram;
moveDustInAir->setParticleSystem(dustParticleSystem);
// 创建一个operator来模拟重力
osgParticle::AccelOperator *accelUp = new osgParticle::AccelOperator;
accelUp->setToGravity(-1); // scale factor for normal acceleration due to gravity.
moveDustInAir->addOperator(accelUp);
// 计算空气阻力 Add an operator to our program to calculate the friction of air.
osgParticle::FluidFrictionOperator *airFriction = new osgParticle::FluidFrictionOperator;
airFriction->setFluidToAir();
//airFriction->setFluidDensity(1.2929/*air*/*5.0f);
moveDustInAir->addOperator(airFriction);
// Finally, add the program to the scene
mt->addChild(moveDustInAir);
#pragma endregion
mt->addChild(emitter);
mt->setMatrix(osg::Matrix::translate(0, 0, 0));
return mt;
}
private:
osg::Vec3 m_vPosition;
osg::Vec3 m_vRotation;
float m_fMoveSpeed;
float m_fRotateSpeed;
float m_fAngle;
osg::Node * TerrainNode;
osg::Group* rootNode;
osg::Camera * master;
osg::Vec3 tankPosition;
osg::Vec3 tankRotation;
osg::MatrixTransform* mt;
osgViewer::Viewer* viewer;
unsigned int windowwidth, windowheight;
};
关注:人工智能咖 随时获取动态