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可以选取以下三种模式之 ...
随机推荐
- 802.11协议帧格式、Wi-Fi连接交互过程、无线破解入门研究
相关学习资料 Linux黑客大曝光: 第8章 无线网络 无线网络安全攻防实战进阶 无线网络安全 黑客大曝光 第2版 http://zh.wikipedia.org/wiki/IEEE_802.11 h ...
- BUAA1389愤怒的DZY(最大值最小化)
http://acm.buaa.edu.cn/problem/1389/ 愤怒的DZY[问题描述]“愤怒的小鸟”如今已经是家喻户晓的游戏了,机智的WJC最近发明了一个类似的新游戏:“愤怒的DZY”.游 ...
- eclipse中使用git
有的eclipse已经自带了Git了,就不用安装了.如果,想重新安装,可以先卸载GIT,卸载 不同eclipse卸载不一样: 1.在Eclipse中依次点击菜单"Help"-> ...
- PHP实现发红包程序(helloweba网站经典小案例)
我们先来分析下规律. 设定总金额为10元,有N个人随机领取: N=1 第一个 则红包金额=X元: N=2 第二个 为保证第二个红包可以正常发出,第一个红包金额=0.01至9.99之间的某个随机数. 第 ...
- html标签(一)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 锋利的jQuery-3--.css()获取和设置元素的数字属性
$('p').css({"fontSize": "30px", "backgroundColor": "#666"}); ...
- title及alt提示特效
<html> <head> <title>title及alt提示特效</title> <style type="text/css&quo ...
- 动态插入、添加删除表格行的JS代码
<html> <head> <title>Table对象的方法</title> <script language="JavaScript ...
- sed 指令
sed -e 's/:/ /g' 将待处理文本行中:替换为空格, s/A/B/g 是sed中的替换命令, 将A替换为B, 其中,A可以是正则表达式. g表示全部替换. sed 指令 瀏覽數 : 6,5 ...
- wordPress Development
site:http://codex.wordpress.org/Theme_Development 2014-03-24 This article is about developing WordPr ...