Sign in to follow this  

Translate 2D mouse position to place an object in 3D space

This topic is 653 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

I want to be able to click on the screen and place a box at that spot in perspective 3D space. I would need to give the box the offsets in world view. The camera is at 0,0,0. In addition to the screen coordinates where 0,0 is the center of the screen, I also know the z order of where I want to box to go. Say -150

 

I've been struggling with this problem for a day now and google searches are not helping. Thought I could solve it with simple tri which is all I'm good at.

 

Can anyone solve this?

 

Share this post


Link to post
Share on other sites

Thank you guys for your help!

 

Taby, thank you for posting the code. It works GREAT!

 

All I had to do was multiply the final X and Y values by a positive z order.

 

I couldn't be more happy! There's no way I would have been able to write that code. Taby, you are Awesome!

Share this post


Link to post
Share on other sites

Fun little snippet.

 

Many people try to turn a mouse's 2D position into a single location in a 3D world, forgetting that the projection results in a ray or a volume (the front of the volume is the area of a pixel).

 

Yeah, I use the concept of an image plane in my description of the code. Whether or not the code works for a raytracer is undecided as of yet. smile.png

Edited by taby

Share this post


Link to post
Share on other sites

Thank you guys for your help!

 

I found the code on Stack Overflow a few months ago. If you like the code, then upvote me.

Edited by taby

Share this post


Link to post
Share on other sites

Use printf or cout or NSLog or whatever to print out the values held by fx, fy, etc., to see what's going on during the calculations. Let me know how that goes.

Edited by taby

Share this post


Link to post
Share on other sites

Read: http://mathworld.wolfram.com/Tangent.html -- look for the big triangle. Remember SOHCAHTOA.

 

tan(theta), where theta = pi/8, gives you the rise over run. When the camera is 1 unit away from the origin, the run is 1. The rise goes on to give you half of the image plane width.

Edited by taby

Share this post


Link to post
Share on other sites

Read: http://mathworld.wolfram.com/Tangent.html -- look for the big triangle. Remember SOHCAHTOA.

 

tan(theta), where theta = pi/8, gives you the rise over run. When the camera is 1 unit away from the origin, the run is 1. The rise goes on to give you half of the image plane width.

That helps because I didn't know what the tangent was for.

 

I've analyzed the code and simplified it. Below is the final modified version. Let's look at it line by line.

 

First useless line.

const float pi = 4.0f*atanf(1.0f);

All this does is give us the value of PI. math.h has a #define you can use so this is unnecessary. As you'll see in the finial code, many things were unnecessary.

 

Next line.

const float aspect = (float)(m_renderbufferWidth) / (float)(m_renderbufferHeight);

This give us the aspect ratio of the screen. Not sure why this is needed for the x coordinate and not the y. I think it's magic.

 

Next Two lines.

const float fx = 2.0f * ((float)(x) / (float)(m_renderbufferWidth - 1)) - 1.0f;
const float fy = 2.0f * ((float)(y) / (float)(m_renderbufferHeight - 1)) - 1.0f;

This converts screen coordinates from a top/left orientation to a center screen coordinates but there's a little more to this then that.

1) Subtracting the width and height by one. The reason you do this is because resolution sizes are usually all even numbers. For example, a resolution of 1280x720 there is no center pixel or a pixel at 0. The other reason this could be is that you need a value of base 0, as if this was an array and w/h represents the total number of elements.

2) x/y divied by w/h. This gives you a normalize value of the point on your screen. In other words, a value from 0 to 1.

3) 2.0f *; not entirely sure what this is doing and why it is needed.

4) The last - 1.0f flips the sign.

 

Second useless line

const float y_fov = pi/4; // pi/4 radians = 45 degrees

This takes PI and divides it by 4 to give us 45 degrees in radians. This is unnecessary.

 

Modified line.

const float tangent = tan(y_fov / 2.0f);

This divides the 45 degrees by 2 to give use 22.5 degrees in radians. This can be simplified by plopping a number in tan.

 

I know what the rest is doing, I just don't understand why it works.

/************************************************************************
*    desc:  Convert 2d screen coordinates to 3D perspective space
************************************************************************/
void Convert2Dto3D( float & destX, float & destY, float x, float y, float width, float height )
{
    const float aspect = width / height;

    // Convert upper left 0,0 to center screen 0,0 coordinates
    const float fx = (2.0f * (x / (width - 1))) - 1.0f;
    const float fy = (2.0f * (y / (height - 1))) - 1.0f;

    // (pi/4 radians = 45 degrees / 2) or ((pi/4) / 2))
    const float tangent = tan( 0.392699082 );

    // Project x,y to a z plane of 1
    destX = aspect * tangent* fx;
    destY = -tangent * fy;

}   // Convert2Dto3D
Edited by GameCodingNinja

Share this post


Link to post
Share on other sites

fx calculates first x/(width - 1), which gives a value from 0 to 1. Then multiplies it by 2.0, which gives a value from 0 to 2. Then it subtracts 1, which gives you a value from -1 to 1. fy works similarly. Print out the values of fx and fy and click on the corners of your window to get my meaning.

 

You might not want to optimize the y_fov and pi out of the equation. You'd only optimize a little bit without them, and this function doesn't really need to be optimized as such -- the function is only called once per second or so, depending on your mouse skills. Heck, the compiler might even optimize for you. On top of that, I find y_fov / 2 to be more explanatory than some magic number like 0.39269908, even if it's explained in the comments.

 

The aspect is used only for x because y is special. We deal in y's field of view y_fov, not x_fov, which is another reason to not optimize y_fov out of the equation.

Edited by taby

Share this post


Link to post
Share on other sites


// (pi/4 radians = 45 degrees / 2) or ((pi/4) / 2))
const float tangent = tan( 0.392699082 );


I hate stuff like this. If you mis-typed that number, who is ever going to check whether it is correct? Someone is eventually going to have to figure it out after getting a vague bug about the mouse controls being slightly off. Or it will never be fixed, and your game would just feel wrong. You can easily write code that would probably get worked out at compile time using M_PI, or that at worst case would happen once when the compilation unit is initialized by using a static variable outside of the function.

In general, I wouldn't hard-code in any constants, even for something that requires a little time to compute, with also making sure there was a fool-proof means of validating them. I've worked on a few games that did massive amounts of initialization when certain compilation units were loaded, and it's just something to live with when there are hundreds of more noticeable things to be optimized. I don't mind a little more initialization to help avoid an easy-to-miss mistake.

And if you're going to hard-code a number there, why leave in the tan() function?

Share this post


Link to post
Share on other sites

This topic is 653 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this