osg 中鼠标拾取线段的端点和中点
//NeartestPointNodeVisitor.h
#pragma once
#include <osg\Matrix>
#include <vector>
#include <osg\Node>
#include <osg\NodeVisitor>
#include <osg\Camera>
#include <osg\Vec3>
#include <osg\MatrixTransform>
#include <osg\BoundingSphere>
#include <osg\Group>
#include <osg\Array>
#include <osg\Geometry> /// 寻找离鼠标最近的点
class NearestPointNodeVisitor : public osg::NodeVisitor
{
public:
// 构造函数
NearestPointNodeVisitor(float x=, float y=, osg::Matrix m=osg::Matrix::identity(), bool b=false, float error=); // 构造函数。VPW矩阵通过摄像机计算
NearestPointNodeVisitor(float x, float y, osg::Camera* camera, bool b=false, float error=); // 鼠标屏幕坐标
void setMouseXY(float x, float y);
// 摄像机
void setVPWmatrix(osg::Camera* camera);
// VPWmatrix
void setVPWmatrix(osg::Matrix m);
void setPara(float x, float y, osg::Camera* camera, bool b);
// 是否拾取中点
void setComputeMidPoint(bool b);
void setFindFalse(); void apply( osg::Node& node ); void findNearestPoint(const std::vector<osg::Vec3> points, const osg::Matrix &m); void apply( osg::Geode& node ); bool getFind();
//以下两个函数必须配合getFind()函数使用
osg::Vec3 getAttachedPointWinCoord();
osg::Vec3 getAttachedPoint();
private:
/// 鼠标点击的窗口坐标
float mouseX, mouseY;
//视线与远近裁剪面的交点
osg::Vec3 nearPoint, farPoint;
/// 视图*投影*视口(窗口)矩阵的乘积矩阵
osg::Matrix VPWmatrix;
/// 吸附误差(单位为屏幕像素)
float attachError;
/// 被吸附的点(离鼠标最近的点)
osg::Vec3 attachedPoint;
/// 被吸附的点对应的窗口坐标
osg::Vec3 attachedPointWinCoord;
/// 被吸附点的深度值
float depth;
/// 是否找到可吸附点
bool find;
/// 每个geom中线段的中点
std::map< osg::Geometry*, std::vector<osg::Vec3> > geom_map_midPoint;
/// 是否吸附线段的中点
bool isMP;
};
//NeartestPointNodeVisitor.cpp
#include "NearestPointNodeVisitor.h"
#include <osg\Geode>
#include <osg\LineWidth> // 计算点到直线的距离
// 返回值:点C到直线AB的最近距离;nearestPoint是直线AB上距离点C最近的点
template <class T>
float MyPointToLine(const T &C, const T &A, const T &B, T &nearestPoint)
{
float minDistance=;
T ab = B-A;// 直线AB方向向量
T ac = C-A;
T n1 = ac^ab;//ac叉乘ab,得到平面ABC的法向量
T n2 = ab^n1;//ab叉乘n1,得到平行于平面ABC且垂直AB的向量
n2.normalize();//单位化
minDistance = ac*n2;//AC在n2方向上的投影
nearestPoint = C-n2*minDistance;
return minDistance;
}
// 求一个点到线段的最近距离。算法来自于网络。
// 返回值:点C到线段AB的最近距离;nearestPoint是线段AB上距离点C最近的点
template <class T>
float MyPointToLineSegment(const T &C, const T &A, const T &B, T &nearestPoint)
{
T ac = C-A;
T ab = B-A;//线段所在直线向量 float f = ac*ab;
if ( f< )//C点在AB上的投影位于A的左侧
{
nearestPoint = A;
return ac.length();
}
else if (f>ab*ab)//C点在AB上的投影位于B的右侧
{
nearestPoint = B;
return (B-C).length();
}
else//C点在AB上的投影位于线段AB内部
{
float abLen2 = ab.length2();//ab长度的平方
nearestPoint = A;
float epsilon = 0.000001f;
if ( abLen2 - 0.0f > epsilon )//ab长度不等于0,亦即A、B两点不重合
{
nearestPoint += ab*(f/abLen2);
}
return (nearestPoint-C).length();
}
} // 计算两条直线之间的距离
// E是直线AB上一点,F是直线CD上一点,线段EF是直线AB到直线CD距离最短的线段
// 返回值为线段EF的长度
template <class T>
float MyLineToLine(const T& A, const T &B, const T &C, const T &D, T &E, T &F)
{
// 设CD在过AB且平行CD的平面π上的投影为C'D',M、N为CD上两点,P、Q为C'D'上两点,
// AM⊥CD,AP⊥C'D',BN⊥CD,BQ⊥C'D',可以证明P、Q分别为M、N在π上的投影
// 并且有△AEP∽△BEQ,所以AE/EB=AP/BQ,计算出AP和BQ的长度即可
float minDistance=;
T ab = B-A;
T cd = D-C;
T n = ab^cd;//叉乘。直线ab和cd公共垂直的直线方向向量
float epsilon = 1e-;
if (n.length()>epsilon)//ab和cd不平行
{
n.normalize();//单位化n
minDistance = n*(C-A);
}
return minDistance;
} // 计算两条线段之间的距离
// E是线段AB上一点,F是线段CD上一点,线段EF是线段AB到线段CD距离最短的线段
// 返回值为线段EF的长度
template <class T>
float MyLineSegmentToLineSegment(const T& A, const T &B, const T &C, const T &D, T &E, T &F)
{
// 设CD在过AB且平行CD的平面上的投影为C'D',
float minDistance=;
T ab = B-A;
T cd = D-C;
T n = ab^cd;//叉乘。直线ab和cd公共垂直的直线方向向量
float epsilon = 1e-;
if (n.length()>epsilon)//ab和cd不平行
{
n.normalize();//单位化n
//minDistance = n*(C-A)
}
return minDistance;
} NearestPointNodeVisitor::NearestPointNodeVisitor(float x/*=0*/, float y/*=0*/, osg::Matrix m/*=osg::Matrix::identity()*/, bool b/*=false*/, float error/*=5*/) :
NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
mouseX(x),
mouseY(y),
VPWmatrix(m),
attachError(error),//吸附误差
depth(),//默认在远裁剪面
find(false),
isMP(b)//是否吸附线段中点
{ } // 构造函数。VPW矩阵通过摄像机计算
NearestPointNodeVisitor::NearestPointNodeVisitor(float x, float y, osg::Camera* camera, bool b/*=false*/, float error/*=5*/) :
NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
mouseX(x),
mouseY(y),
attachError(error),//吸附误差
depth(),//默认在远裁剪面
find(false),
isMP(b)//是否吸附线段中点
{
osg::Matrix viewMatrix = camera->getViewMatrix();
osg::Matrix projectionMatrix = camera->getProjectionMatrix();
osg::Matrix windowMatrix = camera->getViewport()->computeWindowMatrix();
VPWmatrix = viewMatrix*projectionMatrix*windowMatrix; //以下语句测试证明两种方法都能正确的计算视线和远近裁剪面的交点
//osgManipulator::PointerInfo pi;
//pi.setCamera(camera);
//pi.setMousePosition(x,y);
//osg::Vec3d nearP,farP;
//pi.getNearFarPoints(nearP,farP);
//osg::Vec3 n,f;
//n = osg::Vec3(x,y,0)*osg::Matrix::inverse(VPWmatrix);
//f = osg::Vec3(x,y,1)*osg::Matrix::inverse(VPWmatrix); nearPoint = osg::Vec3(mouseX,mouseY,0.0f)*osg::Matrix::inverse(VPWmatrix);
farPoint = osg::Vec3(mouseX,mouseY,1.0f)*osg::Matrix::inverse(VPWmatrix);
} // 鼠标屏幕坐标
void NearestPointNodeVisitor::setMouseXY(float x, float y)
{
mouseX=x; mouseY=y;
nearPoint = osg::Vec3(mouseX,mouseY,0.0f)*osg::Matrix::inverse(VPWmatrix);
farPoint = osg::Vec3(mouseX,mouseY,1.0f)*osg::Matrix::inverse(VPWmatrix);
}
// 摄像机
void NearestPointNodeVisitor::setVPWmatrix(osg::Camera* camera)
{
osg::Matrix viewMatrix = camera->getViewMatrix();
osg::Matrix projectionMatrix = camera->getProjectionMatrix();
osg::Matrix windowMatrix = camera->getViewport()->computeWindowMatrix();
VPWmatrix = viewMatrix*projectionMatrix*windowMatrix;
nearPoint = osg::Vec3(mouseX,mouseY,0.0f)*osg::Matrix::inverse(VPWmatrix);
farPoint = osg::Vec3(mouseX,mouseY,1.0f)*osg::Matrix::inverse(VPWmatrix);
}
// VPWmatrix
void NearestPointNodeVisitor::setVPWmatrix(osg::Matrix m)
{
VPWmatrix=m;
nearPoint = osg::Vec3(mouseX,mouseY,0.0f)*osg::Matrix::inverse(VPWmatrix);
farPoint = osg::Vec3(mouseX,mouseY,1.0f)*osg::Matrix::inverse(VPWmatrix);
}
void NearestPointNodeVisitor::setPara(float x, float y, osg::Camera* camera, bool b)
{
setMouseXY(x,y);
setVPWmatrix(camera);
nearPoint = osg::Vec3(mouseX,mouseY,0.0f)*osg::Matrix::inverse(VPWmatrix);
farPoint = osg::Vec3(mouseX,mouseY,1.0f)*osg::Matrix::inverse(VPWmatrix);
isMP=b;
}
// 是否拾取中点
void NearestPointNodeVisitor::setComputeMidPoint(bool b) { isMP=b;}
void NearestPointNodeVisitor::setFindFalse() { find=false;} void NearestPointNodeVisitor::apply( osg::Node& node )
{
if ( find )
{
return;
}
osg::NodePath np(getNodePath().begin(),getNodePath().end()-);// 除去自身
osg::Matrix m = osg::computeLocalToWorld(np);//当前节点所在的世界变换矩阵
const osg::BoundingSphere &bs = node.getBound();
osg::Vec3 center = bs.center();
float radius = bs.radius();
center=center*m;//转换成世界坐标
float distance = MyPointToLine(center,nearPoint,farPoint,osg::Vec3());
/// 检测模型包围球球心到视线的距离是否小于包围球半径,这样能减少计算量
/// 同时还能保证很高的正确率(但不能达到100%,例如从球体外侧靠近球面上的点,
/// 此时distance>radius,但是有可能球面上的点在拾取范围之内)
if (distance<radius)
{
traverse( node );
}
} void NearestPointNodeVisitor::findNearestPoint(const std::vector<osg::Vec3> points, const osg::Matrix &m)
{
for ( unsigned int i=; i<points.size(); ++i )
{
osg::Vec3 currentPoint = points.at(i)*m;//转换成世界坐标
osg::Vec3 winCoord = currentPoint*VPWmatrix;//窗口坐标
if (/*winCoord.z()<depth&&*/(winCoord-osg::Vec3(mouseX,mouseY,)).length()<attachError)
{
depth = winCoord.z();
attachedPoint = currentPoint;
attachedPointWinCoord = winCoord;
find=true;
break;// 找到第一个就算结束了
}
}
} void NearestPointNodeVisitor::apply( osg::Geode& node )
{
// 检测geode的包围球是否被射线穿过
osg::Matrix m = osg::computeLocalToWorld(getNodePath());//当前叶子节点对应的世界变换矩阵
const osg::BoundingSphere &bs = node.getBound();
osg::Vec3 center = bs.center();
center=center*m;//转换成世界坐标
float radius = bs.radius();
float distance = MyPointToLine(center,nearPoint,farPoint,osg::Vec3());
if (distance>radius)
{
return;
} osg::Geode::DrawableList drawableList = node.getDrawableList();
osg::Geode::DrawableList::iterator it = drawableList.begin();
for ( ; it != drawableList.end(); ++it)
{
osg::Geometry* geom = it->get()->asGeometry();
if ( !geom )
{
continue;
}
// 检测geom的包围盒是否被射线穿过
const osg::BoundingBox &bs = geom->getBound();
osg::Vec3 center = bs.center();
center=center*m;//转换成世界坐标
float radius = bs.radius();
float distance = MyPointToLine(center,nearPoint,farPoint,osg::Vec3());
// 如果射线没穿过geom,计算下一个geom
if (distance>radius)
{
continue;
} // 遍历geom的全部点
osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geom->getVertexArray());
findNearestPoint(vertices->asVector(), m);
// 拾取中点
if ( isMP )
{
std::map<osg::Geometry*, std::vector<osg::Vec3>>::iterator itr = geom_map_midPoint.find(geom);
// geom的中点集合还没计算
if ( itr == geom_map_midPoint.end() )
{
//osg::TemplatePrimitiveFunctor<MidPointOfGeom> midPoint;
//geom->accept(midPoint);
//geom_map_midPoint.insert(std::map<osg::Geometry*, std::vector<osg::Vec3>>::value_type
// (geom, midPoint.vertex));
osg::Geometry::PrimitiveSetList psList = geom->getPrimitiveSetList();
osg::Geometry::PrimitiveSetList::iterator psListItr = psList.begin();
std::vector<osg::Vec3> midPoints;
for ( ; psListItr != psList.end(); psListItr++ )
{
osg::PrimitiveSet *ps = psListItr->get();
GLenum mode = ps->getMode();
switch( ps->getType() )
{
case osg::PrimitiveSet::DrawArraysPrimitiveType:
{
switch( mode )
{
case osg::PrimitiveSet::LINES:
{
break;
}
case osg::PrimitiveSet::LINE_STRIP:
{
break;
}
case osg::PrimitiveSet::LINE_LOOP:
{
break;
}
case osg::PrimitiveSet::TRIANGLES:
{
break;
}
case osg::PrimitiveSet::TRIANGLE_STRIP:
{
break;
}
case osg::PrimitiveSet::TRIANGLE_FAN:
{
break;
}
case osg::PrimitiveSet::QUADS:
{
break;
}
}
break;
}
case osg::PrimitiveSet::DrawElementsUBytePrimitiveType:
{
break;
}
case osg::PrimitiveSet::DrawElementsUShortPrimitiveType:
{
osg::DrawElementsUShort* deus = dynamic_cast<osg::DrawElementsUShort*>(ps);
const unsigned indexNum = deus->getNumIndices();
switch( mode )
{
case osg::PrimitiveSet::LINES:
{
break;
}
case osg::PrimitiveSet::LINE_STRIP:
{
break;
}
case osg::PrimitiveSet::LINE_LOOP:
{
break;
}
case osg::PrimitiveSet::TRIANGLES:
{
typedef std::map< unsigned int, std::set<unsigned int> > INT_SET;
INT_SET start_end;
for (unsigned int i=; i<indexNum; i+=) {
// 对三个索引排序,最终a<=b<=c
unsigned short a=osg::minimum(deus->at(i),osg::minimum(deus->at(i+),deus->at(i+)));
unsigned short c=osg::maximum(deus->at(i),osg::maximum(deus->at(i+),deus->at(i+)));
unsigned short b=(deus->at(i)+deus->at(i+)+deus->at(i+)-a-c);
//查找(a,b),(a,c)是否已经计算过
INT_SET::iterator itr = start_end.find(a);
if ( itr==start_end.end() )//没有a组
{
std::set<unsigned int> aset;
aset.insert(b);
aset.insert(c);
start_end.insert(make_pair(a,aset));
// 计算中点
midPoints.push_back((vertices->at(a)+vertices->at(b))/);
midPoints.push_back((vertices->at(a)+vertices->at(c))/);
}
else
{
if (itr->second.count(b)==)//有a组但没有(a,b)对
{
start_end[a].insert(b);
midPoints.push_back((vertices->at(a)+vertices->at(b))/);
}
if (itr->second.count(c)==)//有a组但没有(a,c)对
{
start_end[a].insert(c);
midPoints.push_back((vertices->at(a)+vertices->at(c))/);
}
}
//查找(b,c)是否已经计算过
itr = start_end.find(b);
if ( itr==start_end.end() )//没有b组
{
std::set<unsigned int> bset;
bset.insert(c);
start_end.insert(make_pair(b,bset));
midPoints.push_back((vertices->at(b)+vertices->at(c))/);
}
else
{
if (itr->second.count(c)==)//有b组但没有(b,c)对
{
start_end[b].insert(c);
midPoints.push_back((vertices->at(b)+vertices->at(c))/);
}
}
}
break;
}
case osg::PrimitiveSet::TRIANGLE_STRIP:
{
break;
}
case osg::PrimitiveSet::TRIANGLE_FAN:
{
break;
}
case osg::PrimitiveSet::QUADS:
{
break;
}
}
break;
}
case osg::PrimitiveSet::DrawElementsUIntPrimitiveType:
{
break;
}
}// END ps->getType()
}//END for psList
geom_map_midPoint.insert(make_pair(geom, midPoints));
findNearestPoint(midPoints, m);
}//END if ( itr == geom_map_midPoint.end() )
else
{
findNearestPoint(itr->second, m);
}
} // END if ( isMP )
}// for ( ; it != drawableList.end(); ++it)
} bool NearestPointNodeVisitor::getFind()
{
return find;
}
//以下两个函数必须配合getFind()函数使用
osg::Vec3 NearestPointNodeVisitor::getAttachedPointWinCoord()
{
return attachedPointWinCoord;
}
osg::Vec3 NearestPointNodeVisitor::getAttachedPoint()
{
return attachedPoint;
}
osg 中鼠标拾取线段的端点和中点的更多相关文章
- OSG中的示例程序简介
OSG中的示例程序简介 转自:http://www.cnblogs.com/indif/archive/2011/05/13/2045136.html 1.example_osganimate一)演示 ...
- OSG中的示例程序简介(转)
OSG中的示例程序简介 1.example_osganimate一)演示了路径动画的使用 (AnimationPath.AnimationPathCallback),路径动画回调可以作用在Camera ...
- CSharpGL(21)用鼠标拾取、拖拽VBO图元内的点、线或本身
CSharpGL(21)用鼠标拾取.拖拽VBO图元内的点.线或本身 效果图 以最常见的三角形网格(用GL_TRIANGLES方式进行渲染)为例. 在拾取模式为GeometryType.Point时,你 ...
- osg中遇到的问题
osg中遇到的问题 今天写程序的时候, 需要把键盘和鼠标消息转发出来, 就直接写了接口用signal丢出来了. 程序写的很多, 测试的时候却崩溃了.... 在场景中拖拽鼠标左键的时候, 会发现在扔出鼠 ...
- osg项目经验1<MFC+OSG中模型点选效果>
点选主要是重载osg的GUIEventHandler, class CPickHandler : public osgGA::GUIEventHandler{ //自定义回调函数名:CPickHand ...
- canvas学习总结五:线段的端点与连接点
我们在第三节中描述了线段的绘制,其中线段的属性lineWidth是用来改变线段的宽度.让我们来回忆下线宽的用法 function drawLine(){ cxt.lineWidth = 3; cxt. ...
- DirectX11 With Windows SDK--21 鼠标拾取
前言 拾取是一项非常重要的技术,不论是电脑上用鼠标操作,还是手机的触屏操作,只要涉及到UI控件的选取则必然要用到该项技术.除此之外,一些类似魔兽争霸3.星际争霸2这样的3D即时战略游戏也需要通过拾取技 ...
- Qt OpenGL 鼠标拾取实现
在之前的文章中讲到了OpenGL鼠标拾取操作的例子,工作中需要在Qt中实现,下面的程序演示了QT中opengl的拾取例子. 本例子在Qt5.12和Qt Creator4.8.0上测试,使用的是QOpe ...
- OpenGL中的拾取模式( Picking)
1. Opengl中的渲染模式有三种:(1)渲染模式,默认的模式:(2)选择模式, (3)反馈模式.如下 GLint glRenderMode(GLenum mode) mode可以选取以下三种模式之 ...
随机推荐
- Windows Server 2008系统如何取消登录时要按Ctrl+Alt+Delete组合键
1.点桌面任务栏的“开始-->运行”在弹出的窗口中输入gpedit.msc . 2.输入gpedit.msc后,点击确定即打开了组策略编辑器.在组策略编辑器的左框内依次序展开(点前面的“+”号) ...
- java中的不为空判断
String不为空判断 if(null != str && !"".equals(str)) List不为空判断 if(list!=null && ...
- jsp学习(三)
<%@page contentType="text/html;charset=gbk"%> <html> <body> <font siz ...
- Put-Me-Down项目Postmortem
设想和目标 PMD是一款帮助低头族控制使用手机时间的APP,设想按照需求规格说明书内容实现功能,能将数据备份到服务器. 计划 初始计划我们是想将程序方面分为安卓和后台,主要是程序方面的工作.我们对项目 ...
- Linux的学习路线图
一.学习Linux的基本要求1. 掌握至少50个以上的常用命令. 2. 熟悉Gnome/KDE等X-windows桌面环境操作 . 3. 掌握.tgz..rpm等软件包的常用安装方法 4. 学习添加外 ...
- javascript的字符串模板
在其他语言存在字符串内插(string interpolation)或者叫变量内插(Variable interpolation).ES6中的称为template string. 模板字符串使用反引号 ...
- SCI完全攻略:从构思到发表
- --hdu 1050 Moving Tables(贪心)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1050 AC code: #include<stdio.h> #include<str ...
- ping 和 traceroute 命令
ping 和 traceroute 命令 ping 程序 就是发送一个ICMP查询报文给某服务器,以测试该服务器是否可达. 当返回ICMP回显应答时,要打印出序列号.TTL,和往返时间: [roo ...
- 网站性能Web压力测试工具webbench
webbench最多可以模拟3万个并发连接去测试网站的负载能力,个人感觉要比Apache自带的ab压力测试工具好,安装使用也特别方便. 1.适用系统:Linux 2.编译安装: wget http:/ ...