"picking" an 3D object from a mouse click

You discovered a bug in the engine, and you are sure that it is not a problem of your code? Just post it in here. Please read the bug posting guidelines first.
Post Reply
radubolovan
Posts: 60
Joined: Tue Nov 13, 2007 7:03 pm
Location: Bucharest - Romania
Contact:

"picking" an 3D object from a mouse click

Post by radubolovan »

I am developping a 3D online chess game ...
The camera does not move or rotate.
So ... for a test I'm using this code:

Code: Select all

bool DSChessScene::handleEvent( const SEvent& event )
{
	if( ( event.EventType == EET_MOUSE_INPUT_EVENT ) && ( event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN ) )
	{
		core::position2d< s32 > cursorPos = device->getCursorControl()->getPosition();

		ISceneNode* node = smgr->getSceneCollisionManager()->getSceneNodeFromScreenCoordinatesBB( cursorPos );
		if( node )
		{
			node->setVisible( false );
			return true;
		}
	}
	return false;
}
This function is called from EventReciever class.
The problem is that the getSceneNodeFromScreenCoordinatesBB function dows not work properly :(
Here is a snapshot:
http://dsflake.darkstarlinux.ro/~radubo ... hess01.png
As you can see an object was found and has become invisible, but not the one I was clicked.
Addition informations, I am developing this game in linux and using OpenGL.
radubolovan
Posts: 60
Joined: Tue Nov 13, 2007 7:03 pm
Location: Bucharest - Romania
Contact:

Post by radubolovan »

I did some tests:
I wrote a function adapted from

Code: Select all

core::line3d<f32> CSceneCollisionManager::getRayFromScreenCoordinates( core::position2d<s32> pos, ICameraSceneNode* camera)
:

Code: Select all

void transform2dTo3d( core::position2d< s32 > const& pos2d, core::vector3df& pos )
{
	const scene::SViewFrustum* f = camera->getViewFrustum();

	core::vector3df farLeftUp = f->getFarLeftUp();
	core::vector3df lefttoright = f->getFarRightUp() - farLeftUp;
	core::vector3df uptodown = f->getFarLeftDown() - farLeftUp;

	core::rect<s32> viewPort = driver->getViewPort();
	core::dimension2d<s32> screenSize(viewPort.getWidth(), viewPort.getHeight());

	f32 dx = ( pos2d.X / (f32)screenSize.Width );
	f32 dy = ( pos2d.Y / (f32)screenSize.Height );

	pos = f->cameraPosition + (lefttoright * (dx-0.5f)) + (uptodown * (dy-0.5f));
}
between beginFrame and endFrame:

Code: Select all

core::position2d< s32 > cursorPos = device->getCursorControl()->getPosition();

	const scene::SViewFrustum* f = camera->getViewFrustum();

	core::vector3df farLeftUp = f->getFarLeftUp();
	core::vector3df farLeftDown = f->getFarLeftDown();
	core::vector3df farRightUp = f->getFarRightUp();
	core::vector3df farRightDown = f->getFarRightDown();

	core::matrix4 mat;
	driver->setTransform( ETS_WORLD, mat );

	driver->draw3DLine( farLeftUp, farLeftDown, SColor( 255, 255, 0, 0 ) );
	driver->draw3DLine( farLeftUp, farRightUp, SColor( 255, 255, 0, 0 ) );
	driver->draw3DLine( farRightUp, farRightDown, SColor( 255, 255, 0, 0 ) );
	driver->draw3DLine( farLeftDown, farRightDown, SColor( 255, 255, 0, 0 ) );

	core::vector3df start = vector3df( 0.0f, 5.0f, 0.0f );
	core::vector3df end = vector3df( 0.0f, 0.0f, 0.0f );
	driver->draw3DLine( start, end );

	transform2dTo3d( cursorPos, start );
	driver->draw3DLine( start, end, SColor( 255, 255, 255, 0 ) );
Then after guiEnv->drawAll(); here some code:

Code: Select all

core::position2d< s32 > cursorPos = device->getCursorControl()->getPosition();
	core::rect<s32> rt( cursorPos.X - 5, cursorPos.Y - 5, cursorPos.X + 5, cursorPos.Y + 5 );
	driver->draw2DRectangle( video::SColor( 255, 255, 255, 255 ), rt );
And here is the result:
http://dsflake.darkstarlinux.ro/~radubo ... hess02.png
problem no1: the lines that describe the farPlane of frustum should be red ... look at the code
problem no2: only in left and bottom I can see the lines ... who knows where are the others
problem no3: the image explains all (the little white rectangle is the cursor of the mouse)
Here's how I set the camera:

Code: Select all

scene::ICameraSceneNode camera = smgr->addCameraSceneNode();
camera->setNearValue( 0.1f );
camera->setFarValue( 1000.0f );
camera->setPosition( 0.0f, 7.0f, -7.0f );
camera->setTarget( 0.0f, 2.0f, 0.0f );
So ... there are serious bugs in cameraSceneNode (I think) ... maybe the viewFrustum of the camera doesn't update properly ...
radubolovan
Posts: 60
Joined: Tue Nov 13, 2007 7:03 pm
Location: Bucharest - Romania
Contact:

Post by radubolovan »

I've put sone printf code and here is coordinates:
camera pos: ( 0, 7, -7 )
camera target: ( 0, 2, 0 )
farLeftUp: ( -968.972, 16.9763, 1229.35 )
farLeftDown: ( -968.972, -1165.75, 384.54 )
farRightUp: ( 968.972, 16.9763, 1229.35 )
farRightDown: ( 968.972, -1165.75, 384.54 )
Very strange!!! The Z coordinate shouldn't be equal to camera->setFarValue() ? And shoulden't be the same to all vertices?
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

Picking works just fine here...

Code: Select all

#include <irrlicht.h>
using namespace irr;

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

class MyEventReceiver : public IEventReceiver 
{ 
public: 
   MyEventReceiver(scene::ISceneManager* smgr) 
      : SceneManager(smgr) 
      , 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 shift is down and mouse clicked, then do collision test 
      if (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; 
public: 
   scene::ISceneNode* SelectedNode; 
}; 


// 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); 
} 

int main() 
{ 
  // create device and exit if creation failed 

  IrrlichtDevice* device = 
    createDevice(video::EDT_DIRECT3D8, core::dimension2d<s32>(1024, 768)); 

  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);

  // load the scene 
  scene::IMesh* mesh = 0;
  
  scene::IAnimatedMesh* dwarf = smgr->getMesh("../../media/sydney.md2");
  if (dwarf)
    mesh = dwarf->getMesh(0);

  if (!mesh)
  {
    device->drop();
    return 0;
  }

  for (u32 i = 0; i < 2; ++i)
  {
    for (u32 j = 0; j < 10; ++j)
    {
      const core::vector3df pos (10.f * j, 0.f, 10.f * i);
      const core::vector3df scl (.25f, .25f, .25f);

      scene::ISceneNode* node = smgr->addMeshSceneNode(mesh, 0, -1, pos);
      node->setScale (scl);

      const core::vector3df rot (0.f, 1.f * rand() / RAND_MAX, 0.f);

      scene::ISceneNodeAnimator* animator = smgr->createRotationAnimator(rot);
        node->addAnimator(animator);
      animator->drop();

      node->setMaterialFlag(video::EMF_LIGHTING, false);
      node->setMaterialTexture(0, driver->getTexture("../../media/sydney.bmp"));
    }
  }


  // add a user controlled camera 

  scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0, core::vector3df (50, 50, 50), core::vector3df (50, 0, 10));

  // and draw everything. 

  while(device->run()) 
  { 
    if (driver->beginScene(true, true, video::SColor(0,200,200,200))) 
    { 
      smgr->drawAll(); 

      ISceneNode_drawObjectBoundingBox(receiver.SelectedNode, driver);

      driver->endScene(); 
    } 
  } 

  device->drop(); 
  
  return 0; 
} 
radubolovan
Posts: 60
Joined: Tue Nov 13, 2007 7:03 pm
Location: Bucharest - Romania
Contact:

