Rotate a custom node around a pivot point

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
MartinVee
Posts: 139
Joined: Tue Aug 02, 2016 3:38 pm
Location: Québec, Canada

Rotate a custom node around a pivot point

Post by MartinVee »

I'm trying to rotate a custom node around a pivot point (an arbitrary point in 3D, not another node). I searched the forum and I found a few examples on how to do this. As I understood it, I need to move the node to the pivot point, rotate the node, and then move it back to it's position. I must be doing something wrong because it's not working at all. The rotation is still happening on the center of the node.

My custom node is based on a billboard. The render function does the following :

Code: Select all

 
/**
 * @brief Irrlicht's overload render() - Renders this object.
 */
void render()
{
  video::IVideoDriver* driver = SceneManager->getVideoDriver();
 
  if(driver != nullptr)
  {
    driver->setMaterial(_Material);
 
    // render all
    core::matrix4 mat = getAbsoluteTransformation();
    vector3df position = getAbsolutePosition();
    position.X += (getSize().Width / 2);
    position.Y -= (getSize().Height / 2);
    mat.setTranslation(position);
 
    driver->setTransform(video::ETS_WORLD, mat);
 
    driver->drawIndexedTriangleList(_Vertices, 4, _Indices, 2);
 
    driver->setTransform(video::ETS_WORLD, irr::core::IdentityMatrix);
  }
}
 
As you can see, I'm translating the matrix to half the size of the node. I'm doing this for the 2D wrapper I'm still writing, so I can treat the node's position as a 2D point (top-left) instead of a 3d position (center of a node).

I tried to override the setRotation() function by doing the following :

