Version 1 (modified by osg, 7 years ago)

Initial copy of NPS tutorial

Positioning a Camera with a User-Defined Matrix

Goal

Manually position a camera to view a scene.

Overview

To position a camera manually, we can use the viewer class setViewByMatrix() method. This call should be placed between the viewer.update and viewer.frame calls within the simulation loop. The following guidelines are useful:

  • Producer and all classes below osgGA::MatrixManipulator (in terms of abstraction) use 'Y' up coordinates. All others -including the Viewer class matrix manipulators - use 'Z' up.
  • The inverse of a position/orientation matrix can be used to orient a camera.

With these guidelines, the only steps required to position a camera manually are:

  1. Create and initialize a matrix with the correct world position and orientation.
  2. Get the inverse of this matrix and...
  3. Provide a world up orientation. In this case by rotating from 'Y' up to 'Z' up.

The code

Set up position & orientation of matrix

To setup a matrix we can use the methods of the osg::Matrix class. In this case we’ll use a double-precision type, or osg::Matrixd. To position and orient this matrix, we can use the Matrix class methods makeTranslate() and makeRotate(). Both of these methods have a number of overloads for convenience. For this example we’ll use the version of makeRotate() that takes 3 degree/vector pairs. Rotation will be by the number degrees (in radians) about the axis specified by the vector. For translation we’ll use the version that simply takes x, y and z values. We’ll create a single matrix that represents a rotation and translation by post-multiplying the rotation matrix by the translation matrix. The code to achieve this is as follows.

Here's the code to set up a scene. The scene has a small terrain model and a tank at position (10,10,8).

    int main()
    {

       osg::Node* groundNode = NULL;
       osg::Node* tankNode = NULL;
       osg::Group* root = new osg::Group();
       osgProducer::Viewer viewer;
       osg::PositionAttitudeTransform* tankXform;

       groundNode = osgDB::readNodeFile("\\Models\\JoeDirt\\JoeDirt.flt");
       tankNode = osgDB::readNodeFile("\\Models\\T72-Tank\\T72-tank_des.flt");

       // Create green Irish sky
       osg::ClearNode* backdrop = new osg::ClearNode;
       backdrop->setClearColor(osg::Vec4(0.0f,0.8f,0.0f,1.0f));
       root->addChild(backdrop);
       root->addChild(groundNode);

       tankXform = new osg::PositionAttitudeTransform();

       root->addChild(tankXform);
       tankXform->addChild(tankNode);
       tankXform->setPosition( osg::Vec3(10,10,8) );
       tankXform->setAttitude( 
          osg::Quat(osg::DegreesToRadians(-45.0), osg::Vec3(0,0,1) ) );

       viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);
       viewer.setSceneData( root );

       viewer.realize();

Declare and set a matrix to be used for the camera. Set the position of this matrix to be 60 units behind and 7 units above the tank model. We'll also set the orientation of the matrix.

       osg::Matrixd myCameraMatrix;

       osg::Matrixd cameraRotation;
       osg::Matrixd cameraTrans;
       cameraRotation.makeRotate(
          osg::DegreesToRadians(-20.0), osg::Vec3(0,1,0), // roll
          osg::DegreesToRadians(-15.0), osg::Vec3(1,0,0) , // pitch
          osg::DegreesToRadians( 10.0), osg::Vec3(0,0,1) ); // heading 

       // 60 meters behind and 7 meters above the tank model
       cameraTrans.makeTranslate( 10,-50,15 );

       myCameraMatrix = cameraRotation * cameraTrans;

Using the matrix to position the viewer’s camera

Using osgProducer::Viewer default settings, a call to a viewer instance’s update() method results in the viewer’s cameraGroup instance calling its setViewByMatrix method. Normally the argument to this method will be the inverse of the current matrix manipulator’s (trackball, drive, or fly) matrix. One way to change this behavior is to manually set the viewer’s matrix immediately after the viewer update() method is called. To use the camera position/orientation matrix we created above to manually position and orient the Viewer’s camera matrix, we need to first invert the matrix.
In addition to inverting the matrix, we also need to provide world orientation. Normally osgGA::MatrixManipulator matrices (used by osgProducer::Viewer class) represent a ‘Z’ up coordinate system. Producer and osg::Matrix class matrices (like those created above) normally represent a ‘Y’ up coordinate system. So after inverting the matrix from above, we also need to provide a rotation from ‘Y’ up to ‘Z’ up. This can be done by rotating -90 degrees about the x-axis. The code to do this is as follows:

       while( !viewer.done() )
       {
          viewer.sync();
          viewer.update(); 
          if (manuallyPlaceCamera)
          {
             osg::Matrixd i = myCameraMatrix.inverse(myCameraMatrix);
             viewer.setViewByMatrix( ( 
                Producer::Matrix(i.ptr() ))
                * Producer::Matrix::rotate( -M_PI/2.0, 1, 0, 0 ) );
          }
          viewer.frame();
       }

Note: Pressing the 'v' key switches to the manual camera.