计算使用箭头和鼠标移动的对象 [英] Compute objects moving with arrows and mouse

查看:73
本文介绍了计算使用箭头和鼠标移动的对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用openGL做一个Maya模拟器应用程序,一切进展都很好.只是有一个麻烦,我无法用鼠标计算出移动物体的100%精度. 我使用3D坐标中的箭头比例和屏幕坐标中的箭头比例,并将其乘以某种比例来计算屏幕坐标在x和y方向上的运动程度. . 但是这些计算不能提供很高的准确性,鼠标偏离了箭头.我总是在移动的箭头上需要它,这意味着精度几乎是100% 对这个问题有任何想法或意见的人将始终受到赞赏

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

推荐答案

以下是我想到的一个很小但很简单的 C ++ 示例:

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

它基于以下内容:使用C ++的简单完整的GL + VAO/VBO + GLSL + shaders示例查找更多信息以及我正在为此使用的gl_simple.h.该代码基于 VCL ,因此请忽略 VCL 内容,并将事件移植到您的程序中.

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.

该程序仅使用 OpenGL 1.0 API (代表我的对象的VAO/VBO多维数据集的应用程序),因此对于sin,cos,fabs函数,它不需要math.h即可.

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.

这个想法是在世界/场景中每个可控制对象都有一个控制DragDrop3D对象.首先初始化每个对象,然后在每次鼠标事件时分别对每个对象调用mouse,并在需要时重画.

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.

以下是小预览:

可悲的是,我的 GIF 捕获 SW 不能捕获鼠标光标,但它与移动完全匹配.

Sadly my GIF capture SW does not catch mouse cursor but it matches the movement exactly.

我太懒了,无法直接计算轴之间的交点,反而我反复寻找最接近的匹配项(fordt循环).该部分可以用交集方程代替.

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.

轴与对象坐标系(mm,mmodel)的x,y,z轴绑定.

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.参见相关内容: - OpenGL ray OBB交集

PS. see related: - OpenGL ray OBB intersection

这篇关于计算使用箭头和鼠标移动的对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