Offset in Collision Detection with Ray?

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
QuantumLeap
Posts: 38
Joined: Mon Sep 25, 2006 6:31 pm
Location: San Francisco, California

Post by QuantumLeap »

Of course that is a totally different problem, and you could eliminate it by simply setting the camera id to 0.
That's exactly what I did and works just fine
From the screenshot it looks like gabb is rendering to an external window. Is this the same for you? If so, this could be the source of the problem.
That's an interesting clue. I am indeed using wxWidgets and Irrlicht is displayed inside one of its windows. But I thought the mouse position is calculated within the Irrlicht window only!
As a test, I'll place some Irrlicht native GUI elements and try to make the mouse interact with them.
It's easier to curse a candle than light the darkness
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

I can easily reproduce the problem if I set the viewport area to be smaller than the actual window size. I tested against the older code, and that problem was there... Are either of you doing a driver->setViewPort() call?
Last edited by vitek on Fri Nov 10, 2006 10:23 pm, edited 2 times in total.
gabb
Posts: 8
Joined: Tue Aug 08, 2006 8:54 am

Post by gabb »

Nope, no setViewPort() calls here. But I also experienced problems with TextSceneNodes. Here they disappeared completely (the screenshot was of a former version with 1.1 binaries).

My app is a little bit complex, but I am trying to extract a test case for the problem, maybe it works out.

@QuantumLeap: It's a .NET gui controls library by DevComponents, commercial though.
QuantumLeap
Posts: 38
Joined: Mon Sep 25, 2006 6:31 pm
Location: San Francisco, California

Post by QuantumLeap »

Are either of you doing a driver->setViewPort() call?
Yes I am. My app requires three Irrlicht viewports. Maybe that's the problem then. Strange that the offset is only vertical and I don't notice any problem with the horizontal alignment of the mouse/nodes
It's easier to curse a candle than light the darkness
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

If your Irrlicht window is 800x600 pixels, but the viewport is 800x300 pixels, the pixel shift will be vertical only.

I think I see the problem. The mouse position that you are passing in is in window coordinates, but the collision manager treats it as if it were viewport coordinates. If the window and viewport are exactly the same size, there is no problem, but if they are not...

Also, I think you need to make sure that the camera aspect ratio is set properly to the aspect ratio of the view area. This should be done automatically, but it is not in Irrlicht. If the aspect ratio is not right, the view frustum will not be properly sized, and that will cause the picking code to fail also.
QuantumLeap
Posts: 38
Joined: Mon Sep 25, 2006 6:31 pm
Location: San Francisco, California

Post by QuantumLeap »

Actually what is happening is exactly the opposite. I kept the height of the viewport as big as the Irrlicht window but the width is a little smaller. Hence I changed only the horizontal dimension, but the offset is vertical. That's why I find it strange
It's easier to curse a candle than light the darkness
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

Yes, that could be caused by the last thing I mentioned in my previous post. Make sure that the camera aspect ratio is being set correctly given the viewport size...

Code: Select all

  // this _needs_ to be done when the view rect is resized
  const core::rect<s32>& vp = driver->getViewPort();
  camera->setAspectRatio(1.f * vp.getWidth() / vp.getHeight());
Also, you might want to try this in the block of code that you use to locate the selected node. It works around the issue of view vs window coordinates.

Code: Select all

// convert mouse click to viewport coordinates
video::IVideoDriver* driver = SceneManager->getVideoDriver();

const core::rect<s32>& viewPort = driver->getViewPort();
const core::dimension2di viewSize(viewPort.getWidth(), viewPort.getHeight());
const core::dimension2di screenSize(driver->getScreenSize());

pos.X -= viewPort.UpperLeftCorner.X;
pos.Y -= viewPort.UpperLeftCorner.Y;

pos.X *= 1.f * screenSize.Width / viewSize.Width;
pos.Y *= 1.f * screenSize.Height / viewSize.Height;

if (pos.X < 0 || viewSize.Width < pos.X || pos.Y < 0 || viewSize.Height < pos.Y)
   return false; // user clicked outside the viewport area.
