Gizmo SceneNode

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.
Post Reply
Digan
Posts: 16
Joined: Tue Aug 24, 2010 11:21 am

Gizmo SceneNode

Post by Digan »

Please help me to write a class of gizmo-manipulator.

cgizmoscenenode.cpp

Code: Select all

#include "cgizmoscenenode.h"

CGizmoSceneNode::CGizmoSceneNode(IrrlichtDevice *device, scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id)
        : scene::ISceneNode(parent, mgr, id)
{

    #ifdef _DEBUG
    setDebugName("CGizmoSceneNode");
    #endif

    setAutomaticCulling(scene::EAC_OFF);

    Device = device;
    CollMan = SceneManager->getSceneCollisionManager();

    IAnimatedMesh* arrowMeshRed = SceneManager->addArrowMesh( "x_axisArrow",video::SColor(255, 255, 0, 0),video::SColor(255, 255, 0, 0));
    scene::ISceneNode* nodeX = SceneManager->addMeshSceneNode(arrowMeshRed, this, x_axis);
    if(nodeX)
    {
        nodeX->setMaterialFlag(video::EMF_LIGHTING, false);
        nodeX->setRotation(vector3df(0,0,-90));
        nodeX->setScale(vector3df(10,30,10));
    }


    IAnimatedMesh* arrowMeshGreen = SceneManager->addArrowMesh( "y_axisArrow",video::SColor(255, 0, 255, 0),video::SColor(255, 0, 255, 0));
    scene::ISceneNode* nodeY = SceneManager->addMeshSceneNode(arrowMeshGreen, this, y_axis);
    if(nodeY)
    {
        nodeY->setMaterialFlag(video::EMF_LIGHTING, false);
        nodeY->setScale(vector3df(10,30,10));
    }


    IAnimatedMesh* arrowMeshBlue = SceneManager->addArrowMesh( "z_axisArrow",video::SColor(255, 0, 0, 255),video::SColor(255, 0, 0, 255));
    scene::ISceneNode* nodeZ = SceneManager->addMeshSceneNode(arrowMeshBlue, this, z_axis);
    if(nodeZ)
    {
        nodeZ->setMaterialFlag(video::EMF_LIGHTING, false);
        nodeZ->setRotation(vector3df(90,0,0));
        nodeZ->setScale(vector3df(10,30,10));
    }

    setDebugDataVisible(EDS_BBOX);
}

CGizmoSceneNode::~CGizmoSceneNode(void)
{
}

void CGizmoSceneNode::OnRegisterSceneNode()
{
    if (IsVisible)
    {
        SceneManager->registerNodeForRendering(this);
    }

    ISceneNode::OnRegisterSceneNode();
}

void CGizmoSceneNode::render()
{
    currentAxix = CollMan->getSceneNodeFromScreenCoordinatesBB(Device->getCursorControl()->getPosition(), x_axis | y_axis | y_axis, true);
}

const core::aabbox3d<f32>& CGizmoSceneNode::getBoundingBox() const
{
    return Box;
}

ISceneNode* CGizmoSceneNode::getCurrentAxis()
{
    return currentAxix;
}
MyEventReceiver.cpp

Code: Select all

#include "MyEventReceiver.h"

MyEventReceiver::MyEventReceiver(IrrlichtDevice *device)
{
	Device = device;
	CollMan = Device->getSceneManager()->getSceneCollisionManager(); 
	LMouseButtonDown  = 0;
        SelectedNode      = 0;

        gizmo = new CGizmoSceneNode(Device, Device->getSceneManager()->getRootSceneNode(), Device->getSceneManager());
}

MyEventReceiver::~MyEventReceiver(void)
{
}

