七、OSG学习笔记-碰撞检测
前一章节:
六、OSG学习笔记-漫游(操作器)-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/145515189?spm=1001.2014.3001.5502
主要用来判断空间里两个物体的交点
场景显示原理:
场景中物体是否发生交点,需要遍历 node 节点,如下图:
一、实现简单的碰撞检测
代码:OsgStudy/Hits · CuiQingCheng/OsgStudy - 码云 - 开源中国
代码如下:
#include<windows.h>
#include<iostream>
#include<osgViewer/Viewer>
#include<osgDB/ReadFile>
#include<osg/Geode>
#include<osg/Geometry>
#include<osg/ShapeDrawable>
#include<osgUtil/IntersectionVisitor>
#include<osgUtil/LineSegmentIntersector>
// 画线
osg::ref_ptr<osg::Geode> CreateLine(const osg::Vec3& start, const osg::Vec3& end)
{
osg::ref_ptr<osg::Geode> gNode = new osg::Geode;
osg::ref_ptr<osg::Geometry> gy = new osg::Geometry;
osg::ref_ptr<osg::Vec3Array> coords = new osg::Vec3Array; // 存放点集
gNode->addDrawable(gy.get());
gy->setVertexArray(coords);
coords->push_back(start);
coords->push_back(end);
gy->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_LOOP, 0, 2));
return gNode;
}
// 画圆
osg::ref_ptr<osg::Geode> CreateSphere(const osg::Vec3 ¢er)
{
osg::ref_ptr<osg::Geode> gNode = new osg::Geode;
gNode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(center, 0.2)));
return gNode;
}
// 画盒子
osg::ref_ptr<osg::Geode> CreateBox()
{
osg::ref_ptr<osg::Geode> gnode = new osg::Geode;
gnode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0, 0, 0), 10.0, 10.0, 10.0)));// 这里中心点为 (0,0,0)
//gnode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0, 0, 0), 0.1, 0.1, 20.0)));// 这里中心点为 (0,0,0)
return gnode;
}
int main()
{
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
viewer->setUpViewInWindow(100, 100, 1500, 1000);
osg::ref_ptr<osg::Group> group = new osg::Group;
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("glider.osg");
osg::Vec3 start = osg::Vec3(-10, 7, 15);
osg::Vec3 end = osg::Vec3(5, -4, -12);
osgUtil::LineSegmentIntersector::Intersections intersections;
// 用于定义线段相交检测器。
osg::ref_ptr<osgUtil::LineSegmentIntersector> lineIv = new osgUtil::LineSegmentIntersector(start, end);
// 用于遍历场景图并执行相交检测。
osg::ref_ptr<osgUtil::IntersectionVisitor> iv = new osgUtil::IntersectionVisitor(lineIv);
group->addChild(node.get());
group->addChild(CreateBox());
group->accept(*iv.get());
group->addChild(CreateLine(start, end));
// 如果有碰撞,则输出所有交点
if (lineIv->containsIntersections())
{
intersections = lineIv->getIntersections();
for (osgUtil::LineSegmentIntersector::Intersections::iterator iter = intersections.begin(); iter != intersections.end(); ++iter)
{
std::cout << (iter)->getWorldIntersectPoint().x() << " " << (iter)->getWorldIntersectPoint().y() << " " << (iter)->getWorldIntersectPoint().z()<< std::endl;
group->addChild(CreateSphere((iter)->getWorldIntersectPoint()));
}
}
viewer->setSceneData(group.get());
return viewer->run();
}
代码执行效果:
二、上楼梯算法(碰撞检测应用)
上楼梯:
示例代码:
OsgStudy/TravelPlus · CuiQingCheng/OsgStudy - 码云 - 开源中国
Travel.h
#pragma once
#include<windows.h>
#include<iostream>
#include<osg/Node>
#include<osgViewer/Viewer>
#include<osgViewer/ViewerEventHandlers>
#include<osgDB/ReadFile>
#include<osgGA/TrackballManipulator>
// 图元库
#include<osg/Geode>
#include<osg/ShapeDrawable>
#include<osg/Matrix>
#include<osg/MatrixTransform>
#include<osg/PositionAttitudeTransform>
// 回调
#include<osg/AnimationPath>
#include<osgGA/GUIEventAdapter>
#include <osgGA/StandardManipulator>
// 自定义操作器类
#include "../NodeMatrix/NodeMatrix.h"
// 导入静态库
#ifdef _DEBUG
#pragma comment (lib, "../x64/Debug/NodeMatrix.lib")
#else
#pragma comment (lib, "../x64/Release/NodeMatrix.lib")
#endif // DEBUG
class TravelManipulator : public osgGA::CameraManipulator
{
public:
TravelManipulator();
~TravelManipulator();
public:
// 实现得到和设置矩阵的接口
/** 设置当前视口 using a 4x4 Matrix.*/
void setByMatrix(const osg::Matrixd& matrix) override;
/** 设置当前视口 using a 4x4 Matrix.*/
void setByInverseMatrix(const osg::Matrixd& matrix) override;
/** 得到当前矩阵 4x4 Matrix.*/
osg::Matrixd getMatrix() const override;
/** 得到当前逆矩阵 used as a model view matrix.*/
osg::Matrixd getInverseMatrix() const override;
// 响应事件
bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us )override;
// 设置步长
void setStep(int iStep);
// 获得步长
int getStep() const;
// 设置到某一点
void setPosition(osg::Vec3d pos);
// 得到当前坐标
osg::Vec3d getPosition();
// 设置进行碰撞检测节点
void setNode(osg::Node* node);
void setAuto(bool bauto);
private:
// 改变位置
void ChangePosition(const osg::Vec3d& delta);
private:
// 进行碰撞检测节点
osg::Node* m_node{NULL};
// 当前物体
NodeMatrix* m_nCurrentNode{ NULL };
// 视点
osg::Vec3d m_vPosition;
// 朝向
osg::Vec3 m_vRotation;
// 移动步长
int m_vStep;
// 旋转步长
float m_vRotateStep;
// 记录坐标
int m_iLeftX;
int m_iLeftY;
// 鼠标左键是否按下
bool m_bLeftDown{ false };
bool m_bAuto{ false };
};
Travel.cpp
#include "Travel.h"
#include <osgGA/GUIEventAdapter>
#include<osgUtil/IntersectionVisitor>
#include<osgUtil/LineSegmentIntersector>
TravelManipulator::TravelManipulator( )
{
m_vPosition = osg::Vec3(0, 0, 10);
// 围绕X轴转转90度
m_vRotation = osg::Vec3(osg::PI_2, 0, 0);
m_vStep = 2;
m_vRotateStep = 0.0;
}
TravelManipulator::~TravelManipulator()
{
}
void TravelManipulator::setByMatrix(const osg::Matrixd& matrix)
{
}
void TravelManipulator::setByInverseMatrix(const osg::Matrixd& matrix)
{
}
osg::Matrixd TravelManipulator::getMatrix() const
{
osg::Matrixd mat;
mat.makeTranslate(m_vPosition);
osg::Matrixd retMat = osg::Matrixd::rotate(m_vRotation[0], osg::X_AXIS, m_vRotation[1], osg::Y_AXIS, m_vRotation[2], osg::Z_AXIS) * mat;
return retMat;
}
osg::Matrixd TravelManipulator::getInverseMatrix() const
{
osg::Matrixd mat;
mat.makeTranslate(m_vPosition);
osg::Matrixd retMat = osg::Matrixd::rotate(m_vRotation[0], osg::X_AXIS, m_vRotation[1], osg::Y_AXIS, m_vRotation[2], osg::Z_AXIS) * mat;
return osg::Matrixd::inverse(retMat);
}
bool TravelManipulator::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us)
{
osgViewer::Viewer* view = dynamic_cast<osgViewer::Viewer*>(&us);
if (view)
{
m_node = view->getSceneData();
}
switch (ea.getEventType())
{
case osgGA::GUIEventAdapter::KEYDOWN:
{
if ((ea.getKey() == 'w') || (ea.getKey() == 'W') || (ea.getKey() == osgGA::GUIEventAdapter::KEY_Up))
{
osg::Vec3d delta = osg::Vec3d(m_vStep * cosf(osg::PI_2 + m_vRotation._v[2]), m_vStep * sinf(osg::PI_2 + m_vRotation._v[2]), 0);
ChangePosition(delta);
return true;
}
else if ((ea.getKey() == 's') || (ea.getKey() == 'S') || (ea.getKey() == osgGA::GUIEventAdapter::KEY_Down))
{
osg::Vec3d delta = osg::Vec3d(-m_vStep * cosf(osg::PI_2 + m_vRotation._v[2]), -m_vStep * sinf(osg::PI_2 + m_vRotation._v[2]), 0);
ChangePosition(delta);
return true;
}
else if ((ea.getKey() == 'a') || (ea.getKey() == 'A'))
{
osg::Vec3d delta = osg::Vec3d(m_vStep * sinf(osg::PI_2 + m_vRotation._v[2]), m_vStep * cosf(osg::PI_2 + m_vRotation._v[2]), 0);
ChangePosition(delta);
return true;
}
else if ((ea.getKey() == 'd') || (ea.getKey() == 'D'))
{
osg::Vec3d delta = osg::Vec3d(-m_vStep * sinf(osg::PI_2 + m_vRotation._v[2]), -m_vStep * cosf(osg::PI_2 + m_vRotation._v[2]), 0);
ChangePosition(delta);
return true;
}
else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left)
{
m_vRotation[2] += 0.2;
return true;
}
else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right)
{
m_vRotation[2] -= 0.2;
return true;
}
else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Home)
{
m_vPosition += osg::Vec3d(0, 0, m_vStep);
//ChangePosition(osg::Vec3d(0, 0, m_vStep));
return true;
}
else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_End)
{
m_vPosition += osg::Vec3d(0, 0, -m_vStep);
//ChangePosition(osg::Vec3d(0, 0, -m_vStep));
return true;
}
}
break;
case osgGA::GUIEventAdapter::PUSH: // 鼠标按下
{
if (ea.getButton() == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON)
{
m_iLeftX = ea.getX();
m_iLeftY = ea.getY();
m_bLeftDown = true;
}
return false;
}
break;
case osgGA::GUIEventAdapter::DRAG: // 鼠标拖动
{
if (m_bLeftDown)
{
int delX = ea.getX() - m_iLeftX;
m_iLeftX = ea.getX();
double dx = osg::DegreesToRadians(0.01 * delX);
m_vRotation[2] += dx;
int delY = ea.getY() - m_iLeftY;
m_iLeftY = ea.getY();
m_vRotation[0] -= osg::DegreesToRadians(0.012 * delY);
if (m_vRotation[0] > osg::PI)
{
m_vRotation[0] = osg::PI;
}
else if (m_vRotation[0] < 0)
{
m_vRotation[0] = 0;
}
}
}
break;
case osgGA::GUIEventAdapter::RELEASE: // 鼠标释放
{
if (ea.getButton() == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON)
{
m_bLeftDown = false;
}
}
break;
default:
break;
}
return false;
}
void TravelManipulator::setStep(int iStep)
{
m_vStep = iStep;
}
int TravelManipulator::getStep() const
{
return m_vStep;
}
void TravelManipulator::setPosition(osg::Vec3d pos)
{
m_vPosition = pos;
}
osg::Vec3d TravelManipulator::getPosition()
{
return m_vPosition;
}
void TravelManipulator::setNode(osg::Node* node)
{
if (!m_bAuto)
{
m_node = node;
}
}
void TravelManipulator::setAuto(bool bauto)
{
m_bAuto = bauto;
}
void TravelManipulator::ChangePosition(const osg::Vec3d& delta)
{
// 源点m_vPosition与目的点 new pos;
osg::Vec3d newPos = m_vPosition + delta;
osg::Vec3d start = osg::Vec3d(newPos.x(), newPos.y(), newPos.z() + 2);// z轴高度设置为移动后高度1米
osg::Vec3d end = osg::Vec3d(newPos.x(), newPos.y(), newPos.z() - 1000);
osg::Vec3d start1 = osg::Vec3d(m_vPosition.x(), m_vPosition.y(), m_vPosition.z() + 2);
osg::Vec3d end1 = osg::Vec3d(newPos.x(), newPos.y(), newPos.z() + 2);
osg::ref_ptr<osgUtil::IntersectionVisitor> iv = new osgUtil::IntersectionVisitor;
// 竖直直线
osg::ref_ptr<osgUtil::LineSegmentIntersector> ls = new osgUtil::LineSegmentIntersector(start, end);
// 水平直线
osg::ref_ptr<osgUtil::LineSegmentIntersector> ls2 = new osgUtil::LineSegmentIntersector(start1, end1);
// 直线组
osg::ref_ptr<osgUtil::IntersectorGroup> lg = new osgUtil::IntersectorGroup;
lg->addIntersector(ls.get());
lg->addIntersector(ls2.get());
// 存放交点集合
osgUtil::LineSegmentIntersector::Intersections intersections;
iv->setIntersector(lg.get());
m_node->accept(*(iv.get()));
long height = 0;
if (ls->containsIntersections() && (!ls2->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();
}
}
}
else {
//不许走
return;
}
m_vPosition += delta;
m_vPosition.set(osg::Vec3d(m_vPosition.x(), m_vPosition.y(), height+1));
if (NULL != m_nCurrentNode)
{
m_nCurrentNode->toPosition(m_vPosition);
}
}
main.cpp
#include "Travel.h"
int main()
{
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
// 获取WindowingSystemInterface实例
osg::ref_ptr<osg::GraphicsContext::WindowingSystemInterface> wsi = osg::GraphicsContext::getWindowingSystemInterface();
if (!wsi)
{
std::cerr << "无法获取窗口系统接口。" << std::endl;
return 1;
}
// 获取主显示器的屏幕设置
unsigned int screenNum = 0; // 主显示器的屏幕编号通常为0
osg::GraphicsContext::ScreenSettings settings;
wsi->getScreenSettings(screenNum, settings);
// 获取屏幕的宽度和高度
unsigned int width = settings.width;
unsigned int height = settings.height;
viewer->setUpViewInWindow(0, 0, width, height);
viewer->addEventHandler(new osgViewer::WindowSizeHandler);
osg::ref_ptr<NodeMatrix> nm = new NodeMatrix;
nm->addsChild(osgDB::readNodeFile("ceep.ive"));
viewer->setSceneData(nm.get());
// 加入操作器
osgGA::CameraManipulator* pCameraPlator = new TravelManipulator();
viewer->setCameraManipulator(pCameraPlator);
return viewer->run();
}