D3DXVec3Unproject Problems

Started by
29 comments, last by Kram 16 years ago
I have asked questions about the D3DXVec3Unproject method before, but that was embedded into another thread so I think Ill ask the question in a fresh thread. I am trying to make a very simple, rotating cube move to a position in 3d-space from a users mouse click. I can do 90% of this, but when I try to convert the users mouse click into 3D-space, I get strange values (or at least I think they are strange). I have placed a cube at and around the -3 to +3 vertex positions in 3D space. Then I setup the camera, etc... and I can see the cube in the middle of the black screen. Then when I click the mouse, I call a self-defined method called

SetupLocalMousePoints(IDirect3DDevice9* p_d3dDevice)

The method is defined as:

D3DXVECTOR3* GameEntity::SetupLocalMousePoints(IDirect3DDevice9
* p_d3dDevice)
{
	if (m_mousePoint)
	{
		D3DXVECTOR3 result;
		D3DXVECTOR3 screenPoint;
		D3DVIEWPORT9 viewport;
		D3DXMATRIX matProjection;
		D3DXMATRIX matView;
		D3DXMATRIX matWorld;

		screenPoint.x = m_mousePoint->x;
		screenPoint.y = m_mousePoint->y;
		screenPoint.z = 0.0f;
		m_mousePoint = 0;

		p_d3dDevice->GetViewport(&viewport);
		p_d3dDevice->GetTransform( D3DTS_PROJECTION, &matProjection );
		p_d3dDevice->GetTransform( D3DTS_VIEW, &matView );
		p_d3dDevice->GetTransform( D3DTS_WORLD, &matWorld );

		//transform the mouse clicked location
		D3DXVec3Unproject( &result, &screenPoint, &viewport, &matProjection, &matView, &matWorld );
		return &result;
	}
	return NULL;
}

m_mousePoint is a private class member variable:

POINTS* m_mousePoint

The rest should be pretty obvious. Now, the issue is when I click in the TOP RIGHT corner of the screen, I am getting. X: -23, Y:8 and Z:0.44 (ish). Why on earth would X be a negative number??? I am having real trouble here and would appreciate any help at all! Thanks Mark
Advertisement
Because what you're doing is dangerous. Your compiler should generate a warning about this - you're returning a pointer to a local variable.

When your function exits, your result variable goes out of scope, and is destroyed. Your pointer now points to rubbish, because the thing it was pointing to is now gone. To solve this, either return by value, or have an extra parameter in your function like this:
void GameEntity::SetupLocalMousePoints(IDirect3DDevice9* p_d3dDevice, D3DXVECTOR3& return){    /*...*/    D3DXVec3Unproject(&return, &screenPoint, &viewport, &matProjection, &matView, &matWorld);    /*...*/}
NextWar: The Quest for Earth available now for Windows Phone 7.
Quote:Original post by Sc4Freak
Because what you're doing is dangerous. Your compiler should generate a warning about this - you're returning a pointer to a local variable.

When your function exits, your result variable goes out of scope, and is destroyed. Your pointer now points to rubbish, because the thing it was pointing to is now gone. To solve this, either return by value, or have an extra parameter in your function like this:
...


Well thats a good point. I got so caught up in the 3D stuff that I forgot about basic c++...

But anyways, my breakpoints were actually set in that method, so the values that I was checking were the correct ones, but still wrong.

Any more thoughts on the issue here?

Thanks
Mark

P.S. I am happy to attach the project to this thread, or email it, or whatever, to anyone who wants it :)
The usual suspects:

Are you mayhaps using a pure device?
In that case the GetTransform and GetViewport calls will fail. You should check the result codes from them.

It might be a good idea to store the matrices and viewport when you set them anyway to save the getter calls.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Quote:Original post by Endurion
The usual suspects:

Are you mayhaps using a pure device?
In that case the GetTransform and GetViewport calls will fail. You should check the result codes from them.

It might be a good idea to store the matrices and viewport when you set them anyway to save the getter calls.


Thanks,

the Getters are all working ok, I have just checked them and they all return S_OK. It seems that maybe I am getting the correct values, but maybe my view is all wrong.

Below is my render method that I use:

void GameEntity::Render(IDirect3DDevice9* p_d3dDevice){	// select which vertex format we are using    p_d3dDevice->SetFVF(CUSTOMFVF);    // set the view transform    D3DXMATRIX matView;    // the view transform matrix    D3DXMatrixLookAtLH(&matView,    &D3DXVECTOR3 (0.0f, 8.0f, 25.0f),					// the camera position    &D3DXVECTOR3 (0.0f, 0.0f, 0.0f),					// the look-at position    &D3DXVECTOR3 (0.0f, 1.0f, 0.0f));					// the up direction    p_d3dDevice->SetTransform(D3DTS_VIEW, &matView);    // set the world transform    static float index = 0.0f; index += 0.03f;				// an ever-increasing float value    D3DXMATRIX matRotateY;									// a matrix to store the rotation    D3DXMatrixRotationY(&matRotateY, index);				// the rotation matrix	    // set the projection transform    D3DXMATRIX matProjection;						// the projection transform matrix    D3DXMatrixPerspectiveFovLH(&matProjection,                               D3DXToRadian(45),    // the horizontal field of view                               640.0f / 480.0f,		// aspect ratio                               1.0f,				// the near view-plane                               100.0f);				// the far view-plane    p_d3dDevice->SetTransform(D3DTS_PROJECTION, &matProjection);	//convert clicked positions into 3D space positions if user has	//clicked the mouse...	this->SetupLocalMousePoints(p_d3dDevice, &m_xOffset, &m_yOffset);	//Remember...Scaling, Translation, Rotation	//translate the X and Y of needed	D3DXMATRIX matTranslate;	D3DXMatrixTranslation(&matTranslate, m_xOffset, m_yOffset, m_zOffset);	//ALWAYS, Rotate before Translate    p_d3dDevice->SetTransform(D3DTS_WORLD, &(matRotateY * matTranslate));  // set the world transform    // select the vertex buffer to display    p_d3dDevice->SetStreamSource(0, this->GetVertexBuffer(), 0, sizeof(CUSTOMVERTEX));    // set the texture    p_d3dDevice->SetTexture(0, m_triangleTexture);    // draw the textured square    p_d3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);    p_d3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 4, 2);    p_d3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 8, 2);    p_d3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 12, 2);    p_d3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 16, 2);    p_d3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 20, 2); }


and the SetupLocalMousePoints method (yes it is different than the original post, as I am simply try new things :))

