info-inventor-dev
[Top] [All Lists]

Bug: SoTransform.c++

To: info-inventor-dev@xxxxxxxxxxx
Subject: Bug: SoTransform.c++
From: Doug Roble <doug@xxxxxx>
Date: Tue, 09 Nov 2004 17:12:19 -0800
Sender: info-inventor-dev-bounce@xxxxxxxxxxx
User-agent: Mozilla Thunderbird 0.8 (X11/20040913)

We recently discovered a precision related issue with the SoTransform node and rendering to an OpenGL window. Specifically, in SoTransform::doAction there are the following lines:

    if (! rotation.isIgnored() && ! rotation.isDefault())
        SoModelMatrixElement::rotateBy(state, this, rotation.getValue());

If the rotation is very small (a couple of degrees or less), small changes
to the rotation will not change what is drawn on the screen after the
SoTransform in the Inventor tree.

What's happening is that the quaternion representation of the rotation
is converted to an axis/angle representation in SoGLModelMatrixElement.c++
in the method SoGLModelMatrixElement::rotateEltBy. This is immediately
handed to the OpenGL command glRotatef ( angle, x, y, z ) which then converts
it to a matrix and multiplies it by the current Model Matrix.

The conversion from quaternion to axis/angle uses an "acosf" function
and the conversion from axis/angle to matrix uses sin and cos. (And glRotatef
is one of those "not to be trusted" functions in OpenGL.) Precision is lost
with these trig functions near zero.

The result is that a rotation angle change from 0.020 to 0.023 doesn't
change what's drawn on the screen at all. It should.

Certainly, not that big of a deal, really, but it doesn't have to be that
way. Here's the fix. Go to SoTransform.c++ and change the above lines to:

  if (! rotation.isIgnored() && ! rotation.isDefault())
    {
      SbMatrix rot_mat;
      rotation.getValue ( rot_mat );

      SoModelMatrixElement::mult ( state, this, rot_mat );
    }

Converting a quaternion directly into a matrix is much more accurate (no
trig functions!) and probably just as fast.

Alternately, and probably more general of a solution, is to change
in SoGLModelMatrixElement.c++

void
SoGLModelMatrixElement::rotateEltBy(const SbRotation &rotation)
//
////////////////////////////////////////////////////////////////////////
{
    SbVec3f     axis;
    float       angle;

    SoModelMatrixElement::rotateEltBy(rotation);

    rotation.getValue(axis, angle);

    glRotatef(angle * (180.0 / M_PI), axis[0], axis[1], axis[2]);
}

to

void
SoGLModelMatrixElement::rotateEltBy(const SbRotation &rotation)
//
////////////////////////////////////////////////////////////////////////
{
    SbMatrix rot_mat;

    SoModelMatrixElement::rotateEltBy(rotation);

    rotation.getValue( rot_mat );

    glMultMatrixf((float *) rot_mat.getValue());
}

Respectfully submitted,

  Doug

P.S. We are using inventor-2.1.5-10, not the latest CVS image. Sorry if this has
been dealt with.

--

Doug Roble      Computer Graphics Software Contraptioneer
Digital Domain  310.314.2838

<Prev in Thread] Current Thread [Next in Thread>
  • Bug: SoTransform.c++, Doug Roble <=