I think maybe the get*ScreenCoordinates*() functions should be renamed to get*ViewCoordinates*() or the collision manager should have a new set of functions added.

The current get*ScreenCoordinates*() methods actually expect that the position is in viewport coordinates, which is wrong given the function name and documentation. The existing code happens to work for the most common case, but does not work for other cases.
Last edited by vitek on Thu May 10, 2007 5:20 am, edited 1 time in total.
QuantumLeap
Posts: 38
Joined: Mon Sep 25, 2006 6:31 pm
Location: San Francisco, California

Post by QuantumLeap »

Vitek,

Thanks for your try. But it's not working! I already had the camera aspect ratio adjusted, so that's not the problem. And I tried your coordinate adjustment from window to viewport and it produced absolutely no result (I forgot to mention that the viewport where the picking is happening has the same upper left corner as the Irrlicht window, so no translation is necessary). This is something else, although I don't know what. I temporarily solved the question (in a inelegant way though) by adding to pos.Y 27 pixels, the value of the displacement. Now it works perfectly as expected!

I really appreciate your patch though.
It's easier to curse a candle than light the darkness
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

As I mentioned before, if possible it would be very useful to get a simple testcase that illustrates the problem. If I can't reproduce the problem, it will make it more difficult to solve it.

Travis
cvf
Posts: 7
Joined: Wed May 16, 2007 12:39 pm

Post by cvf »

I have the same problem in my window:

Image Image
Image Image

I have checked the mouse coordinates I get, and the size of the view with:

Code: Select all

GetWindowRect
getViewPort
getScreenSize
but all seems to be OK, and the size of the view is the same with the 3 functions above...

The offset seems to change when I move the camera and the position of the mouse... This doesn't happen in all cases, before moving the camera all the nodes work perfectly in all zones of the screen. This seems to happen only after moving the camera...
Any ideas? Thanks in advance,

Hugo
cvf
Posts: 7
Joined: Wed May 16, 2007 12:39 pm

Post by cvf »

I have modified the "14.Win32Window" Irrlicht example to show the bug. Here it is the modified version of "main.cpp":

Code: Select all

// this example only runs in windows and demonstrates that Irrlicht
// can run inside a win32 window.

#include <irrlicht.h>
#include <afxwin.h>
#include <windows.h> // this example only runs with windows

using namespace irr;

#pragma comment(lib, "irrlicht.lib")


const int IDFILTER = 8;
irr::scene::ISceneManager* smgr;
video::IVideoDriver* driver;

/////////////////////////////////////////
/////////////////////////////////////////

class C3DWnd : public CWnd
{
public:
	C3DWnd();
	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
	scene::ISceneNode* m_selectedSceneNode;
protected:
	DECLARE_MESSAGE_MAP()
};

C3DWnd::C3DWnd()
{
	smgr = NULL;
	driver = NULL;
	m_selectedSceneNode = 0;
}

void C3DWnd::OnMouseMove(UINT nFlags, CPoint point)
{
	scene::ISceneNode* selectedSceneNode = 0;
	selectedSceneNode = smgr->getSceneCollisionManager()->getSceneNodeFromScreenCoordinatesBB(core::position2d<s32>(point.x, point.y), IDFILTER);

	if (m_selectedSceneNode)
		if (selectedSceneNode != m_selectedSceneNode)
			m_selectedSceneNode->getMaterial(0).EmissiveColor.set(0,0,0,0);

	if (selectedSceneNode)
	{
		selectedSceneNode->getMaterial(0).EmissiveColor.set(128, 255, 0, 0);
		m_selectedSceneNode = selectedSceneNode;

		driver->beginScene(true, true, 0);
		smgr->drawAll();
		driver->endScene();
	}

	CWnd::OnMouseMove(nFlags, point);
}

BEGIN_MESSAGE_MAP(C3DWnd, CWnd)
	ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()

/////////////////////////////////////////
/////////////////////////////////////////


C3DWnd g_3DWnd;
HWND hOKButton;
HWND hWnd;

