Jump to content
  • Advertisement
Sign in to follow this  
KShots

OpenGL "dragging" a distant point under the mouse

This topic is 4402 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello all I'm trying to work out a problem - I want to click on an object and drag it under the mouse. Currently, I can select the object using the OpenGL selection buffer, and I can use gluUnProject() to figure the exact coordinates of the hit on the object in question... but because the viewing frustum is not a cube, but a trapezoid... it's not trivial to simply "drag" the object along the camera's perpendicular vectors. So, here's how I envision solving this one: I can get the "depth" of the object easily. I'm aware the the near and far "squares" of the frustum can be obtained. I suspect I can take the X and Y coordinates of the mouse, convert them to percentages (0 in x is far left, 1 is far right), calculate the extremities at the depth of the object in question, and apply those percentages to the extremities to obtain new coordinates. For this, I would need only to know how to find the frustum coordinates. Any ideas? Is there a better way?

Share this post


Link to post
Share on other sites
Advertisement
What you'll want to do is generate a pick ray form the eye of the camera to where the screen is and intersect that with the deisred plane of movement.

This pseudocode assumes you are using perspective projection.
Note: matrices are the usual column-major format.
M.e refers to the ith element of M in that format.
Vectors presumably initialize to zero.
Vector versus Matrix multiplies occurr in the usual way.


// utilities that make life easier

// linear interpolation
float lerp(float t, float a, float b)
{
return a+t*(b-a);
}
// invert an orthonormal matrix
void invertM(matrix &M){
// lots of code
}
// intersect a ray with a plane
vector &isect_rayplane(ray &r, plane &n){
// will post separately
}

// somewhere in your program:
int width = SCREEN_WIDTH
int height = SCREEN_HEIGHT
float aspect = float(width)/float(height);

...

// DV_out is the direction of the ray
// BP_out is the base point of the ray
// output coordinates are located conveniently in object space
void pick_ray(int x, int y, vector &DV_out, vector &BP_out)
{
matrix TM, PM;
glGetFloatv(GL_MODELVIEW_MATRIX,&TM.e[0]);
invertM(TM);

BP_out = TM.column(3); // the translation column, or rightmost of
// the modelview matrix

glGetFloatv(GL_PROJECTION_MATRIX,&PM.e[0]);

vector DVtmp;

// normalize the input screen coords (you may have to invert the Y axis here)
DVtmp.x = aspect * lerp(float(x)/float(width),-1.0f,1.0f);
DVtmp.y = aspect * lerp(float(y)/float(height),-1.0f,1.0f);
DVtmp.z = -PM.e[5]; // this is the distance from the camera's "eye"
// to the screen, or what I believe is
// called the "rasterization plane"

DV_out = (DVtmp * TM) - BP_out;
}

/*
Now we want to keep trak of the cursor coordinates for the current and previous
program cycles, as well as their corresponding pick rays and their
intersections with our plane of choice.
*/


int CCX, CCY; // current cursor position
int LCX = 0, LCY = 0; // last cursor position
ray CR, LR;

// intersection points of both rays
vector ixC, ixL;

// the plane of choice
plane PlaneOfChoice;

// much later on, say in WndProc():

case WM_LBUTTONDOWN: // or the button of your choice
{
LR = CR; // reset the last recorded pick ray
ixL = ixC; // reset the last intersection point
};
case WM_MOUSEMOVE:
{
CCX = LOWORD(lParam);
CCY = HIWORD(lParam);

if(left_button_is_down) // or some other button
{
// create the pick ray from these mouse coordinates
pick_ray(CCX,CCY,CR.direction,CR.base);

// intersect the current ray with the plane of choice
ixC = isect_rayplane(CR,PlaneOfChoice);

// handle dragging (move objects by adding the delta vector to
// their position)
vector DV = ixC - ixL;

// do the moving here

...

// record intersection points and pick ray
ixL = ixC;
LR = CR;
}

// save mouse coordinates
LCX = CCX;
LCY = CCY;
};




[Edited by - i-photon on July 28, 2006 4:24:06 AM]

Share this post


Link to post
Share on other sites
I think the matrix inverter is too big to post here. Just consider looking it up online.

As far as the ray/plane intersector goes:

vector &isect_rayplane(ray &r, plane &p)
{
float t = -dot(p.normal,r.base + r.direction)/dot(p.normal,ray.direction);

return vector(t*r.direction + r.base);
}

I hope this helps :)

[Edited by - i-photon on July 28, 2006 1:50:22 AM]

Share this post


Link to post
Share on other sites
On second thought, here ya go:


