Code: Select all
#include <irrlicht.h>
#pragma comment(lib, "Irrlicht.lib")
using namespace irr;
// 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);
}
class MyEventReceiver : public IEventReceiver
{
public:
MyEventReceiver(IrrlichtDevice* device)
: Device(device)
, Selected(0)
{
}
virtual ~MyEventReceiver()
{
if (Selected)
Selected->drop();
}
virtual bool OnEvent(SEvent event)
{
if (event.EventType == irr::EET_KEY_INPUT_EVENT &&
event.KeyInput.Key == irr::KEY_ESCAPE &&
!event.KeyInput.PressedDown)
{
Device->closeDevice();
return true;
}
if (event.EventType == irr::EET_MOUSE_INPUT_EVENT &&
event.MouseInput.Event == irr::EMIE_LMOUSE_LEFT_UP)
{
scene::ISceneManager* smgr = Device->getSceneManager();
gui::ICursorControl* cursor = Device->getCursorControl();
if (Selected)
Selected->drop();
Selected =
smgr->getSceneCollisionManager()->getSceneNodeFromScreenCoordinatesBB(cursor->getPosition());
if (Selected)
Selected->grab();
return true;
}
return false;
}
IrrlichtDevice* Device;
scene::ISceneNode* Selected;
};
int main()
{
// ask user for driver
video::E_DRIVER_TYPE driverType =
video::EDT_DIRECT3D8;
// 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.
MyEventReceiver receiver(device);
device->setEventReceiver(&receiver);
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
// load the scene
// rotate the root by 45 deg
scene::ISceneNode* root = smgr->getRootSceneNode();
root->setRotation(core::vector3df(0, 45, 0));
u32 n;
for (n = 0; n < 5; ++n)
{
scene::ISceneNode* cube = smgr->addCubeSceneNode();
cube->setMaterialTexture(0, driver->getTexture("../../media/particlewhite.bmp"));
cube->setMaterialFlag(video::EMF_LIGHTING, false);
core::vector3df scale(1.f, 1.f, 1.f);
scale *= (n + 1) / 5.f;
cube->setScale(scale);
core::vector3df position(-7.5f, 0.f, (n+1) * 15.f);
cube->setPosition(position);
}
for (n = 0; n < 5; ++n)
{
scene::ISceneNode* cube = smgr->addCubeSceneNode(10.f * (n + 1) / 5.f);
cube->setMaterialTexture(0, driver->getTexture("../../media/particlewhite.bmp"));
cube->setMaterialFlag(video::EMF_LIGHTING, false);
core::vector3df position(7.5f, 0.f, (n+1) * 15.f);
cube->setPosition(position);
}
scene::IAnimatedMesh* tile = smgr->addHillPlaneMesh("floor", core::dimension2df(15, 15), core::dimension2di(1, 1));
for (n = 0; n < 5; ++n)
{
scene::ISceneNode* floor = smgr->addAnimatedMeshSceneNode(tile);
floor->setMaterialTexture(0, driver->getTexture("../../media/particlered.bmp"));
floor->setMaterialFlag(video::EMF_LIGHTING, false);
core::vector3df position(-7.5f, 0.f, (n+1) * 15.f);
floor->setPosition(position);
}
for (n = 0; n < 5; ++n)
{
scene::ISceneNode* floor = smgr->addAnimatedMeshSceneNode(tile);
floor->setMaterialTexture(0, driver->getTexture("../../media/particlered.bmp"));
floor->setMaterialFlag(video::EMF_LIGHTING, false);
core::vector3df position(7.5f, 0, (n+1) * 15.f);
floor->setPosition(position);
}
// add camera
scene::ICameraSceneNode* camera = smgr->addCameraSceneNode();
camera->setTarget(core::vector3df(20, 0, 30));
camera->setPosition(core::vector3df(0, 25, 0));
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();
if (receiver.Selected)
ISceneNode_drawObjectBoundingBox(receiver.Selected, driver);
driver->endScene();
}
}
}
device->drop();
return 0;
}
I actually caused this bug when fixing a problem with picking accuracy for rotated scene nodes [see this]. A simple, but less accurate, solution is to transform the nodes bounding box into world coordinates using transformBoxEx(). That works okay as long as you aren't picking objects that have been rotated.
Another option is to transform each corner of the box back to world coordinates before doing the distance test. This is an accurate solution, and it only creates additional expense if the bounding box tests passes. Here is the change to getPickedNodeBB()...
Code: Select all
// do intersection test in object space
if (box.intersectsWithLine(line))
{
box.getEdges(edges);
f32 distance = 0.0f;
// find distance to farthest edge of bounding box in world space
// note that ray is already in world space, and we convert each
// edge into world space before testing.
for (s32 e=0; e<8; ++e)
{
current->getAbsoluteTransformation().transformVect(edges[e]);
f32 t = edges[e].getDistanceFromSQ(ray.start);
if (t > distance)
distance = t;
}
if (distance < outbestdistance)
{
outbestnode = current;
outbestdistance = distance;
}
}