void GameEntity::SetupLocalMousePoints(IDirect3DDevice9* p_d3dDevice, float* m_xOffset, float* m_yOffset){	if (m_mousePoint)	{		D3DXVECTOR3 screenPoint;		D3DVIEWPORT9 viewport;		D3DXMATRIX matProjection;		D3DXMATRIX matView;		D3DXMATRIX matWorld;		D3DXVECTOR3 screenVector;		screenPoint.x = m_mousePoint->x;		screenPoint.y = m_mousePoint->y;		screenPoint.z = 0.0f;		HRESULT viewportRes = p_d3dDevice->GetViewport(&viewport);		HRESULT projRes = p_d3dDevice->GetTransform( D3DTS_PROJECTION, &matProjection );		HRESULT viewRes = p_d3dDevice->GetTransform( D3DTS_VIEW, &matView );		HRESULT worldRes = p_d3dDevice->GetTransform( D3DTS_WORLD, &matWorld );		//transform the mouse clicked location		D3DXVec3Unproject( &screenVector, &screenPoint, &viewport, &matProjection, &matView, &matWorld );		*m_xOffset = screenVector.x;		*m_yOffset = screenVector.y;		m_mousePoint = 0;	}}


Mark
You're setting the world transform after the call to SetupLocalMousePoints, that might end up using an old world transform matrix.

Also, with one Unproject call with z=0 you get the 3d coordinate on the near plane. You should do two Unproject calls, one with z=0, one with z=1.

Use the resulting line to do intersection tests on your cube.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Quote:Original post by Endurion
You're setting the world transform after the call to SetupLocalMousePoints, that might end up using an old world transform matrix.

Also, with one Unproject call with z=0 you get the 3d coordinate on the near plane. You should do two Unproject calls, one with z=0, one with z=1.

Use the resulting line to do intersection tests on your cube.


Yes, I thought that is what I would have to do, setup the X and Y transformation, which is what SetupLocalMousePoints does, then call the D3DTS_WORLD transformation with the altered X and Y offsets. Isn't that be the way it should be done?

If I did it afterwards, the translation of the X, Y and Z values would be done too late wouldn't they? Although, the next time the Render() method is called, they will be altered correctly, but surely that shouldn't matter, should it??

Also, what is the reasoning behind needing to do the z=0 and z=1 calls?

Thanks for the help!
Mark
Think about why you're making the Unproject call.

What you see on the screen is a 2D projection of objects in a world-space volume that's a truncated pyramid. A point on the 2D screen is the projection of a line in world space. In screen-space the points on that line are ( mouseX, mouseY, some-Z-value ).

Looking down from the top:
+----------------*--+    far plane ( z=1 ) \              /  /  \...........[/]./   \          /  /    \        /  /     +------*--+   near plane ( z=0 )

Your mouse point on the screen is the line between the two asterisks in world space. The place you want to place the cube is somewhere on that line at some z value between 0 ( the near plane ) and 1 ( the far plane ).

You have to decide where on that line, what z value in screen-space, you want the cube to be. Use that z-value in the Unproject call to get the world-space position.

One way to determine that screen-space z-value is to choose the plane (
  ......  
in the diagram above ) where you want the cube to be. Pick a point (any point) in world-space in that plane. If you want to move the cube within the same plane all the time, you can use the point at the center of the cube in world-space. Use a Project call to get the screen-space Z-value for that plane and use that Z-value in your Unproject call with your mouse points.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Ok so correct me if I am wrong, but you are saying that I need to setup the Z value because the line that moves back through the 3D space projection, is NOT a strait line, rather, it moves away as a diagonal line. So at different values of Z will give different values of X and Y?

I think Im getting you.

Therefor, if you want to keep the cube on the SAME Z plane, you need to get the current Z position of the center of the cube and use that in the Unproject call.

Correct?

If so, could I please ask you to help me GET the Z corrds from the D3DXVec3Project call? If it converts a Vector from 3D space into screen space, Im not too sure on the best way of getting that Vector...

Thanks so much for the help, I appreciate it greatly.
Yep. The line "behind" the mouse point is a diagonal line (it is straight, by the way ) and the world-space x,y,z values vary along the screen-space line as the screen-space z-value changes.

For the project call, use a world-space D3DXVECTOR3 at the center of your cube. Then use the pOut z-value from the Project call in your Unproject pIn vector.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

This topic is closed to new replies.

Advertisement