目录
Stairs代码UpDownStairsCameraMainpulator.h
Stairs代码UpDownStairsCameraMainpulator.cpp
OSG三维开发专栏
《OSG开发笔记(三):OSG使用osgQt嵌入Qt应用程序》
《OSG开发笔记(四):OSG不使用osgQt重写类嵌入Qt应用程序》:
《OSG开发笔记(七):OSG复现OpenGL入门示例和OSG坐标系》
《OSG开发笔记(九):OSG模型的基本操作之添加/删除、显示/隐藏、开关节点开/关》:
《OSG开发笔记(十):OSG模型的变换之平移、旋转和缩放》
《OSG开发笔记(十二):OSG基本几何图形、内置几何类型》
《OSG开发笔记(十八):OSG鼠标拾取pick、拽托球体以及多光源》
《OSG开发笔记(二十一):OSG使用HUD绘制图形以及纹理混合模式》
《OSG开发笔记(二十三):Qt使用QOpenGLWidget渲染OSG和地球仪》
《OSG开发笔记(二十四):OSG漫游之平移、转向和低抬头》
《OSG开发笔记(二十五):OSG漫游之CS移动、碰撞检测与跳跃》
《OSG开发笔记(二十七):OSG路径漫游之录制播放固定路径动画》
持续补充中…
OSG开发笔记(二十六):OSG漫游之上下楼梯
前言
前面实现了碰撞,比如掉下去摔死,前面有障碍物不能走动,跳跃等等,本篇写一个上下楼算法。
Demo效果
Demo运行程序下载:
https://download.csdn.net/download/qq21497936/11575952
画楼梯代码
将多个立方体做成转换对象,然后偏移添加进去。
漫游器
复制上一章的CS漫游器,对几个细节进行调整优化。
行走碰撞逻辑调整
正常的行走碰撞不可能完全贴合地面,存在比如0.0001的距离,足够小的距离则可以等于没有距离,但是又没有碰撞到,而CS漫游器的时候我们设置的是0.1的距离,现在单独设置离地面垂直距离最小距离为0.0001,截取相关的代码如下:
漫游器上楼梯
这里涉及到精细碰撞,上楼梯主要是垂直的,上楼梯逻辑如下:
- 步骤一:先判断物体底部移动是否碰撞,有碰撞进行下一步;
- 步骤二:在允许上的高度z值判断物体是否碰撞,没有碰撞进行下一步;
- 步骤三:设置楼梯高度然后向前移动(准确的应该要获取前面底部碰撞但是高度z没碰撞的物体的盒子上边距离,然后将其设置为物体的);
以上流程就是上楼梯算法步骤,下楼梯则无需,本身我们就有下坠机制。
关键代码
初始化场景代码
osg::ref_ptr<osg::Node> OsgWidget::getUpDownStairsAndManipulator()
{
// 隐藏按键面板
ui->groupBox_pannel->setVisible(false);
osg::ref_ptr<osg::Group> pGroup = new osg::Group;
// 首先画个地板
{
osg::ref_ptr<osg::Geode> pGeode = new osg::Geode;
// 地板格子长、宽、行、列
float dx = 1.0f;
float dy = 1.0f;
int rows = 20;
int cols = 20;
osg::Vec3 vec3center(0.0f - dx * rows / 2, 0.0f - dx * cols / 2, 0.0f);
// 顶点
osg::Vec3Array *pVec3Array = new osg::Vec3Array;
for(int index = 0; index <= rows; index++)
{
for(int index2 = 0; index2 <= cols; index2++)
{
pVec3Array->push_back(osg::Vec3(index2 * dx, index * dy, 0) + vec3center);
}
}
// 颜色
osg::Vec4Array *pVec4ColorArray = new osg::Vec4Array;
pVec4ColorArray->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
pVec4ColorArray->push_back(osg::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
// 索引(使用索引,之前的demo没有使用过,此源码第一次使用)之处
osg::UIntArray * pCoordIndices = new osg::UIntArray;
osg::UIntArray * pColorIndices = new osg::UIntArray;
for(int index = 0; index < rows; index++)
{
for(int index2 = 0; index2 < cols; index2++)
{
// 顶点索引
pCoordIndices->push_back((index2 ) + (index ) * (cols + 1));
pCoordIndices->push_back((index2 + 1) + (index ) * (cols + 1));
pCoordIndices->push_back((index2 + 1) + (index + 1) * (cols + 1));
pCoordIndices->push_back((index2 ) + (index + 1) * (cols + 1));
pColorIndices->push_back(((index2 ) + (index + 1) * (cols+1) ) % pVec4ColorArray->size());
}
}
// 法线
osg::Vec3Array * pNormal = new osg::Vec3Array;
pNormal->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));
// 图形(支持索引的需要使用deprecated_osg: :Geometry)
deprecated_osg::Geometry * pGeometry = new deprecated_osg::Geometry;
pGeometry->setVertexArray(pVec3Array);
pGeometry->setVertexIndices(pCoordIndices);
pGeometry->setColorArray(pVec4ColorArray);
pGeometry->setColorIndices(pColorIndices);
pGeometry->setColorBinding(deprecated_osg::Geometry::BIND_PER_PRIMITIVE);
pGeometry->setSecondaryColorArray(pVec4ColorArray);
pGeometry->setSecondaryColorIndices(pColorIndices);
pGeometry->setSecondaryColorBinding(deprecated_osg::Geometry::BIND_PER_PRIMITIVE);
pGeometry->setNormalArray(pNormal);
pGeometry->setNormalBinding(deprecated_osg::Geometry::BIND_OVERALL);
pGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, pCoordIndices->size()));
osg::StateSet *pStateSet = pGeometry->getOrCreateStateSet();
pStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
pStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
pGeode->addDrawable(pGeometry);
pGroup->addChild(pGeode);
}
// 画几个立方体,当作楼梯
{
int rows = 20;
int cols = 20;
// 绘制立方体
osg::ref_ptr<osg::Geode> pGeode = new osg::Geode;
// 精细度
osg::ref_ptr<osg::TessellationHints> pHints = new osg::TessellationHints;
pHints->setDetailRatio(0.5);
// 颜色
// 绘制几何体
float width = 0.25f;
osg::ref_ptr<osg::ShapeDrawable> pShapeDrawable =
new osg::ShapeDrawable(
new osg::Box(osg::Vec3f(-width * rows * 2 + 0.25 + width + 2 * width, 0, width / 2),
width * 2,
width * 2,
width),
pHints.get());
pGeode->addDrawable(pShapeDrawable.get());
osg::StateSet *pStateSet = pGeode->getOrCreateStateSet();
pStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON);
pStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
// 关闭光照开启深度测试
for(int index = 0; index < rows; index++)
{
for(int index2 = 0; index2 < cols; index2++)
{
osg::ref_ptr<osg::MatrixTransform> pTrans = new osg::MatrixTransform;
pTrans->addChild(pGeode);
osg::Matrix matrix = pTrans->getMatrix();
matrix *= osg::Matrix::translate((width * index + index2 * rows * width)*2,
0,
width * index + index2 * rows * width);
pTrans->setMatrix(matrix);
pGroup->addChild(pTrans.get());
}
}
}
// 设置漫游器
{
UpDownStairsCameraMainpulator *pUpDownStairsCameraMainpulator = new UpDownStairsCameraMainpulator;
pUpDownStairsCameraMainpulator->setHitsNode(pGroup.get());
pUpDownStairsCameraMainpulator->setOsgWidget(this);
_pViewer->setCameraManipulator(pUpDownStairsCameraMainpulator);
}
return pGroup.get();
}
Stairs代码UpDownStairsCameraMainpulator.h
#ifndef UPDOWNSTAIRSCAMERAMAINPULATOR_H
#define UPDOWNSTAIRSCAMERAMAINPULATOR_H
#include <osgGA/CameraManipulator>
#include <osgGA/GUIActionAdapter>
#include <osgGA/GUIEventAdapter>
#include <QObject>
class OsgWidget;
class UpDownStairsCameraMainpulator : public osgGA::CameraManipulator, public QObject
{
public:
UpDownStairsCameraMainpulator();
~UpDownStairsCameraMainpulator();
public:
virtual void setByMatrix(const osg::Matrixd& matrix); // 设置相机的位置姿态矩阵
virtual void setByInverseMatrix(const osg::Matrixd& matrix); // 设置相机的视图矩阵
virtual osg::Matrixd getMatrix() const; // 获取相机的姿态矩阵
virtual osg::Matrixd getInverseMatrix() const; // 获取相机的视图矩阵
virtual bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &us);
public:
void setHitsNode(osg::Node *pNode);
void setOsgWidget(OsgWidget *pOsgWidget);
protected:
bool isHits(osg::Vec3f oldPos, osg::Vec3 newPos);
void reset();
protected:
void timerEvent(QTimerEvent *event);
private:
osg::Vec3 _position; // 视点当前位置
osg::Vec3 _rotation; // 朝向
float _moveStep; // 移动步长5
float _rotateStep; // 旋转步长
float _originAngleX; // X轴初始化时偏移角度
float _originAngleY; // Y轴初始化时偏移角度
float _originAngleZ; // Z轴初始化时偏移角度
float _offsetAngleX; // X轴当前旋转角度
float _offsetAngleY; // Y轴当前旋转角度
float _offsetAngleZ; // Z轴当前旋转角度
float _radius; // 将视口的当前物体认为是一个球
float _verticalSpace; // 间距,踩地面上实际碰撞的最小间距
osg::Node *_pNode;
bool _canJump;
OsgWidget *_pOsgWidget;
};
#endif // MYCAMERAMAINPULATOR_H
Stairs代码UpDownStairsCameraMainpulator.cpp
#include "UpDownStairsCameraMainpulator.h"
#include <QDebug>
#include "osg/Math"
#include "osg/LineSegment"
#include "OsgWidget.h"
UpDownStairsCameraMainpulator::UpDownStairsCameraMainpulator()
: QObject(0),
_originAngleX(90.0f),
_originAngleY(0.0f),
_originAngleZ(0.0f),
_offsetAngleX(0.0f),
_offsetAngleY(0.0f),
_offsetAngleZ(0.0f),
_radius(0.5f),
_pNode(0),
_verticalSpace(0.0001),
_canJump(false)
{
// 使用漫游器的初始化位置
_position = osg::Vec3(-9.5f, 0.0f, _radius + _verticalSpace);
_offsetAngleZ = -90.0f;
_rotation = osg::Vec3( osg::DegreesToRadians(_originAngleX + _offsetAngleX),
osg::DegreesToRadians(_originAngleY + _offsetAngleY),
osg::DegreesToRadians(_originAngleZ + _offsetAngleZ));
_moveStep = 0.08f;
_rotateStep = 2.0f;
startTimer(15);
}
UpDownStairsCameraMainpulator::~UpDownStairsCameraMainpulator()
{
}
void UpDownStairsCameraMainpulator::setByMatrix(const osg::Matrixd &matrix)
{
}
void UpDownStairsCameraMainpulator::setByInverseMatrix(const osg::Matrixd &matrix)
{
computeHomePosition();
}
osg::Matrixd UpDownStairsCameraMainpulator::getMatrix() const
{
osg::Matrixd mat;
mat.makeTranslate(_position);
return osg::Matrixd::rotate(_rotation.x(), osg::X_AXIS,
_rotation.y(), osg::Y_AXIS,
_rotation.z(), osg::Z_AXIS) * mat;
}
osg::Matrixd UpDownStairsCameraMainpulator::getInverseMatrix() const
{
osg::Matrixd mat;
mat.makeTranslate(_position);
return osg::Matrixd::inverse(osg::Matrixd::rotate(_rotation.x(), osg::X_AXIS,
_rotation.y(), osg::Y_AXIS,
_rotation.z(), osg::Z_AXIS) * mat);
}
bool UpDownStairsCameraMainpulator::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &us)
{
float offsetX = 0.0f;
float offsetY = 0.0f;
float offsetZ = 0.0f;
osg::Vec3f newPosition;
osg::Vec3f newPositionBottom;
osg::Vec3f positionBottom;
switch (ea.getEventType())
{
case osgGA::GUIEventAdapter::KEYDOWN:
switch (ea.getKey()) {
case osgGA::GUIEventAdapter::KEY_E:
case osgGA::GUIEventAdapter::KEY_Right:
_offsetAngleZ = _offsetAngleZ - _rotateStep;
_rotation[2] = osg::DegreesToRadians(_originAngleZ + _offsetAngleZ);
break;
case osgGA::GUIEventAdapter::KEY_Q:
case osgGA::GUIEventAdapter::KEY_Left:
_offsetAngleZ = _offsetAngleZ + _rotateStep;
_rotation[2] = osg::DegreesToRadians(_originAngleZ + _offsetAngleZ);
break;
case osgGA::GUIEventAdapter::KEY_W:
offsetY = _moveStep * cos(osg::DegreesToRadians(-_offsetAngleZ));
offsetX = _moveStep * sin(osg::DegreesToRadians(-_offsetAngleZ));
newPosition = _position + osg::Vec3f(offsetX, offsetY, offsetZ);
positionBottom = _position - osg::Vec3f(0, 0, _radius);
newPositionBottom = newPosition - osg::Vec3f(0, 0, _radius);
if(!isHits(positionBottom, newPositionBottom))
{
_position = newPosition;
}else if(!isHits(_position, newPosition))
{
_position[2] += 0.5f;
}
break;
case osgGA::GUIEventAdapter::KEY_S:
offsetY = _moveStep * cos(osg::DegreesToRadians(-_offsetAngleZ));
offsetX = _moveStep * sin(osg::DegreesToRadians(-_offsetAngleZ));
newPosition = _position - osg::Vec3f(offsetX, offsetY, offsetZ);
positionBottom = _position - osg::Vec3f(0, 0, _radius);
newPositionBottom = newPosition - osg::Vec3f(0, 0, _radius);
if(!isHits(positionBottom, newPositionBottom))
{
_position = newPosition;
}else if(!isHits(_position, newPosition))
{
_position[2] += 0.5f;
}
break;
case osgGA::GUIEventAdapter::KEY_A:
offsetX = -_moveStep * cos(osg::DegreesToRadians(-_offsetAngleZ));
offsetY = _moveStep * sin(osg::DegreesToRadians(-_offsetAngleZ));
newPosition = _position + osg::Vec3f(offsetX, offsetY, offsetZ);
positionBottom = _position - osg::Vec3f(0, 0, _radius);
newPositionBottom = newPosition - osg::Vec3f(0, 0, _radius);
if(!isHits(positionBottom, newPositionBottom))
{
_position = newPosition;
}else if(!isHits(_position, newPosition))
{
_position[2] += 0.5f;
}
break;
case osgGA::GUIEventAdapter::KEY_D:
offsetX = _moveStep * cos(osg::DegreesToRadians(-_offsetAngleZ));
offsetY = -_moveStep * sin(osg::DegreesToRadians(-_offsetAngleZ));
newPosition = _position + osg::Vec3f(offsetX, offsetY, offsetZ);
positionBottom = _position - osg::Vec3f(0, 0, _radius);
newPositionBottom = newPosition - osg::Vec3f(0, 0, _radius);
if(!isHits(positionBottom, newPositionBottom))
{
_position = newPosition;
}else if(!isHits(_position, newPosition))
{
_position[2] += 0.5f;
}
break;
case osgGA::GUIEventAdapter::KEY_Up:
if(_offsetAngleX > 20)
{
break;
}
_offsetAngleX += _rotateStep;
_rotation[0] = osg::DegreesToRadians(_originAngleX + _offsetAngleX);
break;
case osgGA::GUIEventAdapter::KEY_Down:
if(_offsetAngleX < -20)
{
break;
}
_offsetAngleX -= _rotateStep;
_rotation[0] = osg::DegreesToRadians(_originAngleX + _offsetAngleX);
break;
case osgGA::GUIEventAdapter::KEY_Space:
// if(_position[2] >= _radius && _position[2] <= _radius + _moveStep)
if(_canJump)
{
_position[2] += 1.0f;
}
_canJump = false;
break;
default:
break;
}
break;
default:
break;
}
return true;
}
void UpDownStairsCameraMainpulator::setHitsNode(osg::Node *pNode)
{
_pNode = pNode;
}
bool UpDownStairsCameraMainpulator::isHits(osg::Vec3f oldPos, osg::Vec3 newPos)
{
bool ret = false;
if(_pNode == 0)
{
return ret;
}
// osg3.4.0 类 osgUtil::IntersectionVisitor已经细化,此处用
osg::ref_ptr<osgUtil::LineSegmentIntersector> pLs = new osgUtil::LineSegmentIntersector(oldPos, newPos);
osg::ref_ptr<osgUtil::IntersectionVisitor> pIv = new osgUtil::IntersectionVisitor(pLs);
// 碰撞检测
_pNode->accept(*pIv);
if(pLs->containsIntersections())
{
// qDebug() << "hit";
ret = true;
}else{
ret = false;
}
return ret;
}
void UpDownStairsCameraMainpulator::reset()
{
_originAngleX = 90.0f;
_originAngleY = 0.0f;
_originAngleZ = 0.0f;
_offsetAngleX = 0.0f;
_offsetAngleY = 0.0f;
_offsetAngleZ = -90.0f;
// 使用漫游器的初始化位置
_position = osg::Vec3(-9.5f, 0.0f, _radius + _verticalSpace);
_rotation = osg::Vec3( osg::DegreesToRadians(_originAngleX + _offsetAngleX),
osg::DegreesToRadians(_originAngleY + _offsetAngleY),
osg::DegreesToRadians(_originAngleZ + _offsetAngleZ));
if(_pOsgWidget)
{
_pOsgWidget->update();
}
}
void UpDownStairsCameraMainpulator::timerEvent(QTimerEvent *event)
{
// 往下offsetX = -_moveStep * cos(osg::DegreesToRadians(-_offsetAngleZ));
float offsetX = 0.0f;
float offsetY = 0.0f;
float offsetZ = 0.0f;
offsetZ = -_moveStep - _radius;
osg::Vec3f newPosition = _position + osg::Vec3f(offsetX, offsetY, offsetZ);
if(!isHits(_position, newPosition))
{
newPosition[2] += _radius;
_position = newPosition;
if(_position[2] < -10.0f)
{
reset();
}
if(_pOsgWidget)
{
_pOsgWidget->update();
}
}else{
if(newPosition[2] == 0.0 + _verticalSpace)
{
}else{
newPosition[2] = _verticalSpace;
_pOsgWidget->update();
}
_canJump = true;
}
}
void UpDownStairsCameraMainpulator::setOsgWidget(OsgWidget *pOsgWidget)
{
_pOsgWidget = pOsgWidget;
}
工程模板:对应版本号1.23.0
对应版本号1.23.0