static LRESULT CALLBACK CustomWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_COMMAND:
		{
			HWND hwndCtl = (HWND)lParam;
			int code = HIWORD(wParam);

			if (hwndCtl == hOKButton)
			{
				DestroyWindow(hWnd);
				PostQuitMessage(0);
				return 0;
			}
		}
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	//case WM_MOUSEMOVE:
	//	break;
	}

	return DefWindowProc(hWnd, message, wParam, lParam);
}





int main()
//int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hpre, LPSTR cmd, int cc)
{
	HINSTANCE hInstance = 0;
	// create dialog

	const char* Win32ClassName = "CIrrlichtWindowsTestDialog";

	WNDCLASSEX wcex;
	wcex.cbSize			= sizeof(WNDCLASSEX);
	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)CustomWndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= DLGWINDOWEXTRA;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= NULL;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW);
	wcex.lpszMenuName	= 0;
	wcex.lpszClassName	= Win32ClassName;
	wcex.hIconSm		= 0;

	RegisterClassEx(&wcex);

	DWORD style = WS_SYSMENU | WS_BORDER | WS_CAPTION |
		WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX;

	int windowWidth = 800;
	int windowHeight = 600;

	hWnd = CreateWindow( Win32ClassName, "Irrlicht Win32 window example:  ---Shifted Mouse Coordinates Bug---",
		style, 100, 100, windowWidth, windowHeight,
		NULL, NULL, hInstance, NULL);

	RECT clientRect;
	GetClientRect(hWnd, &clientRect);
	windowWidth = clientRect.right;
	windowHeight = clientRect.bottom;

	// create ok button

	hOKButton = CreateWindow("BUTTON", "OK - Close", WS_CHILD | WS_VISIBLE | BS_TEXT,
		windowWidth - 160, windowHeight - 40, 150, 30, hWnd, NULL, hInstance, NULL);

	// create some text

	CreateWindow("STATIC", "Move the mouse over the objects to see the Shifted Mouse Coordinates Bug\n"\
							"In the left-down corner works fine, but try in the other corners...",
		WS_CHILD | WS_VISIBLE, 20, 20, 600, 40, hWnd, NULL, hInstance, NULL);

	// create window to put irrlicht in

	//HWND hIrrlichtWindow = CreateWindow("BUTTON", "", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
	//		50, 80, 700, 430, hWnd, NULL, hInstance, NULL);

	g_3DWnd.CreateEx(NULL, "BUTTON", "", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
								50, 80, 700, 430, hWnd, NULL, NULL);
	g_3DWnd.SetFocus();

	// create irrlicht device in the button window

	irr::SIrrlichtCreationParameters param;