bool MyEventReceiver::OnEvent(const SEvent& event)
{
   switch(event.EventType)
   {
      case EET_MOUSE_INPUT_EVENT :
      {
		  switch (event.MouseInput.Event)
                  {
                  case EMIE_LMOUSE_PRESSED_DOWN :
                      {
                          LMouseButtonDown = true;

                          SelectedNode = CollMan->getSceneNodeFromScreenCoordinatesBB(Device->getCursorControl()->getPosition(), 0,true);

                          if(SelectedNode)
                          {
                              initialCursorPosition = Device->getCursorControl()->getPosition();
                              initialObjectPosition = CollMan->getScreenCoordinatesFrom3DPosition(SelectedNode->getAbsolutePosition(), Device->getSceneManager()->getActiveCamera());
                          }

                      }
                      break;
                  case EMIE_LMOUSE_LEFT_UP :
                      {
                          LMouseButtonDown = false;
                          return false;
                      }
                      break;

                  case EMIE_MOUSE_MOVED :
                      {
                          if (!LMouseButtonDown)
                              return false;

                          if (SelectedNode)
                          {
                              vector3df p = SelectedNode->getPosition();

                              plane3df const planeXZ(SelectedNode->getAbsolutePosition(), vector3df(0.f, 1.f, 0.f));

                              position2di currentCursorPosition(Device->getCursorControl()->getPosition());
                              position2di effectiveObjectPosition = initialObjectPosition + currentCursorPosition - initialCursorPosition;
                              line3df ray(CollMan->getRayFromScreenCoordinates(effectiveObjectPosition, Device->getSceneManager()->getActiveCamera()));
                              vector3df intersectWithPlane;

                              if(planeXZ.getIntersectionWithLine(ray.start, ray.getVector(), intersectWithPlane))
                              {
                                  SelectedNode->setPosition(intersectWithPlane);
                              }

                          }
                      }
                      break;
		  default:
			  break;
		  }
      }
      break;

      default:
         break;
   }
   return 0;
}

main.cpp

Code: Select all

#include <irrlicht.h>
#include "MyEventReceiver.h"

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;


int main()
{
   IrrlichtDevice *device = createDevice(video::EDT_OPENGL,core::dimension2d<u32>(800, 600), 32, false, false, false, 0);
    if (!device)
                return 1;

   MyEventReceiver receiver(device);
   device->setEventReceiver(&receiver);


   IVideoDriver *driver      = device->getVideoDriver();
   ISceneManager *smgr      = device->getSceneManager();
   IGUIEnvironment *guienv      = device->getGUIEnvironment();

   device->setWindowCaption(L"Gizmo Test");

   ICameraSceneNode *camera = smgr->addCameraSceneNode(0, vector3df(50,30,-40), vector3df(0,5,0));

   ISceneNode* node = smgr->addCubeSceneNode(20, 0);
   if (node)
   {
      node->setMaterialFlag(EMF_LIGHTING, false);
      node->setMaterialTexture( 0, driver->getTexture("wall.jpg") );
      node->setMaterialFlag(EMF_WIREFRAME, true);
   }

   while(device->run())
   {

      driver->beginScene(true, true, SColor(255,100,101,140));
        smgr->drawAll();
        guienv->drawAll();
        driver->endScene();
    }

   device->drop();

   return 0;
}
Application looks like this:
Image

I have a few questions.

1. How to attach the cube to the gizmo? (setParent method not working correctly)
2. How to restrict the movement of the arrows for only one axis?
3. Can this be done in scene node class? Not in EventReceiver class.
4. How to make so that if one moves arrow, then would move all arrows? All arrows it is childrens of empty scene node.

Thanks
Best regard
Digan
Image
Radikalizm
Posts: 1215
Joined: Tue Jan 09, 2007 7:03 pm
Location: Leuven, Belgium

Post by Radikalizm »

1. Protip: Just saying "it's not working correctly" won't do a thing, please describe what is not working
2. Just use node picking to select an axis, and only manipulate the position vector component which that axis is representing
3. I'd implement this in a custom animator instead of cramming it into the scene node code
4. You could create a small sphere connect to the base of all 3, then when the sphere is picked you could do free movement
Digan
Posts: 16
Joined: Tue Aug 24, 2010 11:21 am

Post by Digan »

Thanks for reply.
Radikalizm wrote:1. Protip: Just saying "it's not working correctly" won't do a thing, please describe what is not working
Bug with this:

Code: Select all

                  case EMIE_LMOUSE_PRESSED_DOWN :
                      {
                          LMouseButtonDown = true;

                          SelectedNode = CollMan->getSceneNodeFromScreenCoordinatesBB(Device->getCursorControl()->getPosition(), 0,true);

                          if(SelectedNode)
                          {
                              initialCursorPosition = Device->getCursorControl()->getPosition();
                              initialObjectPosition = CollMan->getScreenCoordinatesFrom3DPosition(SelectedNode->getAbsolutePosition(), Device->getSceneManager()->getActiveCamera());

                              if((SelectedNode->getID() != center_axis) && (SelectedNode->getID() != x_axis) && (SelectedNode->getID() != y_axis) && (SelectedNode->getID() != z_axis))
                              {
                                  gizmo->setPosition(SelectedNode->getAbsolutePosition());
                                  SelectedNode->setParent(gizmo);
                              }
                          }

                      }
