2
\$\begingroup\$

I'm fairly new to transformation matrices. I'm using them to translate and rotate nodes and their child nodes. The following code works when applied once, but shows strange behavior when applied many times in a row:

void Node::update() {
    Mat4 parentTrans = (haveParent ? parent->getTransform() : Mat4::Identity());
    Mat4 thisTrans = this->getTransform();

    Mat4 translate = Mat4::Translation(this->getPosition());
    Mat4 rotate(this->getRotation());
    Mat4 result = parentTrans * thisTrans * translate * rotate;

    this->setPosition(result.getTranslation());
    this->setRotation(result.getRotation());
}

This works for a somewhat complex test case: a parent node w/ one child node, the child node is offset from the parent node, the parent node is rotated, and the child node is has its own local rotation. In this case, the drawing looks correct.

I'm running into problems when applying the above multiple times. For example, when I'm animating a rotating node (changing the rotation just a bit on each frame). Here, the position of the node becomes huge and it just zooms back to some very large position.

Any help or advice on how to address this is greatly appreciated.

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

You seem to be stacking matrix calculations. The "node original transform sources" should be kept pristine while calculating "node in the graph stuff".

Assuming your original code looks a bit like this:

bool haveParent;
Node* parent;

Mat4 rotation;  // or Quat
Mat4& getRotation() const { return rotation; }

Vec3 position; 
Vec3& getPosition() const { return position; }

Mat4 Node::getTransform() {
  return getTranslation() * getRotation();
}

const Vec3& 

void Node::update() {
  Mat4 parentTrans = (this->haveParent ? this->parent->getTransform() : Mat4::Identity());
  Mat4 thisTrans = this->getTransform();

  Mat4 translate = Mat4::Translation(this->getPosition());
  Mat4 rotate(this->getRotation());
  Mat4 result = parentTrans * thisTrans * translate * rotate;

  this->setPosition(result.getTranslation());
  this->setRotation(result.getRotation());
}

You would probably need something like this:

// Assume the attributes are private and the methods are public.
Node* mParent; // nullptr on initialization

Mat4 mThisNodeRotation;  // or Quat
Mat4& getThisNodeRotation() const { return mThisNodeRotation; }

Vec3 mThisNodePosition; 
Vec3& getThisNodePosition() const { return mThisNodePosition; }

Mat4 mWorldRotation;    // Rotation computed with the parent's ancestries
Mat4 mWorldPosition; // Translation computed with the parent's ancestries

const Mat4& getWorldRotation()    const { return mWorldRotation; }
      void  setWorldRotation( const Mat4& aValue ) { mWorldRotation = aValue; }

const Vec3& getWorldPosition() const { return mWorldPosition; }
      void  setWorldPosition( const Vec3& aValue ) { mWorldPosition = aValue; }

const Mat4& getWorldTransform()   const { return mWorldPosition * mWorldRotation; }

Mat4 Node::getThisNodeTransform() {
  return getTranslation() * getThisNodeRotation();
}

void Node::update() {
  Mat4 parentTrans = (this->parent ? this->parent->getWorldTransform() : Mat4::Identity());

  Mat4 worldResult = parentTrans * getThisNodePosition() * getThisNodeRotation();

  this->setWorldPosition(result.getTranslation());
  this->setWorldRotation(result.getRotation());
}

I have not tested the code, but it's been done with nearly the best of my knowledge.

When computing the final position of a node, you really have to keep a wall between the node's data, and what's being applied to the scene graph: when computing the position in the scene graph, it must not change the node's original settings (of course, it's the position w.r.t. the parent, but this does not need to be modified during the update and the calculation of the final location).

My coding standards have been applied, please disregard as I'm becoming an old monkey.

\$\endgroup\$
1
\$\begingroup\$

Looks like including thisTrans in the calculation of result was incorrect. Updated code is:

Mat4 result = parentTrans * translate * rotate;

Thanks to everyone asking/answering other transformation matrix related questions as they were very helpful.

\$\endgroup\$
1
  • \$\begingroup\$ If this answered your question, you can mark it as the accepted answer, even if it's your own! \$\endgroup\$
    – Vaillancourt
    Jan 29, 2016 at 14:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .