目录

前言

Demo效果

画楼梯代码

漫游器

行走碰撞逻辑调整

漫游器上楼梯

关键代码

初始化场景代码

Stairs代码UpDownStairsCameraMainpulator.h

Stairs代码UpDownStairsCameraMainpulator.cpp

工程模板:对应版本号1.23.0


OSG三维开发专栏

OSG开发笔记(一):OSG介绍、编译

OSG开发笔记(二):OSG帮助文档编译

OSG开发笔记(三):OSG使用osgQt嵌入Qt应用程序

OSG开发笔记(四):OSG不使用osgQt重写类嵌入Qt应用程序》:

OSG开发笔记(五):OSG场景理解与基础类概述

OSG开发笔记(六):OSG内存管理

OSG开发笔记(七):OSG复现OpenGL入门示例和OSG坐标系

OSG开发笔记(八):OSG模型文件存储与读取

OSG开发笔记(九):OSG模型的基本操作之添加/删除、显示/隐藏、开关节点开/》:

OSG开发笔记(十):OSG模型的变换之平移、旋转和缩放

OSG开发笔记(十一):OSG渲染状态与2D纹理映射

OSG开发笔记(十二):OSG基本几何图形、内置几何类型

OSG开发笔记(十三):OSG三维纹理映射(体渲染)

OSG开发笔记(十四):OSG交互

OSG开发笔记(十五):OSG光照

OSG开发笔记(十六):OSG视口、相机和视点

OSG开发笔记(十七):OSG中的相机移动

OSG开发笔记(十八):OSG鼠标拾取pick、拽托球体以及多光源

OSG开发笔记(十九):OSG文字显示

OSG开发笔记(二十):OSG使用HUD显示文字

OSG开发笔记(二十一):OSG使用HUD绘制图形以及纹理混合模式

OSG开发笔记(二十二):OSG场景背景

OSG开发笔记(二十三):Qt使用QOpenGLWidget渲染OSG地球仪

OSG开发笔记(二十四):OSG漫游之平移、转向和低抬头

OSG开发笔记(二十五):OSG漫游之CS移动、碰撞检测与跳跃

OSG开发笔记(二十六):OSG漫游之上下楼梯

OSG开发笔记(二十七):OSG路径漫游之录制播放固定路径动画

OSG开发笔记(二十八):OSG模型固定路径动画

  持续补充中…

 

        OSG开发笔记(二十六):OSG漫游之上下楼梯

 

前言

       前面实现了碰撞,比如掉下去摔死,前面有障碍物不能走动,跳跃等等,本篇写一个上下楼算法。

 

Demo效果

        OSG开发笔记(二十六):OSG漫游之上下楼梯-LMLPHP

Demo运行程序下载:

       https://download.csdn.net/download/qq21497936/11575952

 

画楼梯代码

       将多个立方体做成转换对象,然后偏移添加进去。

       OSG开发笔记(二十六):OSG漫游之上下楼梯-LMLPHP

       OSG开发笔记(二十六):OSG漫游之上下楼梯-LMLPHP

 

漫游器

       复制上一章的CS漫游器,对几个细节进行调整优化。

行走碰撞逻辑调整

       正常的行走碰撞不可能完全贴合地面,存在比如0.0001的距离,足够小的距离则可以等于没有距离,但是又没有碰撞到,而CS漫游器的时候我们设置的是0.1的距离,现在单独设置离地面垂直距离最小距离为0.0001,截取相关的代码如下:

        OSG开发笔记(二十六):OSG漫游之上下楼梯-LMLPHP

漫游器上楼梯

       这里涉及到精细碰撞,上楼梯主要是垂直的,上楼梯逻辑如下:

 OSG开发笔记(二十六):OSG漫游之上下楼梯-LMLPHP

  • 步骤一:先判断物体底部移动是否碰撞,有碰撞进行下一步;
  • 步骤二:在允许上的高度z值判断物体是否碰撞,没有碰撞进行下一步;
  • 步骤三:设置楼梯高度然后向前移动(准确的应该要获取前面底部碰撞但是高度z没碰撞的物体的盒子上边距离,然后将其设置为物体的);

       以上流程就是上楼梯算法步骤,下楼梯则无需,本身我们就有下坠机制。

       OSG开发笔记(二十六):OSG漫游之上下楼梯-LMLPHP

关键代码

初始化场景代码

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

09-17 07:10