Offset in Collision Detection with Ray?
Offset in Collision Detection with Ray?
Hi all,
I'm experiencing a problem with my collision detection. I am trying to select/highlight a scenenode via mouse hover and it works, but I always have a offset. It seems like the bounding box is off by a dozen pixel or something.
Does anyone have an idea what could cause this? BackBuffer?
Please help ,
gabb
I'm experiencing a problem with my collision detection. I am trying to select/highlight a scenenode via mouse hover and it works, but I always have a offset. It seems like the bounding box is off by a dozen pixel or something.
Does anyone have an idea what could cause this? BackBuffer?
Please help ,
gabb
Looks like an issue in the collision manager. The bounding box intersection test is using a poorly constructed bounding box when doing the ray-box intersection test. The following code will demonstrate...
The code uses the maya camera scene node controls, and if you press Shift+Click, you will select a node. Several bounding boxes are drawn for the selected node.
You will notice that the node selection works if you click in the area that is enclosed by the blue bounding box. Most users would expect that the green bounding box would be used for the bounding box tests, or maybe even the red box.
The issue is in the scene collision manager. It uses the nodes getTransformedBoundingBox() method to get the the bounding box for collision testing. For accurate collision testing, the ray should be transformed into object space [using the inverse of the object absolute transform] and then tested against the object space bounding box.
Travis
Code: Select all
#include <irrlicht.h>
#pragma comment(lib, "Irrlicht.lib")
using namespace irr;
class MyEventReceiver : public IEventReceiver
{
public:
MyEventReceiver(scene::ISceneManager* smgr)
: SceneManager(smgr)
, ShiftKeyDown(false)
, SelectedNode(0)
{
SceneManager->grab();
}
virtual ~MyEventReceiver()
{
SceneManager->drop();
}
// you may need to change the parameter type depending on your Irrlicht version
virtual bool OnEvent(const SEvent& event)
{
if (event.EventType == EET_KEY_INPUT_EVENT &&
event.KeyInput.Key == KEY_SHIFT)
{
ShiftKeyDown = event.KeyInput.PressedDown;
return true;
}
// if shift is down and mouse clicked, then do collision test
if (ShiftKeyDown &&
event.EventType == EET_MOUSE_INPUT_EVENT &&
event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
{
const core::position2di pos(event.MouseInput.X, event.MouseInput.Y);
SelectedNode =
SceneManager->getSceneCollisionManager()->getSceneNodeFromScreenCoordinatesBB(pos);
return true;
}
return false;
}
private:
scene::ISceneManager* SceneManager;
bool ShiftKeyDown;
public:
scene::ISceneNode* SelectedNode;
};
// draw the world aligned bounding box that completely encloses the object bounding box
void ISceneNode_drawWorldBoundingBox(scene::ISceneNode* node,
video::IVideoDriver* driver,
video::SColor color = video::SColor(255, 255, 0, 0))
{
if (!node || !driver)
return;
video::SMaterial matl;
matl.Lighting = false;
driver->setMaterial(matl);
core::matrix4 matx;
driver->setTransform(video::ETS_WORLD, matx);
core::aabbox3df bbox(node->getBoundingBox());
node->getAbsoluteTransformation().transformBoxEx(bbox);
driver->draw3DBox(bbox, color);
}
// draw the object aligned bounding box
void ISceneNode_drawObjectBoundingBox(scene::ISceneNode* node,
video::IVideoDriver* driver,
video::SColor color = video::SColor(255, 0, 255, 0))
{
if (!node || !driver)
return;
video::SMaterial matl;
matl.Lighting = false;
driver->setMaterial(matl);
driver->setTransform(video::ETS_WORLD, node->getAbsoluteTransformation());
driver->draw3DBox(node->getBoundingBox(), color);
}
// draw the transformed object bounding box
void ISceneNode_drawTransformedBoundingBox(scene::ISceneNode* node,
video::IVideoDriver* driver,
video::SColor color = video::SColor(255, 0, 0, 255))
{
if (!node || !driver)
return;
video::SMaterial matl;
matl.Lighting = false;
driver->setMaterial(matl);
core::matrix4 matx;
driver->setTransform(video::ETS_WORLD, matx);
driver->draw3DBox(node->getTransformedBoundingBox(), color);
}
int main()
{
// ask user for driver
video::E_DRIVER_TYPE driverType =
video::EDT_DIRECT3D9;
// create device and exit if creation failed
IrrlichtDevice *device = createDevice(driverType, core::dimension2d<s32>(800, 600), 32, false, false, true);
if (device == 0)
return 1; // could not create selected driver.
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
MyEventReceiver receiver(smgr);
device->setEventReceiver(&receiver);
// add camera
smgr->addCameraSceneNodeMaya();
// load pre-built scene. you may want to remove the animators so things stay still
smgr->loadScene("media/example.irr");
s32 fps = 0;
while(device->run())
{
// window is active, so render scene
if (device->isWindowActive())
{
if (driver->beginScene(true, true, video::SColor(100, 50, 50, 100)))
{
smgr->drawAll();
ISceneNode_drawWorldBoundingBox(receiver.SelectedNode, driver);
ISceneNode_drawObjectBoundingBox(receiver.SelectedNode, driver);
ISceneNode_drawTransformedBoundingBox(receiver.SelectedNode, driver);
driver->endScene();
}
}
}
device->drop();
return 0;
}
You will notice that the node selection works if you click in the area that is enclosed by the blue bounding box. Most users would expect that the green bounding box would be used for the bounding box tests, or maybe even the red box.
The issue is in the scene collision manager. It uses the nodes getTransformedBoundingBox() method to get the the bounding box for collision testing. For accurate collision testing, the ray should be transformed into object space [using the inverse of the object absolute transform] and then tested against the object space bounding box.
Travis
If you want the accurate bounding box test, the following code seems to work quite well. This is going to be slower than the original code, but at least the hit tests are accurate...
You'll have to fix the header also, but you should be able to figure that out on your own. If someone is going to add this fix to the core, maybe I could get them to add the scene node filter concept that was proposed here.
Travis
Code: Select all
//! recursive method for going through all scene nodes
void CSceneCollisionManager::getPickedNodeBB(ISceneNode* root,
const core::line3df& ray,
s32 bits,
bool bNoDebugObjects,
f32& outbestdistance,
ISceneNode*& outbestnode)
{
core::vector3df edges[8];
const core::list<ISceneNode*>& children = root->getChildren();
core::list<ISceneNode*>::Iterator it = children.begin();
for (; it != children.end(); ++it)
{
ISceneNode* current = *it;
if (current->isVisible() &&
(bNoDebugObjects ? !current->isDebugObject() : true) &&
(bits==0 || (bits != 0 && (current->getID() & bits))))
{
// get world to object space transform
core::matrix4 mat;
if (!current->getAbsoluteTransformation().getInverse(mat))
continue;
// transform vector from world space to object space
core::line3df line(ray);
mat.transformVect(line.start);
mat.transformVect(line.end);
const core::aabbox3df& box = current->getBoundingBox();
// do intersection test in object space
if (box.intersectsWithLine(line))
{
box.getEdges(edges);
f32 distance = 0.0f;
for (s32 e=0; e<8; ++e)
{
f32 t = edges[e].getDistanceFromSQ(line.start);
if (t > distance)
distance = t;
}
if (distance < outbestdistance)
{
outbestnode = current;
outbestdistance = distance;
}
}
}
getPickedNodeBB(current, ray, bits, bNoDebugObjects, outbestdistance, outbestnode);
}
}
Travis
-
- Posts: 38
- Joined: Mon Sep 25, 2006 6:31 pm
- Location: San Francisco, California
-
- Posts: 38
- Joined: Mon Sep 25, 2006 6:31 pm
- Location: San Francisco, California
-
- Posts: 38
- Joined: Mon Sep 25, 2006 6:31 pm
- Location: San Francisco, California
-
- Posts: 38
- Joined: Mon Sep 25, 2006 6:31 pm
- Location: San Francisco, California
QuantumLeap: what was the reason it stopped working for you? I have the exactly same problem. After building it from source my collision detection also stopped returning the nodes.
BTW: do you happen to have TextSceneNodes? Those disappeared for me too, but I am not sure if this is not a glitch in my .NET wrapper or whatsoever ..
BTW: do you happen to have TextSceneNodes? Those disappeared for me too, but I am not sure if this is not a glitch in my .NET wrapper or whatsoever ..
Actually, nevermind. The new changes actually make it worse - for my project that is. I used to be able to select nodes that are behind other nodes, with the new code that stopped working - apparently the *best node is now the very first node that has a collision with the ray - not the one who's been aimed at best ... if you know what I mean.
Also: I still got that offset, it has not become better at all as far as I can tell.
Here is a screenshot. In the upper right corner of the 3d screen you can see a white node - this is the currently selected node, although the mousepointer is at least one centimeter underneath it. (unfortunately mouse pointers are not included in screenshots - take my word for it).
EDIT: marked the node and mouse position
Also: I still got that offset, it has not become better at all as far as I can tell.
Here is a screenshot. In the upper right corner of the 3d screen you can see a white node - this is the currently selected node, although the mousepointer is at least one centimeter underneath it. (unfortunately mouse pointers are not included in screenshots - take my word for it).
EDIT: marked the node and mouse position
Are you absolutely sure that you replaced the Irrlicht.lib/.dll and relinked? The test code I used makes 40 randomly placed cubes and displays the picked node. It works as I would expect.
It might be useful to show the object bounding box for each of your scene nodes. If the bounding box is not accurate the picking won't work right.
If you can provide a simple testcase that shows the problem, I'd be happy to look at it for you. Something simple, not your entire app...
Travis
It has always been that way. The picked node is the node closest to the start of the ray [or camera position] that has its bounding box intersected by that ray. I didn't change that code at all. The only thing that I changed was the bounding box that is used for picking.best node is now the very first node that has a collision with the ray - not the one who's been aimed at best ...
It might be useful to show the object bounding box for each of your scene nodes. If the bounding box is not accurate the picking won't work right.
If you can provide a simple testcase that shows the problem, I'd be happy to look at it for you. Something simple, not your entire app...
Travis
-
- Posts: 38
- Joined: Mon Sep 25, 2006 6:31 pm
- Location: San Francisco, California
Gabb
I have exactly the same problem as you! Vitek's patch corrected the bounding boxes problem (I display them and the location is as should be) but now it seems the ray and not the box has an offset. Just like in your case, it's about 80 pixels under the picked node, but other then that it works perfectly.
BTW, which GUI are you using for your project? It seems really cool! Is it wxWidgets?
Vitek, yes I replaced the .dll and .lib files. Did you try a test application with the whole new SVN code?
I have exactly the same problem as you! Vitek's patch corrected the bounding boxes problem (I display them and the location is as should be) but now it seems the ray and not the box has an offset. Just like in your case, it's about 80 pixels under the picked node, but other then that it works perfectly.
BTW, which GUI are you using for your project? It seems really cool! Is it wxWidgets?
Vitek, yes I replaced the .dll and .lib files. Did you try a test application with the whole new SVN code?
It's easier to curse a candle than light the darkness
When using the SVN version I occasionally see the camera as a selected node, but I'm not seeing any issues with the pick position being off. There is obviously something that you guys are doing that my test code is not. 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.
The camera pick is a seperate issue I'm looking at. I need to find out why the camera nod is not picked with my version of Irrlicht, but it is with the SVN version. Of course that is a totally different problem, and you could eliminate it by simply setting the camera id to 0.
Also note that there is an issue with the picking of text scene nodes. The bounding box of the node is always 1x1x1, but the font appears the same size. This means that if the camera is very near to the text scene node, the node bounding box will take up most of the screen, but the text only takes up a very small area.
Travis
The camera pick is a seperate issue I'm looking at. I need to find out why the camera nod is not picked with my version of Irrlicht, but it is with the SVN version. Of course that is a totally different problem, and you could eliminate it by simply setting the camera id to 0.
Also note that there is an issue with the picking of text scene nodes. The bounding box of the node is always 1x1x1, but the font appears the same size. This means that if the camera is very near to the text scene node, the node bounding box will take up most of the screen, but the text only takes up a very small area.
Travis