计算用箭头和鼠标移动的对象

时间:2018-06-17 01:25:54

标签: c++ opengl math

我正在使用openGL进行maya模拟器应用程序,一切进展顺利。只有一个麻烦,我不能用鼠标计算100%移动物体的准确度。 我使用3D坐标中的箭头缩放和屏幕坐标中的箭头,并将其与一些缩放相乘,以计算屏幕坐标的x和y方向上的运动程度。 。 但是这些计算不能提供高精度,鼠标偏离箭头。我总是在移动的箭头上需要它,这意味着精度几乎是100% 任何对此问题有想法或意见的人都将不胜感激

enter image description here

这是我尝试的一些代码:

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;

这是我测试的代码,它不是很准确

1 个答案:

答案 0 :(得分:0)

这里有一些丑陋但非常简单的 C ++ 示例,其中包括:

//---------------------------------------------------------------------------
#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(); }
//---------------------------------------------------------------------------

它基于以下内容:simple complete GL+VAO/VBO+GLSL+shaders example in C++您可以在其中找到更多信息以及我正在使用的gl_simple.h。代码基于 VCL ,因此请忽略 VCL 内容并将事件移植到您的编程类型中。

该程序仅使用 OpenGL 1.0 API (表示我的对象的VAO / VBO多维数据集的appart),因此math.h函数除sin,cos,fabs之外不需要任何其他内容。

这个想法是在世界/场景中为每个可控对象设置一个控件DragDrop3D对象。首先是每个初始化,然后在每个鼠标事件上为每个鼠标事件调用mouse,并在需要时重绘。

这里有小预览:

preview

可悲的是,我的 GIF 捕获 SW 并没有捕获鼠标光标,但它与运动完全匹配。

我太懒了直接计算轴之间的交集,而是迭代找到最接近的匹配(for循环与dt)。那部分可以用交叉方程代替。

轴与主体坐标系(mm,mmodel)的x,y,z轴有界。

对于更多对象,您还应添加锁定,以便只能选择一个对象...

PS。见相关: - OpenGL ray OBB intersection