// returns the determinant of a 4x4 column-major matrix
float detM(matrix &M)
{
float *e = &M->e[0];
return e[1]*e[11]*e[14]*e[4]-
e[1]*e[10]*e[15]*e[4]-
e[11]*e[13]*e[2]*e[4]+
e[10]*e[13]*e[3]*e[4]-
e[0]*e[11]*e[14]*e[5]+
e[0]*e[10]*e[15]*e[5]+
e[11]*e[12]*e[2]*e[5]-
e[10]*e[12]*e[3]*e[5]-
e[1]*e[11]*e[12]*e[6]+
e[0]*e[11]*e[13]*e[6]+
e[1]*e[10]*e[12]*e[7]-
e[0]*e[10]*e[13]*e[7]-
e[15]*e[2]*e[5]*e[8]+
e[14]*e[3]*e[5]*e[8]+
e[1]*e[15]*e[6]*e[8]-
e[13]*e[3]*e[6]*e[8]-
e[1]*e[14]*e[7]*e[8]+
e[13]*e[2]*e[7]*e[8]+
e[15]*e[2]*e[4]*e[9]-
e[14]*e[3]*e[4]*e[9]-
e[0]*e[15]*e[6]*e[9]+
e[12]*e[3]*e[6]*e[9]+
e[0]*e[14]*e[7]*e[9]-
e[12]*e[2]*e[7]*e[9];
}
// inverts a 4x4 orthogonal column-major matrix
void invertM(matrix &M)
{
matrix TMP = M;
float *e = &TMP.e[0];
float d = detM(M); // get the determinant
if(d != 0.0f)
d = 1.0f/d;
else // edit: woops! forgot this...
d = 1.0f;

M->e[0] = (-e[11]*e[14]*e[5]+e[10]*e[15]*e[5]+
e[11]*e[13]*e[6]-e[10]*e[13]*e[7]-e[15]*e[6]*e[9]+e[14]*e[7]*e[9])*d;
M->e[1] = (e[1]*e[11]*e[14]-e[1]*e[10]*e[15]-
e[11]*e[13]*e[2]+e[10]*e[13]*e[3]+e[15]*e[2]*e[9]-e[14]*e[3]*e[9])*d;
M->e[2] = (-e[15]*e[2]*e[5]+e[14]*e[3]*e[5]+
e[1]*e[15]*e[6]-e[13]*e[3]*e[6]-e[1]*e[14]*e[7]+e[13]*e[2]*e[7])*d;
M->e[3] = (e[11]*e[2]*e[5]-e[10]*e[3]*e[5]-
e[1]*e[11]*e[6]+e[1]*e[10]*e[7]+e[3]*e[6]*e[9]-e[2]*e[7]*e[9])*d;

M->e[4] = (e[11]*e[14]*e[4]-e[10]*e[15]*e[4]-
e[11]*e[12]*e[6]+e[10]*e[12]*e[7]+e[15]*e[6]*e[8]-e[14]*e[7]*e[8])*d;
M->e[5] = (-e[0]*e[11]*e[14]+e[0]*e[10]*e[15]+
e[11]*e[12]*e[2]-e[10]*e[12]*e[3]-e[15]*e[2]*e[8]+e[14]*e[3]*e[8])*d;
M->e[6] = (e[15]*e[2]*e[4]-e[14]*e[3]*e[4]-
e[0]*e[15]*e[6]+e[12]*e[3]*e[6]+e[0]*e[14]*e[7]-e[12]*e[2]*e[7])*d;
M->e[7] = (-e[11]*e[2]*e[4]+e[10]*e[3]*e[4]+
e[0]*e[11]*e[6]-e[0]*e[10]*e[7]-e[3]*e[6]*e[8]+e[2]*e[7]*e[8])*d;

M->e[8] = (-e[11]*e[13]*e[4]+e[11]*e[12]*e[5]-
e[15]*e[5]*e[8]+e[13]*e[7]*e[8]+e[15]*e[4]*e[9]-e[12]*e[7]*e[9])*d;
M->e[9] = (-e[1]*e[11]*e[12]+e[0]*e[11]*e[13]+
e[1]*e[15]*e[8]-e[13]*e[3]*e[8]-e[0]*e[15]*e[9]+e[12]*e[3]*e[9])*d;
M->e[10] = (-e[1]*e[15]*e[4]+e[13]*e[3]*e[4]+
e[0]*e[15]*e[5]-e[12]*e[3]*e[5]+e[1]*e[12]*e[7]-e[0]*e[13]*e[7])*d;
M->e[11] = (e[1]*e[11]*e[4]-e[0]*e[11]*e[5]+
e[3]*e[5]*e[8]-e[1]*e[7]*e[8]-e[3]*e[4]*e[9]+e[0]*e[7]*e[9])*d;

M->e[12] = (e[10]*e[13]*e[4]-e[10]*e[12]*e[5]+
e[14]*e[5]*e[8]-e[13]*e[6]*e[8]-e[14]*e[4]*e[9]+e[12]*e[6]*e[9])*d;
M->e[13] = (e[1]*e[10]*e[12]-e[0]*e[10]*e[13]-
e[1]*e[14]*e[8]+e[13]*e[2]*e[8]+e[0]*e[14]*e[9]-e[12]*e[2]*e[9])*d;
M->e[14] = (e[1]*e[14]*e[4]-e[13]*e[2]*e[4]-
e[0]*e[14]*e[5]+e[12]*e[2]*e[5]-e[1]*e[12]*e[6]+e[0]*e[13]*e[6])*d;
M->e[15] = (-e[1]*e[10]*e[4]+e[0]*e[10]*e[5]-
e[2]*e[5]*e[8]+e[1]*e[6]*e[8]+e[2]*e[4]*e[9]-e[0]*e[6]*e[9])*d;
}






I copy/pasted this from my vector library. I hope it works for you.

[Edited by - i-photon on July 28, 2006 6:04:18 PM]

Share this post


Link to post
Share on other sites
Hmm... I think I see what you're suggesting. Instead of taking the depth and a parametric equation of the frustum, you're taking the depth and using a pick ray to find the new location. I should have taken that approach to begin with - I have to do the pick ray anyway to find the exact depth of the object at the point in question.

In my case, I don't use column-major matrices... but that's not that much of a problem, now that I see what I should be doing. Thanks for the pointer :)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!