计算用箭头和鼠标移动的对象
我正在用 openGL 做一个 Maya 模拟器应用程序,一切进展顺利.只有一个麻烦,我无法通过鼠标计算移动对象的 100% 准确度.我正在使用 3D 坐标中的箭头和屏幕坐标中的箭头的缩放比例,并将其与一些缩放比例相乘以计算屏幕坐标 x 和 y 方向上的运动程度..但是这些计算并没有给出很高的准确度,鼠标偏离了箭头.我需要它总是在移动的箭头上,这意味着准确度几乎是 100%任何对此问题有想法或意见的人将永远不胜感激
这是我尝试的一些代码:
gluProject((GLdouble)x3DVertex, (GLdouble)y3DVertex, (GLdouble)z3DVertex, modelMatrix, projectMatrix, viewport, &xScreenVertex, &yScreenVertex, &zScreenVertex);如果(轴 == 0){gluProject((GLdouble)x3DVertex + 1, (GLdouble)y3DVertex, (GLdouble)z3DVertex, modelMatrix, projectMatrix, viewport, &xScreenArrow, &yScreenArrow, &zScreenArrow);}否则如果(轴 == 1){gluProject((GLdouble)x3DVertex, (GLdouble)y3DVertex + 1, (GLdouble)z3DVertex, modelMatrix, projectMatrix, viewport, &xScreenArrow, &yScreenArrow, &zScreenArrow);}别的{gluProject((GLdouble)x3DVertex, (GLdouble)y3DVertex, (GLdouble)z3DVertex + 1, modelMatrix, projectMatrix, viewport, &xScreenArrow, &yScreenArrow, &zScreenArrow);}float totalScaleXY = abs(xScreenArrow - xScreenVertex) + abs(yScreenArrow - yScreenVertex);scaleX = abs(xScreenArrow - xScreenVertex)/totalScaleXY;scaleY = abs(yScreenArrow - yScreenVertex)/totalScaleXY;float lengthArrowOnScreen = sqrt(pow((xScreenArrow - xScreenVertex), 2) + pow((yScreenArrow - yScreenVertex), 2));scale3dAndScreen = 1/lengthArrowOnScreen;
这是我正在测试的代码,不是很准确
解决方案这里有一个丑陋但非常简单的 C++ 示例,说明我对此的想法:
//---------------------------------------------------------------------#include #include <math.h>#pragma hdrstop#include "Unit1.h"#include "gl_simple.h"//---------------------------------------------------------------------------#pragma 包(smart_init)#pragma 资源*.dfm"TForm1 *Form1;//---------------------------------------------------------------------------双 mview[16],mmodel[16],mproj[16];//OpenGL矩阵//---------------------------------------------------------------------------//向量/矩阵数学//---------------------------------------------------------------------------double vector_len(double *a) { return sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2]));}//= |向量[3]|void vector_sub(double *c,double *a,double *b) { for (int i=0;i<3;i++) c[i]=a[i]-b[i];}//c[3] = a[3] - b[3]void matrix_mul(double *c,double *a,double *b)//c[4x4] = a[4x4] * b [4x4]{双 q[16];q[0]=(a[0]*b[0])+(a[1]*b[4])+(a[2]*b[8])+(a[3]*b[12]]);q[1]=(a[0]*b[1])+(a[1]*b[5])+(a[2]*b[9])+(a[3]*b[13]]);q[2]=(a[0]*b[2])+(a[1]*b[6])+(a[2]*b[10])+(a[3]*b[14]]);q[3]=(a[0]*b[3])+(a[1]*b[7])+(a[2]*b[11])+(a[3]*b[15]]);q[4]=(a[4]*b[0])+(a[5]*b[4])+(a[6]*b[8])+(a[7]*b[12]]);q[5]=(a[4]*b[1])+(a[5]*b[5])+(a[6]*b[9])+(a[7]*b[13]]);q[6]=(a[4]*b[2])+(a[5]*b[6])+(a[6]*b[10])+(a[7]*b[14]]);q[7]=(a[4]*b[3])+(a[5]*b[7])+(a[6]*b[11])+(a[7]*b[15]]);q[8]=(a[8]*b[0])+(a[9]*b[4])+(a[10]*b[8])+(a[11]*b[12]]);q[9]=(a[8]*b[1])+(a[9]*b[5])+(a[10]*b[9])+(a[11]*b[13]]);q[10]=(a[8]*b[2])+(a[9]*b[6])+(a[10]*b[10])+(a[11]*b[14]]);q[11]=(a[8]*b[3])+(a[9]*b[7])+(a[10]*b[11])+(a[11]*b[15]]);q[12]=(a[12]*b[0])+(a[13]*b[4])+(a[14]*b[8])+(a[15]*b[12]]);q[13]=(a[12]*b[1])+(a[13]*b[5])+(a[14]*b[9])+(a[15]*b[13]]);q[14]=(a[12]*b[2])+(a[13]*b[6])+(a[14]*b[10])+(a[15]*b[14]]);q[15]=(a[12]*b[3])+(a[13]*b[7])+(a[14]*b[11])+(a[15]*b[15]]);for(int i=0;i<16;i++) c[i]=q[i];}void matrix_mul_vector(double *c,double *a,double *b)//c[3] = a[4x4] * (b[3],1){双 q[3];q[0]=(a[0]*b[0])+(a[4]*b[1])+(a[8]*b[2])+(a[12]);q[1]=(a[1]*b[0])+(a[5]*b[1])+(a[9]*b[2])+(a[13]);q[2]=(a[2]*b[0])+(a[6]*b[1])+(a[10]*b[2])+(a[14]);for(int i=0;i<3;i++) c[i]=q[i];}//---------------------------------------------------------------------------class DragDrop3D//箭头平移控件(每个对象都需要一个){上市:双*毫米;//模型矩阵双*mv;//查看矩阵双*mp;//投影矩阵双o[3];//起点双 l[3];//箭头的长度双 a, b;//箭头的大小 a 长 b 宽/2内部选择;//选择的轴DragDrop3D(){毫米=空;mv=空;mp=空;o[0]=-1.0;l[0]=3.5;a=0.5;o[1]=-1.0;l[1]=3.5;b=0.25;o[2]=-1.0;l[2]=3.5;sel=-1;}DragDrop3D(DragDrop3D& a) { *this=a;}~DragDrop3D() {}DragDrop3D* operator = (const DragDrop3D *a) { *this=*a;返回这个;}//DragDrop3D* operator = (const DragDrop3D &a) { ...copy... return this;}void project(double *scr,double *mmvp,double *obj)//obj ->可控硅{matrix_mul_vector(scr,mmvp,obj);如果 (fabs(scr[2])>1e-10) scr[2]=1.0/scr[2];否则 scr[2]=0.0;scr[0]*=scr[2];scr[1]*=scr[2];}void draw()//渲染箭头{国际;双角,当=2.0*M_PI/36.0,u0,u1,v0,v1,q=1.0/sqrt((a*a)+(b*b));//轴线glBegin(GL_LINES);glColor3f(1.0,0.0,0.0);glVertex3dv(o);glVertex3d(o[0]+l[0],o[1],o[2]);glColor3f(0.0,1.0,0.0);glVertex3dv(o);glVertex3d(o[0],o[1]+l[1],o[2]);glColor3f(0.0,0.0,1.0);glVertex3dv(o);glVertex3d(o[0],o[1],o[2]+l[2]);glEnd();//锥体glBegin(GL_TRIANGLES);u1=b*cos(-dang);v1=b*sin(-dang);对于 (e=1,ang=0.0;e;ang+=dang){如果 (ang>=2.0*M_PI) { ang=2.0*M_PI;e=0;}u0=u1;u1=b*cos(ang);v0=v1;v1=b*sin(ang);//Xif (sel==0) glColor3f(1.0,0.0,0.0);否则 glColor3f(0.5,0.1,0.1);glNormal3f(a*q,(u0+u1)*0.5*q,(v0+v1)*0.5*q);glVertex3d(o[0]+l[0],o[1],o[2]);glVertex3d(o[0]+l[0]-a,o[1]+u0,o[2]+v0);glVertex3d(o[0]+l[0]-a,o[1]+u1,o[2]+v1);glNormal3f(-1.0,0.0,0.0);glVertex3d(o[0]+l[0]-a,o[1],o[2]);glVertex3d(o[0]+l[0]-a,o[1]+u1,o[2]+v1);glVertex3d(o[0]+l[0]-a,o[1]+u0,o[2]+v0);//是if (sel==1) glColor3f(0.0,1.0,0.0);否则 glColor3f(0.1,0.5,0.1);glNormal3f((u0+u1)*0.5*q,a*q,(v0+v1)*0.5*q);glVertex3d(o[0],o[1]+l[1],o[2]);glVertex3d(o[0]+u1,o[1]+l[1]-a,o[2]+v1);glVertex3d(o[0]+u0,o[1]+l[1]-a,o[2]+v0);glNormal3f(0.0,-1.0,0.0);glVertex3d(o[0],o[1]+l[1]-a,o[2]);glVertex3d(o[0]+u0,o[1]+l[1]-a,o[2]+v0);glVertex3d(o[0]+u1,o[1]+l[1]-a,o[2]+v1);//Zif (sel==2) glColor3f(0.0,0.0,1.0);否则 glColor3f(0.1,0.1,0.5);glNormal3f((v0+v1)*0.5*q,(u0+u1)*0.5*q,a*q);glVertex3d(o[0],o[1],o[2]+l[2]);glVertex3d(o[0]+v1,o[1]+u1,o[2]+l[2]-a);glVertex3d(o[0]+v0,o[1]+u0,o[2]+l[2]-a);glNormal3f(0.0,0.0,-1.0);glVertex3d(o[0] ,o[1] ,o[2]+l[2]-a);glVertex3d(o[0]+v0,o[1]+u0,o[2]+l[2]-a);glVertex3d(o[0]+v1,o[1]+u1,o[2]+l[2]-a);}glEnd();}bool mouse(double mx,double my,TShiftState sh)//如果需要重绘则处理鼠标事件返回{if ((mm==NULL)||(mv==NULL)||(mp==NULL)) 返回假;国际ql;双 p[3],mmvp[16];布尔_重绘=假;//MVP = M*V*Pmatrix_mul(mmvp,mm,mv);matrix_mul(mmvp,mmvp,mp);//将屏幕坐标转换为<-1,+1>GL 国家数据中心mx= (2.0*mx/double(xs))-1.0;my=1.0-(2.0*my/double(ys)) ;//鼠标左键状态 (last,actual)ql=sh.Contains(ssLeft);//选择(无鼠标按钮)如果(!ql){int sel0=sel;sel=-1;//箭头中心屏幕x,y距离鼠标选择如果关闭p[0]=o[0]+l[0]-0.5*a;p[1]=o[1];p[2]=o[2];项目(p,mmvp,p);p[0]-=mx;p[1]-=我的;p[2]=0.0;如果 (vector_len(p)<=0.5*b) sel=0;//Xp[1]=o[1]+l[1]-0.5*a;p[0]=o[0];p[2]=o[2];项目(p,mmvp,p);p[0]-=mx;p[1]-=我的;p[2]=0.0;如果 (vector_len(p)<=0.5*b) sel=1;//是p[2]=o[2]+l[2]-0.5*a;p[1]=o[1];p[0]=o[0];项目(p,mmvp,p);p[0]-=mx;p[1]-=我的;p[2]=0.0;如果 (vector_len(p)<=0.5*b) sel=2;//Z_redraw=(sel0!=sel);}//将箭头中心拖动到鼠标位置(活动按钮)如果 ((ql)&&(sel>=0)&&(sel<3)){国际我;双len0,len;双 p0[3],dp[3]={0.0,0.0,0.0},t,dt,q[3]={mx,my,0.0};//选定的箭头位置、方向对于 (i=0;i<3;i++) p0[i]=o[i];p0[sel]+=l[sel]-0.5*a;dp[sel]=1.0;//轴/鼠标_射线之间的最近"交点对于 (len0=-1.0,t=0.0,dt=1.0;fabs(dt)>1e-5;t+=dt){//轴上的位置 p(t) = p0 + t*dp对于 (i=0;i<3;i++) p[i]=p0[i]+(t*dp[i]);//len = 到鼠标的距离项目(p,mmvp,p);vector_sub(p,p,q);p[2]=0.0;len=vector_len(p);//处理迭代步骤如果 (len0<-0.5) len0=len;如果 (len>len0) dt=-0.1*dt;len0=len;}//由 t 翻译双 m[16]={1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,};m[12+sel]=t;matrix_mul(mm,m,mm);_重绘=真;}返回_重绘;}};//---------------------------------------------------------------------------DragDrop3D ctrl;//我得到了一个立方体,所以单箭头拖放控制就足够了//---------------------------------------------------------------------------void gl_draw()//主要渲染代码{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glEnable(GL_CULL_FACE);glEnable(GL_LIGHTING);glEnable(GL_LIGHT0);glEnable(GL_COLOR_MATERIAL);//在里面静态布尔_init =真;如果(_init){_init=假;//M,V 初始化矩阵glMatrixMode(GL_MODELVIEW);glPushMatrix();glLoadIdentity();glTranslated(0.0,0.0,-25.0);glGetDoublev(GL_MODELVIEW_MATRIX,mview);glLoadIdentity();glTranslated(-2.0,-1.0,-1.0);glRotated(-35.0,0.0,1.0,0.0);glGetDoublev(GL_MODELVIEW_MATRIX,mmodel);glPopMatrix();//矩阵 ->控制glGetDoublev(GL_PROJECTION_MATRIX,mproj);ctrl.mp=mproj;ctrl.mv=mview;ctrl.mm=mmodel;}//矩阵 ->OpenGLglMatrixMode(GL_MODELVIEW);glLoadMatrixd(mview);glMultMatrixd(mmodel);//绘制 VAO 立方体ctrl.draw();vao_draw();//这里渲染你的对象glFlush();交换缓冲区(hdc);}//---------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner){//应用初始化gl_init(句柄);vao_init();}//---------------------------------------------------------------------------void __fastcall TForm1::FormDestroy(TObject *Sender){//应用程序退出gl_exit();vao_exit();}//---------------------------------------------------------------------------void __fastcall TForm1::FormResize(TObject *Sender){//调整窗口大小gl_resize(ClientWidth,ClientHeight);}//---------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender){//窗口重绘gl_draw();}//---------------------------------------------------------------------------void __fastcall TForm1::FormMouseWheel(TObject *Sender, TShiftState Shift, int WheelDelta, TPoint &MousePos, bool &Handled){//鼠标滚轮平移相机(如缩放)GLfloat dz=2.0;如果 (WheelDelta<0) dz=-dz;glMatrixMode(GL_MODELVIEW);glPushMatrix();glLoadMatrixd(mview);glTranslatef(0,0,dz);glGetDoublev(GL_MODELVIEW_MATRIX,mview);glPopMatrix();gl_draw();}//---------------------------------------------------------------------------//鼠标事件void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y) { if (ctrl.mouse(X,Y,Shift)) gl_draw();}void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y) { if (ctrl.mouse(X,Y,Shift)) gl_draw();}void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { if (ctrl.mouse(X,Y,Shift)) gl_draw();}//---------------------------------------------------------------------------
它基于此:
遗憾的是我的 GIF 捕获 SW 没有捕获鼠标光标,但它完全匹配移动.
我懒得直接计算轴之间的交集,而是迭代以找到最接近的匹配(那个 for
循环和 dt
).那部分可以用相交方程代替.
轴以对象坐标系 (mm,mmodel
) 的 x、y、z 轴为界.
对于更多的对象,您还应该添加锁,以便只能选择一个对象...
PS. 见相关:- OpenGL 射线 OBB 交集
I am doing a maya simulator application with openGL, everything is progressing pretty well. Just have only one trouble, i can not calculate 100% accuracy of moving object by mouse. I'm using the scaling of arrows in 3D coordinates and arrows in the screen coordinates and multiplying it with some scaling to calculate the degree of motion in the x and y direction of the screen coordinates. . But these calculations do not give high accuracy, the mouse is deviated from the arrow. I need it always on the moving arrows, meaning that the accuracy is almost 100% Anyone who has ideas or comments on this issue will always be appreciated
This is some code i try:
gluProject((GLdouble)x3DVertex, (GLdouble)y3DVertex, (GLdouble)z3DVertex, modelMatrix, projectMatrix, viewport, &xScreenVertex, &yScreenVertex, &zScreenVertex);
if (axis == 0)
{
gluProject((GLdouble)x3DVertex + 1, (GLdouble)y3DVertex, (GLdouble)z3DVertex, modelMatrix, projectMatrix, viewport, &xScreenArrow, &yScreenArrow, &zScreenArrow);
}
else if (axis == 1)
{
gluProject((GLdouble)x3DVertex, (GLdouble)y3DVertex + 1, (GLdouble)z3DVertex, modelMatrix, projectMatrix, viewport, &xScreenArrow, &yScreenArrow, &zScreenArrow);
}
else
{
gluProject((GLdouble)x3DVertex, (GLdouble)y3DVertex, (GLdouble)z3DVertex + 1, modelMatrix, projectMatrix, viewport, &xScreenArrow, &yScreenArrow, &zScreenArrow);
}
float totalScaleXY = abs(xScreenArrow - xScreenVertex) + abs(yScreenArrow - yScreenVertex);
scaleX = abs(xScreenArrow - xScreenVertex) / totalScaleXY;
scaleY = abs(yScreenArrow - yScreenVertex) / totalScaleXY;
float lengthArrowOnScreen = sqrt(pow((xScreenArrow - xScreenVertex), 2) + pow((yScreenArrow - yScreenVertex), 2));
scale3dAndScreen = 1 / lengthArrowOnScreen;
This is the code I'm testing, it's not very accurate
解决方案Here small ugly but very simple C++ example of what I had in mind for this:
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "Unit1.h"
#include "gl_simple.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
double mview[16],mmodel[16],mproj[16]; // OpenGL matrices
//---------------------------------------------------------------------------
// vector/matrix math
//---------------------------------------------------------------------------
double vector_len(double *a) { return sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2])); } // = |vector[3]|
void vector_sub(double *c,double *a,double *b) { for (int i=0;i<3;i++) c[i]=a[i]-b[i]; } // c[3] = a[3] - b[3]
void matrix_mul(double *c,double *a,double *b) // c[4x4] = a[4x4] * b [4x4]
{
double q[16];
q[ 0]=(a[ 0]*b[ 0])+(a[ 1]*b[ 4])+(a[ 2]*b[ 8])+(a[ 3]*b[12]);
q[ 1]=(a[ 0]*b[ 1])+(a[ 1]*b[ 5])+(a[ 2]*b[ 9])+(a[ 3]*b[13]);
q[ 2]=(a[ 0]*b[ 2])+(a[ 1]*b[ 6])+(a[ 2]*b[10])+(a[ 3]*b[14]);
q[ 3]=(a[ 0]*b[ 3])+(a[ 1]*b[ 7])+(a[ 2]*b[11])+(a[ 3]*b[15]);
q[ 4]=(a[ 4]*b[ 0])+(a[ 5]*b[ 4])+(a[ 6]*b[ 8])+(a[ 7]*b[12]);
q[ 5]=(a[ 4]*b[ 1])+(a[ 5]*b[ 5])+(a[ 6]*b[ 9])+(a[ 7]*b[13]);
q[ 6]=(a[ 4]*b[ 2])+(a[ 5]*b[ 6])+(a[ 6]*b[10])+(a[ 7]*b[14]);
q[ 7]=(a[ 4]*b[ 3])+(a[ 5]*b[ 7])+(a[ 6]*b[11])+(a[ 7]*b[15]);
q[ 8]=(a[ 8]*b[ 0])+(a[ 9]*b[ 4])+(a[10]*b[ 8])+(a[11]*b[12]);
q[ 9]=(a[ 8]*b[ 1])+(a[ 9]*b[ 5])+(a[10]*b[ 9])+(a[11]*b[13]);
q[10]=(a[ 8]*b[ 2])+(a[ 9]*b[ 6])+(a[10]*b[10])+(a[11]*b[14]);
q[11]=(a[ 8]*b[ 3])+(a[ 9]*b[ 7])+(a[10]*b[11])+(a[11]*b[15]);
q[12]=(a[12]*b[ 0])+(a[13]*b[ 4])+(a[14]*b[ 8])+(a[15]*b[12]);
q[13]=(a[12]*b[ 1])+(a[13]*b[ 5])+(a[14]*b[ 9])+(a[15]*b[13]);
q[14]=(a[12]*b[ 2])+(a[13]*b[ 6])+(a[14]*b[10])+(a[15]*b[14]);
q[15]=(a[12]*b[ 3])+(a[13]*b[ 7])+(a[14]*b[11])+(a[15]*b[15]);
for(int i=0;i<16;i++) c[i]=q[i];
}
void matrix_mul_vector(double *c,double *a,double *b) // c[3] = a[4x4] * (b[3],1)
{
double q[3];
q[0]=(a[ 0]*b[0])+(a[ 4]*b[1])+(a[ 8]*b[2])+(a[12]);
q[1]=(a[ 1]*b[0])+(a[ 5]*b[1])+(a[ 9]*b[2])+(a[13]);
q[2]=(a[ 2]*b[0])+(a[ 6]*b[1])+(a[10]*b[2])+(a[14]);
for(int i=0;i<3;i++) c[i]=q[i];
}
//---------------------------------------------------------------------------
class DragDrop3D // arrow translation controls (you need one for each objet)
{
public:
double *mm; // model matrix
double *mv; // view matrix
double *mp; // projection matrix
double o[3]; // start point
double l[3]; // length of the arrows
double a,b; // size of the arrow head a length, b width/2
int sel; // selected axis
DragDrop3D()
{
mm=NULL; mv=NULL; mp=NULL;
o[0]=-1.0; l[0]=3.5; a=0.5;
o[1]=-1.0; l[1]=3.5; b=0.25;
o[2]=-1.0; l[2]=3.5; sel=-1;
}
DragDrop3D(DragDrop3D& a) { *this=a; }
~DragDrop3D() {}
DragDrop3D* operator = (const DragDrop3D *a) { *this=*a; return this; }
//DragDrop3D* operator = (const DragDrop3D &a) { ...copy... return this; }
void project(double *scr,double *mmvp,double *obj) // obj -> scr
{
matrix_mul_vector(scr,mmvp,obj);
if (fabs(scr[2])>1e-10) scr[2]=1.0/scr[2]; else scr[2]=0.0;
scr[0]*=scr[2];
scr[1]*=scr[2];
}
void draw() // render arrows
{
int e;
double ang,dang=2.0*M_PI/36.0,u0,u1,v0,v1,q=1.0/sqrt((a*a)+(b*b));
// axis lines
glBegin(GL_LINES);
glColor3f(1.0,0.0,0.0); glVertex3dv(o); glVertex3d(o[0]+l[0],o[1],o[2]);
glColor3f(0.0,1.0,0.0); glVertex3dv(o); glVertex3d(o[0],o[1]+l[1],o[2]);
glColor3f(0.0,0.0,1.0); glVertex3dv(o); glVertex3d(o[0],o[1],o[2]+l[2]);
glEnd();
// cones
glBegin(GL_TRIANGLES);
u1=b*cos(-dang);
v1=b*sin(-dang);
for (e=1,ang=0.0;e;ang+=dang)
{
if (ang>=2.0*M_PI) { ang=2.0*M_PI; e=0; }
u0=u1; u1=b*cos(ang);
v0=v1; v1=b*sin(ang);
// X
if (sel==0) glColor3f(1.0,0.0,0.0); else glColor3f(0.5,0.1,0.1);
glNormal3f(a*q,(u0+u1)*0.5*q,(v0+v1)*0.5*q);
glVertex3d(o[0]+l[0] ,o[1] ,o[2] );
glVertex3d(o[0]+l[0]-a,o[1]+u0,o[2]+v0);
glVertex3d(o[0]+l[0]-a,o[1]+u1,o[2]+v1);
glNormal3f(-1.0,0.0,0.0);
glVertex3d(o[0]+l[0]-a,o[1],o[2]);
glVertex3d(o[0]+l[0]-a,o[1]+u1,o[2]+v1);
glVertex3d(o[0]+l[0]-a,o[1]+u0,o[2]+v0);
// Y
if (sel==1) glColor3f(0.0,1.0,0.0); else glColor3f(0.1,0.5,0.1);
glNormal3f((u0+u1)*0.5*q,a*q,(v0+v1)*0.5*q);
glVertex3d(o[0] ,o[1]+l[1] ,o[2] );
glVertex3d(o[0]+u1,o[1]+l[1]-a,o[2]+v1);
glVertex3d(o[0]+u0,o[1]+l[1]-a,o[2]+v0);
glNormal3f(0.0,-1.0,0.0);
glVertex3d(o[0] ,o[1]+l[1]-a,o[2] );
glVertex3d(o[0]+u0,o[1]+l[1]-a,o[2]+v0);
glVertex3d(o[0]+u1,o[1]+l[1]-a,o[2]+v1);
// Z
if (sel==2) glColor3f(0.0,0.0,1.0); else glColor3f(0.1,0.1,0.5);
glNormal3f((v0+v1)*0.5*q,(u0+u1)*0.5*q,a*q);
glVertex3d(o[0] ,o[1] ,o[2]+l[2] );
glVertex3d(o[0]+v1,o[1]+u1,o[2]+l[2]-a);
glVertex3d(o[0]+v0,o[1]+u0,o[2]+l[2]-a);
glNormal3f(0.0,0.0,-1.0);
glVertex3d(o[0] ,o[1] ,o[2]+l[2]-a);
glVertex3d(o[0]+v0,o[1]+u0,o[2]+l[2]-a);
glVertex3d(o[0]+v1,o[1]+u1,o[2]+l[2]-a);
}
glEnd();
}
bool mouse(double mx,double my,TShiftState sh) // handle mouse events return if redraw is needed
{
if ((mm==NULL)||(mv==NULL)||(mp==NULL)) return false;
int ql; double p[3],mmvp[16]; bool _redraw=false;
// MVP = M*V*P
matrix_mul(mmvp,mm,mv);
matrix_mul(mmvp,mmvp,mp);
// convert screen coords to <-1,+1> GL NDC
mx= (2.0*mx/double(xs))-1.0;
my=1.0-(2.0*my/double(ys)) ;
// mouse left button state (last,actual)
ql=sh.Contains(ssLeft);
// select (no mouse button)
if (!ql)
{
int sel0=sel; sel=-1;
// arrowhead center screen x,y distance to mouse select if close
p[0]=o[0]+l[0]-0.5*a; p[1]=o[1]; p[2]=o[2]; project(p,mmvp,p); p[0]-=mx; p[1]-=my; p[2]=0.0; if (vector_len(p)<=0.5*b) sel=0; // X
p[1]=o[1]+l[1]-0.5*a; p[0]=o[0]; p[2]=o[2]; project(p,mmvp,p); p[0]-=mx; p[1]-=my; p[2]=0.0; if (vector_len(p)<=0.5*b) sel=1; // Y
p[2]=o[2]+l[2]-0.5*a; p[1]=o[1]; p[0]=o[0]; project(p,mmvp,p); p[0]-=mx; p[1]-=my; p[2]=0.0; if (vector_len(p)<=0.5*b) sel=2; // Z
_redraw=(sel0!=sel);
}
// drag arrowhead center into mouse position (active button)
if ((ql)&&(sel>=0)&&(sel<3))
{
int i;
double len0,len;
double p0[3],dp[3]={0.0,0.0,0.0},t,dt,q[3]={mx,my,0.0};
// selected arrowhead position,direction
for (i=0;i<3;i++) p0[i]=o[i];
p0[sel]+=l[sel]-0.5*a;
dp[sel]=1.0;
// "closest" intersection between axis/mouse_ray
for (len0=-1.0,t=0.0,dt=1.0;fabs(dt)>1e-5;t+=dt)
{
// position on axis p(t) = p0 + t*dp
for (i=0;i<3;i++) p[i]=p0[i]+(t*dp[i]);
// len = distance to mouse
project(p,mmvp,p);
vector_sub(p,p,q); p[2]=0.0;
len=vector_len(p);
// handle iteration step
if (len0<-0.5) len0=len;
if (len>len0) dt=-0.1*dt;
len0=len;
}
// translate by t
double m[16]=
{
1.0,0.0,0.0,0.0,
0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0,
};
m[12+sel]=t;
matrix_mul(mm,m,mm);
_redraw=true;
}
return _redraw;
}
};
//---------------------------------------------------------------------------
DragDrop3D ctrl; // I got single cube so single arrow drag drop control suffice
//---------------------------------------------------------------------------
void gl_draw() // main rendering code
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
// init
static bool _init=true;
if (_init)
{
_init=false;
// M,V init matrices
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslated(0.0,0.0,-25.0);
glGetDoublev(GL_MODELVIEW_MATRIX,mview);
glLoadIdentity();
glTranslated(-2.0,-1.0,-1.0);
glRotated(-35.0,0.0,1.0,0.0);
glGetDoublev(GL_MODELVIEW_MATRIX,mmodel);
glPopMatrix();
// matrices -> ctrl
glGetDoublev(GL_PROJECTION_MATRIX,mproj);
ctrl.mp=mproj;
ctrl.mv=mview;
ctrl.mm=mmodel;
}
// matrices -> OpenGL
glMatrixMode(GL_MODELVIEW);
glLoadMatrixd(mview);
glMultMatrixd(mmodel);
// draw VAO cube
ctrl.draw();
vao_draw(); // here render your object instead
glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
// application init
gl_init(Handle);
vao_init();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
// application exit
gl_exit();
vao_exit();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
// window resize
gl_resize(ClientWidth,ClientHeight);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
// window repaint
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheel(TObject *Sender, TShiftState Shift, int WheelDelta, TPoint &MousePos, bool &Handled)
{
// mouse wheel translates camera (like zoom)
GLfloat dz=2.0;
if (WheelDelta<0) dz=-dz;
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixd(mview);
glTranslatef(0,0,dz);
glGetDoublev(GL_MODELVIEW_MATRIX,mview);
glPopMatrix();
gl_draw();
}
//---------------------------------------------------------------------------
// mouse events
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y) { if (ctrl.mouse(X,Y,Shift)) gl_draw(); }
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y) { if (ctrl.mouse(X,Y,Shift)) gl_draw(); }
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { if (ctrl.mouse(X,Y,Shift)) gl_draw(); }
//---------------------------------------------------------------------------
It is based on this: simple complete GL+VAO/VBO+GLSL+shaders example in C++ where you will find more info and the gl_simple.h
I am using for this. The code is VCL based so ignore the VCL stuff and port the events to your kind of programming.
The program is using just OpenGL 1.0 API (appart of the VAO/VBO cube representing my object) so it does not need anything apart math.h
for sin,cos,fabs
functions.
The idea is to have one control DragDrop3D
object per each controlable object in the world/scene. Init each first and then call mouse
for each on every mouse event and redraw if needed.
Here small preview:
Sadly my GIF capture SW does not catch mouse cursor but it matches the movement exactly.
I was too lazy to compute the intersection between axises directly instead I iterate to find closest match (that for
loop with dt
). That part could be replaced by intersection equation.
The axises are bounded to the x,y,z axises of the object coordinate system (mm,mmodel
).
For more objects you should also add locks so only one object can be selected ...
PS. see related: - OpenGL ray OBB intersection
相关文章