Code: Select all

 
void setRotation(const vector3df & rotation)
{
  vector3df oldPos = getPosition();
 
  setPosition(vector3df(oldPos.X + getPivotPoint().X, oldPos.Y + getPivotPoint().Y, oldPos.Z));
 
  BaseClass::setRotation(rotation);
 
  setPosition(oldPos);
 
But the result stays the same.

I suspect that my rendering code, and particularly the fact that I compute the matrix each time, is responsible for that. Or I may have understood the concept poorly.

Can someone point me in the right direction? How should I rotate my custom node using a custom pivot point?

Thanks!
devsh
Competition winner
Posts: 2057
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK
Contact:

Re: Rotate a custom node around a pivot point

Post by devsh »

in order to rotate some stuff lets say vertices v_i around a point P we do

Rotate(v_i - P) + P

where your rotation function can be based off matrices or quaternions, whatever you fancy
MartinVee
Posts: 139
Joined: Tue Aug 02, 2016 3:38 pm
Location: Québec, Canada

Re: Rotate a custom node around a pivot point

Post by MartinVee »

Wow, okay, I thought I understood the concept of a matrix a little more than that, but I've been working on this for the better part of the day, and I'm at a point where I'm just throwing new code at the problem just to see what sticks.

Here's my modified render function :

Code: Select all

 
void render()
{
  video::IVideoDriver* driver = SceneManager->getVideoDriver();
 
  if(driver != nullptr)
  {
    driver->setMaterial(_Material);
 
    PrepareVertices();
 
    // render all
    core::matrix4 rotationMatrix, originalMatrix, finalMatrix;
 
    vector3df position = getAbsolutePosition();
    position.X += (getSize().Width / 2);
    position.Y -= (getSize().Height / 2);
 
    rotationMatrix.setRotationCenter(_pivotPoint, _pivotPoint);
    rotationMatrix.setRotationDegrees(getAbsoluteTransformation().getRotationDegrees());
 
    originalMatrix.setTranslation(position);
    originalMatrix.setRotationDegrees(getAbsoluteTransformation().getRotationDegrees());
    originalMatrix.setScale(getAbsoluteTransformation().getScale());
 
    finalMatrix = rotationMatrix * originalMatrix;
 
    finalMatrix.setTranslation(originalMatrix.getTranslation());
    finalMatrix.setRotationDegrees(originalMatrix.getRotationDegrees());
 
    driver->setTransform(video::ETS_WORLD, finalMatrix);
 
    driver->drawIndexedTriangleList(_Vertices, 4, _Indices, 2);
 
    driver->setTransform(video::ETS_WORLD, irr::core::IdentityMatrix);
  }
}
 
Like it is, instead of rotating, the image "dances" from left to right. I had quite a lot of iterations, and nothing came close to what I'm trying to achieve.

What am I doing wrong here?
MartinVee
Posts: 139
Joined: Tue Aug 02, 2016 3:38 pm
Location: Québec, Canada

Re: Rotate a custom node around a pivot point

Post by MartinVee »

Okay, I made some progress, but I'm still stuck.

Here's what my render method looks like :

Code: Select all

 
void render()
{
  video::IVideoDriver* driver = SceneManager->getVideoDriver();
 
  if(driver != nullptr)
  {
    driver->setMaterial(_Material);
 
    PrepareVertices();
 
    // render all
    core::matrix4 finalMatrix;
 
    vector3df position = getAbsolutePosition();
    position.X += (getSize().Width / 2);
    position.Y -= (getSize().Height / 2);
 
    finalMatrix = matrix4().setTranslation(position + getPivotPoint3D()) *
      matrix4().setScale(getAbsoluteTransformation().getScale()) *
      matrix4().setRotationDegrees(getAbsoluteTransformation().getRotationDegrees()) *
      matrix4().setTranslation(position - getPivotPoint3D());
 
    driver->setTransform(video::ETS_WORLD, finalMatrix);
 
    driver->drawIndexedTriangleList(_Vertices, 4, _Indices, 2);
 
    driver->setTransform(video::ETS_WORLD, irr::core::IdentityMatrix);
  }
}
 
The problems I'm still having :
  1. When I set a pivot point centered on the object, it doesn't rotate from the center, so there's probably something off with my translations.
  2. With the previous version of my render method, all the objects had a translation of half their size to make up for the fact that the positions are used as 2D positions. This doesn't work anymore ; all objects are offseted down and right.
  3. Using 5 matrices to achieve this seems highly inefficient to me.
Can someone please help me and explain to me what I've been doing wrong? Thanks a lot!
kornwaretm
Competition winner
Posts: 189
Joined: Tue Oct 16, 2007 3:53 am
Location: Indonesia
Contact:

Re: Rotate a custom node around a pivot point

Post by kornwaretm »

rotation should be done when the object at position(0,0). you can create the vertices around 0,0 position, this way pivot point already determined at the center of the object. or use 0,0 as the top left corner, so you can easily use a custom pivot point, step 1 translate to the negative of your pivot point, step 2 rotate the object, step 3 translate to desired position. if you not sure try to do the 3 step manually first. if the result correct, create your transform matrix.

btw if you want to use my matrix2D class it is here http://irrlicht.sourceforge.net/forum/v ... =9&t=51662 . if your goal is 2d, you can handle vary resolution easily with it.
MartinVee
Posts: 139
Joined: Tue Aug 02, 2016 3:38 pm
Location: Québec, Canada

Re: Rotate a custom node around a pivot point

Post by MartinVee »

Thanks, kornwaretm! I finally had a breakthrough friday late in the afternoon! Too bad I haven't found your code before, it would have greatly helped me! My goal is indeed writing a wrapper of an old 2D plateform to ease the transition to 3D.

I'm gonna look at how you're doing things, and I'm gonna try to expand on the breakthrough I had. Once I have working code, I'll post it here for posterity/code review.
MartinVee
Posts: 139
Joined: Tue Aug 02, 2016 3:38 pm
Location: Québec, Canada

Re: Rotate a custom node around a pivot point

Post by MartinVee »

Okay, finally got it working! I had so much problems with this because my comprehension of the matrix usage was poor, and I had the concept of vector and position confused for some 3D operations.

Here's my final working code, if someone ever runs into the same problem.

Code: Select all

 
void CustomObject::render()
{
  video::IVideoDriver* driver = SceneManager->getVideoDriver();
 
  if(driver != nullptr)
  {
    driver->setMaterial(_Material);
 
    vector3df currentPosition;
    vector3df currentRotation;
 
    // Holds the pivot point as a vector
    vector3df pivotVector = getPivotPointVector(); // CustomObject::getPivotPointVector() returns a translation vector from the origin to the pivot point
 
    // Check if the pivot point vector is long enough to warrant all those computations.
    if(pivotVector.getLength() > 0.01f) // 0.01f might be too short to be noticable. Change to 0.5f, instead?
    {
      // Rotation/Translation computation matrix
      matrix4 rotationMatrix;
 
      // Offset the origin position by the pivot vector
      currentPosition = -(pivotVector);
 
      // Set the rotation matrix to the object's rotation value
      rotationMatrix.setRotationDegrees(getAbsoluteTransformation().getRotationDegrees());
 
      // rotate the position vector along the rotation matrix
      rotationMatrix.rotateVect(currentPosition);
 
      // Offset the absolute position by the currentPosition (which really holds a translation vector until then).
      currentPosition += getAbsolutePosition();
      currentPosition += pivotVector;
 
      // Get the rotation from the rotation matrix
      currentRotation = rotationMatrix.getRotationDegrees();
    }
    else
    {
      // Rotate on the center position
      currentRotation = getAbsoluteTransformation().getRotationDegrees();
 
      // Get the object world's position.
      currentPosition = getAbsolutePosition();
    }
 
    // Offset the position by half the width and height, so the position is consistent with other 2D engine.
    /* You may not need this code
    currentPosition.X += (getSize().Width / 2);
    currentPosition.Y -= (getSize().Height / 2);  // IV Cartesian quadrant
    */
 
    // Create the final world matrix
    core::matrix4 finalMatrix = matrix4().setTranslation(currentPosition) * matrix4().setRotationDegrees(currentRotation)  * matrix4().setScale(getAbsoluteTransformation().getScale());
 
    driver->setTransform(video::ETS_WORLD, finalMatrix);
 
    driver->drawIndexedTriangleList(_Vertices, 4, _Indices, 2);
 
    driver->setTransform(video::ETS_WORLD, irr::core::IdentityMatrix);
  }
}
 
EDIT : Updated the finalMatrix computation, because the scale wasn't working properly.
Post Reply