Post by radubolovan »

I copied and pasted your code and runed it.
1) I use Linux & OpenGL so I made this change:

Code: Select all

IrrlichtDevice* device = createDevice( video::EDT_OPENGL, core::dimension2d<s32>(1024, 768));
2) I can't see any mesh :( although it's loading!!!


anyway ... I have found a solution for my problem ... here are 3 functions:

Code: Select all

void mulVectByMatrix( core::matrix4 const& mat, core::vector3df& v )
{
	core::vector3df vect = v;

	v.X = mat[0]*vect.X + mat[4]*vect.Y + mat[8]*vect.Z + mat[12];
	v.Y = mat[1]*vect.X + mat[5]*vect.Y + mat[9]*vect.Z + mat[13];
	v.Z = mat[2]*vect.X + mat[6]*vect.Y + mat[10]*vect.Z + mat[14];
}

void transform2dTo3dCameraSpace( float x, float y, core::vector3df& pos )
{
	scene::ICameraSceneNode* camera = smgr->getActiveCamera();

	core::rect<s32> viewPort = driver->getViewPort();

	pos.X = ( x / viewPort.getWidth() ) * 2.0f - 1.0f;
	pos.Y = ( 1.0f - y / viewPort.getHeight() ) * 2.0f - 1.0f;

	core::matrix4 mat = camera->getProjectionMatrix();
	mat.makeInverse();

	mulVectByMatrix( mat, pos );
}

void transform2dTo3dWorldSpace( float x, float y, core::vector3df& pos )
{
	transform2dTo3dCameraSpace( x, y, pos );

	scene::ICameraSceneNode* camera = smgr->getActiveCamera();

	core::matrix4 mat = camera->getViewMatrix();
	mat.makeInverse();

	mulVectByMatrix( mat, pos );
}
There are working just fine :)
Post Reply