Gizmo does not fall into the center of the object or disappears.
4. You could create a small sphere connect to the base of all 3, then when the sphere is picked you could do free movement
No. Base of all it is empty scene node. Free movement until needed.

For example, I pull the red arrow. The remaining arrows must follow.
How to do it? If parent of all arrows is an empty scene node. During movement to reassign the parent?

Maybe write an algorithm of movement of objects by means Gizmo?
Image
Digan
Posts: 16
Joined: Tue Aug 24, 2010 11:21 am

Post by Digan »

Please test this sample.

gizmoTest.7z

I would be grateful for advice on fixing bugs.
Image
Radikalizm
Posts: 1215
Joined: Tue Jan 09, 2007 7:03 pm
Location: Leuven, Belgium

Post by Radikalizm »

First of all, you're setting your relative gizmo position before you set the parent, which means that as soon as you set its parent it will still hold that relative position (so it didn't disappear, it just moved off-screen)

And why do you insist on using an empty scene node as a base for the gizmo?

I can't really decipher your last sentences, so I don't completely understand what you're asking there
Digan
Posts: 16
Joined: Tue Aug 24, 2010 11:21 am

Post by Digan »

Radikalizm wrote: And why do you insist on using an empty scene node as a base for the gizmo?
Now only three arrows.
I can't really decipher your last sentences, so I don't completely understand what you're asking there
I asked for help to fix bugs.
Now the position of the arrows at the beginning of the movement is not correct.
Over the first sentence, I think.

Sorry. I do not know much english.
Image
Radikalizm
Posts: 1215
Joined: Tue Jan 09, 2007 7:03 pm
Location: Leuven, Belgium

Post by Radikalizm »

You can fix the position by setting the empty scene node's position to (0,0,0) and then setting the object you're trying to move as its parent (a node's position is only relative to its parent's position)
Digan
Posts: 16
Joined: Tue Aug 24, 2010 11:21 am

Post by Digan »

Thanks for reply.

Now the position of the arrows at the movement is correct.

But I still have a few questions.

1. How to make the scale of the axis was always the same? Relative to the camera.

Code: Select all

 vector3d<f32> CamFwd  = vector3d<f32>((GizmoSceneNode->getAbsolutePosition()) - (Device->getSceneManager()->getActiveCamera()->getPosition())).normalize();
 vector3d<f32> subPos = GizmoSceneNode->getPosition() - Device->getSceneManager()->getActiveCamera()->getPosition();
f32 ScaleFactor = vector3d<f32>(subPos).dotProduct(CamFwd);
 ScaleFactor = ScaleFactor * 0.017;

GizmoSceneNode->setScale(gizmoInitScale * ScaleFactor);
This example does not work correctly.

2. How to make axes were always in front of the selected object?

That is like so:
Image

But not so:
Image

How to put the axis to the front of the selected object?

Current version: gizmoTest.7z

Thanks
Image
Radikalizm
Posts: 1215
Joined: Tue Jan 09, 2007 7:03 pm
Location: Leuven, Belgium

Post by Radikalizm »

This would be a bit harder

One possibility would be to determine the 2D screen coordinates of the object you're trying to move, and then rendering your gizmo on a fixed plane in front of your camera based on these coordinates

Another method would be to scale the gizmo according to the distance between the camera and the selected object, but I think this would be somewhat harder than the first approach

To render the gizmo in front of the object you could draw the gizmo after everything else in a separate pass
Digan
Posts: 16
Joined: Tue Aug 24, 2010 11:21 am

Post by Digan »

Many thanks.
To render the gizmo in front of the object you could draw the gizmo after everything else in a separate pass
Do not quite understand. Can be an example?
Image
Radikalizm
Posts: 1215
Joined: Tue Jan 09, 2007 7:03 pm
Location: Leuven, Belgium

Post by Radikalizm »

I'll explain you the process:

First of all you draw all your scene elements, without drawing the gizmo, you could do this by using a second scene manager where the first manager holds all your scene data, and the second one holds the gizmo and any other stuff you want to draw on top of the scene
Draw everything in the first scene manager, then after that you can draw everything in the second scene manager (disabling writing to the z-buffer)

You could do this in a single pass, but this is a lot cleaner and easier to manage, and you can use it to your benefit because your actual scene will be completely isolated in its own scene manager, while everything editor-related will be isolated in another
Digan
Posts: 16
Joined: Tue Aug 24, 2010 11:21 am

Post by Digan »

Many thanks :)

It work.

Image

My Gizmo Example: gizmoTest.7z

It remains to scale the axes.
Image
Post Reply