//	param.WindowId = reinterpret_cast<s32>(hIrrlichtWindow); // hColorButton
	param.WindowId = reinterpret_cast<s32>(g_3DWnd.m_hWnd); // hColorButton
	param.DriverType = video::EDT_OPENGL;

	irr::IrrlichtDevice* device = irr::createDeviceEx(param);

	// setup a simple 3d scene

	smgr = device->getSceneManager();
	driver = device->getVideoDriver();

	scene::ICameraSceneNode* cam = smgr->addCameraSceneNode(NULL, core::vector3df(5,5,10),
																core::vector3df(0,0,0));
	cam->setTarget(core::vector3df(10,10,0));

	//scene::ISceneNodeAnimator* anim = smgr->createFlyCircleAnimator(core::vector3df(0,15,0), 30.0f);
	//cam->addAnimator(anim);
	//anim->drop();

	for (int y = 0; y < 10; y++)
	{
		for (int x = 0; x < 10; x++)
		{
			//scene::ISceneNode* DotSceneNode = smgr->addCubeSceneNode(1, NULL, IDFILTER, 
			//												core::vector3df(4*x,4*y,0));

			scene::ISceneNode* DotSceneNode = smgr->addSphereSceneNode(.5, 8, NULL, IDFILTER, 
															core::vector3df(6*x,6*y,0));

			DotSceneNode->getMaterial(0).AmbientColor.set(128, 0, 0, 255);
			DotSceneNode->setMaterialFlag( video::EMF_LIGHTING, true );
			DotSceneNode->setMaterialFlag( video::EMF_NORMALIZE_NORMALS, true);
		}
	}

	smgr->addSkyBoxSceneNode(
	driver->getTexture("../../media/irrlicht2_up.jpg"),
	driver->getTexture("../../media/irrlicht2_dn.jpg"),
	driver->getTexture("../../media/irrlicht2_lf.jpg"),
	driver->getTexture("../../media/irrlicht2_rt.jpg"),
	driver->getTexture("../../media/irrlicht2_ft.jpg"),
	driver->getTexture("../../media/irrlicht2_bk.jpg"));


	// Lights:
	scene::ILightSceneNode* pIrrLight1 = smgr->addLightSceneNode(0, core::vector3df(0.7,0.4,0.6),
													video::SColorf(0.6f, 0.6f, 0.6f));
	pIrrLight1->getLightData().Type = video::ELT_DIRECTIONAL;
	pIrrLight1->getLightData().AmbientColor = video::SColorf(0.6f, 0.6f, 0.6f);

	scene::ILightSceneNode* pIrrLight2 = smgr->addLightSceneNode(0, core::vector3df(-0.4,-0.7,-0.3),
													video::SColorf(0.2f, 0.2f, 0.2f));
	pIrrLight2->getLightData().Type = video::ELT_DIRECTIONAL;


	// show and execute dialog

	ShowWindow(hWnd , SW_SHOW);
	UpdateWindow(hWnd);

	// do message queue

	// Instead of this, you can also simply use your own message loop
	// using GetMessage, DispatchMessage and whatever. Calling
	// Device->run() will cause Irrlicht to dispatch messages internally too.
	// You need not call Device->run() if you want to do your own message
	// dispatching loop, but Irrlicht will not be able to fetch
	// user input then and you have to do it on your own using the window
	// messages, DirectInput, or whatever.

	for (int i = 0; i < 100; i++)
	{
		driver->beginScene(true, true, 0);
		smgr->drawAll();
		driver->endScene();

		cam->setTarget(core::vector3df(i/10.0,i/10.0,2));
		cam->setPosition(core::vector3df(15.0-i/10.0,15.0-i/10.0,8));
		cam->setFarValue(10000000.0f);
		cam->setScale(core::vector3df(1.0,1.0,1.0));

		Sleep(1);
	}


	MSG msg;
	while (device->run())
	{
		driver->beginScene(true, true, 0);
		smgr->drawAll();
		driver->endScene();

		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		Sleep(1);
	}

	// the alternative, own message dispatching loop without Device->run() would
	// look like this:

	/*MSG msg;
	while (true)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);

			if (msg.message == WM_QUIT)
				break;
		}

		// advance virtual time
		device->getTimer()->tick();

		// draw engine picture
		driver->beginScene(true, true, 0);
		smgr->drawAll();
		driver->endScene();
	}*/

	device->closeDevice();
	device->drop();

	return 0;
}
If you try this you will see the Irrlicht mouse coordinates offset bug at getSceneNodeFromScreenCoordinatesBB.
cvf
Posts: 7
Joined: Wed May 16, 2007 12:39 pm

Post by cvf »

I have found the solution to this problem. I posted it at:
http://irrlicht.sourceforge.net/phpBB2/ ... 895#117895

The problem is the line: cam->setFarValue(10000000.0f);
mybrainisfull
Posts: 15
Joined: Sun Jul 29, 2007 4:51 pm
Location: State College, PA

I had the same problem

Post by mybrainisfull »

I am using the triangle selector method for collision detection of mouse clicks with scene objects. I think the default ray from screen coordinates gives a ray which is about 20 pixels below where the mouse click occurred. It turns out that changing the input to the function getRayFromScreenCoordinates from

Code: Select all

getRayFromScreenCoordinates(device->getCursorControl()->getPosition(),camera) 

to

Code: Select all

getRayFromScreenCoordinates(position2d<s32>(device->getCursorControl()->getPosition().X,device->getCursorControl()->getPosition().Y+1),camera)
fixes the problem.
Let me know if I can be of any more help
Post Reply