/**************************************************************************\
*
* This file is part of the Coin 3D visualization library.
* Copyright (C) 1998-2007 by Systems in Motion. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* ("GPL") version 2 as published by the Free Software Foundation.
* See the file LICENSE.GPL at the root directory of this source
* distribution for additional information about the GNU GPL.
*
* For using Coin with software that can not be combined with the GNU
* GPL, and for taking advantage of the additional benefits of our
* support services, please contact Systems in Motion about acquiring
* a Coin Professional Edition License.
*
* See http://www.coin3d.org/ for more information.
*
* Systems in Motion, Postboks 1283, Pirsenteret, 7462 Trondheim, NORWAY.
* http://www.sim.no/ sales@sim.no coin-support@coin3d.org
*
\**************************************************************************/
/*!
\class SoTransform SoTransform.h Inventor/nodes/SoTransform.h
\brief The SoTransform class is the "all-purpose" transformation node type.
\ingroup nodes
Like SoMatrixTransform, nodes of this type gives the application
programmer maximum flexibility when specifying geometry
transformations in a scene graph. If you want to set and keep the
various components of the transformation matrix in separate
entities, this node type is preferable, though.
The order of operations is: first scaling is done, then rotation,
then translation.
FILE FORMAT/DEFAULTS:
\code
Transform {
translation 0 0 0
rotation 0 0 1 0
scaleFactor 1 1 1
scaleOrientation 0 0 1 0
center 0 0 0
}
\endcode
*/
// *************************************************************************
#include
#include
#include
#include
#include
#if COIN_DEBUG
#include
#endif // COIN_DEBUG
// *************************************************************************
/*!
\var SoSFVec3f SoTransform::translation
The translation part of the transformation.
*/
/*!
\var SoSFRotation SoTransform::rotation
The rotation part of the transformation.
Note that there is one \e very common mistake that is easy to make
when setting the value of a an SoSFRotation field, and that is to
inadvertently use the wrong SbRotation constructor. This example
should clarify the problem:
\code
mytransformnode->rotation.setValue(0, 0, 1, 1.5707963f);
\endcode
The programmer clearly tries to set a PI/2 rotation around the Z
axis, but this will fail, as the SbRotation constructor invoked
above is the one that takes as arguments the 4 floats of a \e
quaternion. What the programmer almost certainly wanted to do was to
use the SbRotation constructor that takes a rotation vector and a
rotation angle, which is invoked like this:
\code
mytransformnode->rotation.setValue(SbVec3f(0, 0, 1), 1.5707963f);
\endcode
*/
/*!
\var SoSFVec3f SoTransform::scaleFactor
The scaling part of the transformation.
*/
/*!
\var SoSFRotation SoTransform::scaleOrientation
The orientation the object is set to before scaling.
*/
/*!
\var SoSFVec3f SoTransform::center
The center point for the rotation.
*/
// *************************************************************************
SO_NODE_SOURCE(SoTransform);
/*!
Constructor.
*/
SoTransform::SoTransform(void)
{
SO_NODE_INTERNAL_CONSTRUCTOR(SoTransform);
SO_NODE_ADD_FIELD(translation, (0.0f, 0.0f, 0.0f));
SO_NODE_ADD_FIELD(rotation, (SbRotation(SbVec3f(0.0f, 0.0f, 1.0f), 0.0f)));
SO_NODE_ADD_FIELD(scaleFactor, (1.0f, 1.0f, 1.0f));
SO_NODE_ADD_FIELD(scaleOrientation, (SbRotation(SbVec3f(0.0f, 0.0f, 1.0f), 0.0f)));
SO_NODE_ADD_FIELD(center, (0.0f, 0.0f, 0.0f));
}
/*!
Destructor.
*/
SoTransform::~SoTransform()
{
}
// Doc from superclass.
void
SoTransform::initClass(void)
{
SO_NODE_INTERNAL_INIT_CLASS(SoTransform, SO_FROM_INVENTOR_1|SoNode::VRML1);
}
/*!
Sets the transformation to translate to \a frompoint, with a rotation
so that the (0,0,-1) vector is rotated into the vector from \a frompoint
to \a topoint.
*/
void
SoTransform::pointAt(const SbVec3f & frompoint, const SbVec3f & topoint)
{
this->scaleFactor = SbVec3f(1.0f, 1.0f, 1.0f);
this->center = SbVec3f(0.0f, 0.0f, 0.0f);
this->scaleOrientation = SbRotation(SbVec3f(0.0f, 0.0f, 1.0f), 0.0f);
this->translation = frompoint;
SbVec3f dir = topoint - frompoint;
if (dir.normalize() != 0.0f) {
SbRotation rot(SbVec3f(0.0f, 0.0f, -1.0f), dir);
this->rotation = rot;
}
#if COIN_DEBUG
else {
SoDebugError::postWarning("SoTransform::pointAt",
"frompt == topoint");
}
#endif // COIN_DEBUG
}
/*!
Calculates the matrices to/from scale space.
*/
void
SoTransform::getScaleSpaceMatrix(SbMatrix & mat, SbMatrix & inv) const
{
SbMatrix tmp;
mat.setTranslate(-center.getValue());
tmp.setRotate(scaleOrientation.getValue().inverse());
mat.multRight(tmp);
tmp.setScale(scaleFactor.getValue());
mat.multRight(tmp);
inv = mat.inverse();
}
/*!
Calculates the matrices to/from rotation space.
*/
void
SoTransform::getRotationSpaceMatrix(SbMatrix & mat, SbMatrix & inv) const
{
SbMatrix tmp;
mat.setTranslate(-this->center.getValue());
tmp.setRotate(this->scaleOrientation.getValue().inverse());
mat.multRight(tmp);
tmp.setScale(this->scaleFactor.getValue());
mat.multRight(tmp);
tmp.setRotate(this->scaleOrientation.getValue());
mat.multRight(tmp);
tmp.setRotate(this->rotation.getValue());
mat.multRight(tmp);
inv = mat.inverse();
}
/*!
Calculates the matrices to/from translation space.
*/
void
SoTransform::getTranslationSpaceMatrix(SbMatrix & mat, SbMatrix & inv) const
{
SbMatrix tmp;
mat.setTranslate(-this->center.getValue());
tmp.setRotate(this->scaleOrientation.getValue().inverse());
mat.multRight(tmp);
tmp.setScale(this->scaleFactor.getValue());
mat.multRight(tmp);
tmp.setRotate(this->scaleOrientation.getValue());
mat.multRight(tmp);
tmp.setRotate(this->rotation.getValue());
mat.multRight(tmp);
tmp.setTranslate(this->translation.getValue());
mat.multRight(tmp);
inv = mat.inverse();
}
/*!
Premultiplies this transformation by \a mat.
*/
void
SoTransform::multLeft(const SbMatrix & mat)
{
SbMatrix matrix;
matrix.setTransform(this->translation.getValue(),
this->rotation.getValue(),
this->scaleFactor.getValue(),
this->scaleOrientation.getValue(),
this->center.getValue());
matrix.multLeft(mat);
this->setMatrix(matrix);
}
/*!
Postmultiplies this transformation by \a mat.
*/
void
SoTransform::multRight(const SbMatrix & mat)
{
SbMatrix matrix;
matrix.setTransform(this->translation.getValue(),
this->rotation.getValue(),
this->scaleFactor.getValue(),
this->scaleOrientation.getValue(),
this->center.getValue());
matrix.multRight(mat);
this->setMatrix(matrix);
}
/*!
Premultiplies this transformation by the transformation in \a nodeonright.
*/
void
SoTransform::combineLeft(SoTransformation * nodeonright)
{
SoGetMatrixAction ma(SbViewportRegion(100,100));
ma.apply(nodeonright);
this->multLeft(ma.getMatrix());
}
/*!
Postmultiplies this transformation by the transformation in \a nodeonleft.
*/
void
SoTransform::combineRight(SoTransformation * nodeonleft)
{
SoGetMatrixAction ma(SbViewportRegion(100,100));
ma.apply(nodeonleft);
this->multRight(ma.getMatrix());
}
/*!
Sets the fields to create a transformation equal to \a mat.
*/
void
SoTransform::setMatrix(const SbMatrix & mat)
{
SbVec3f t,s,c = this->center.getValue();
SbRotation r, so;
mat.getTransform(t,r,s,so,c);
this->translation = t;
this->rotation = r;
this->scaleFactor = s;
this->scaleOrientation = so;
}
/*!
Sets the \e center field to \a newcenter. This might affect one
or more of the other fields.
*/
void
SoTransform::recenter(const SbVec3f & newcenter)
{
SbMatrix matrix;
matrix.setTransform(this->translation.getValue(),
this->rotation.getValue(),
this->scaleFactor.getValue(),
this->scaleOrientation.getValue(),
this->center.getValue());
SbVec3f t,s;
SbRotation r, so;
matrix.getTransform(t, r, s, so, newcenter);
this->translation = t;
this->rotation = r;
this->scaleFactor = s;
this->scaleOrientation = so;
this->center = newcenter;
}
// Doc from superclass.
void
SoTransform::doAction(SoAction * action)
{
SbMatrix matrix;
matrix.setTransform(this->translation.getValue(),
this->rotation.getValue(),
this->scaleFactor.getValue(),
this->scaleOrientation.getValue(),
this->center.getValue());
SoModelMatrixElement::mult(action->getState(), this, matrix);
}
// Doc from superclass.
void
SoTransform::GLRender(SoGLRenderAction * action)
{
SoTransform::doAction((SoAction *)action);
}
// Doc from superclass.
void
SoTransform::getBoundingBox(SoGetBoundingBoxAction * action)
{
SoTransform::doAction((SoAction *)action);
}
// Doc from superclass.
void
SoTransform::getMatrix(SoGetMatrixAction * action)
{
SbMatrix m;
m.setTransform(this->translation.getValue(),
this->rotation.getValue(),
this->scaleFactor.getValue(),
this->scaleOrientation.getValue(),
this->center.getValue());
action->getMatrix().multLeft(m);
SbMatrix mi = m.inverse();
action->getInverse().multRight(mi);
}
// Doc from superclass.
void
SoTransform::callback(SoCallbackAction * action)
{
SoTransform::doAction((SoAction *)action);
}
// Doc from superclass.
void
SoTransform::pick(SoPickAction * action)
{
SoTransform::doAction((SoAction *)action);
}
// Doc from superclass. Overrides the traversal method in this class for
// the SoGetPrimitiveCountAction because the number of primitives can
// be different depending on scene location (and thereby distance to
// camera) if there are e.g. SoLOD nodes in the scene.
void
SoTransform::getPrimitiveCount(SoGetPrimitiveCountAction * action)
{
SoTransform::doAction((SoAction *)action);
}