Pick a Center (Open Inventor)

Hi,

I am using a code in which the model is rotated instead of the camera. Here is the code:


// Rotate geometry around an arbitrary point
// Use leftMouse to rotate, press any key to reset transform.

#include <Inventor/Xt/SoXt.h>
#include <Inventor/Xt/viewers/SoXtExaminerViewer.h>

#include <Inventor/nodes/SoCube.h>
#include <Inventor/nodes/SoDrawStyle.h>
#include <Inventor/nodes/SoEventCallback.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoSphere.h>
#include <Inventor/nodes/SoTransform.h>

#include <Inventor/events/SoKeyboardEvent.h> 
#include <Inventor/events/SoMouseButtonEvent.h> 
#include <Inventor/events/SoLocation2Event.h> 
#include <Inventor/projectors/SbSphereSheetProjector.h> 

#include <Inventor/Win/devices/SoWinKeyboard.h> 
#include <Inventor/Win/devices/SoWinMouse.h> 

/////////////////////////////////////////////////////////////

// Global state used by callback functions
// (TODO: Make an application state class)
//
// buttonDown    - Tracks state of mouse button (up or down)
// gpViewer      - Need viewer to get current viewport
//                 (changes if window is resized)
// gpTransform   - Geometry transform node to modify
// gpSphereSheet - Sphere sheet projector to convert mouse
//                 motion into 3D rotation

static bool                    buttonDown    = false;
static SoXtExaminerViewer     *gpViewer      = NULL;
static SoTransform            *gpTransform   = NULL;
static SbSphereSheetProjector *gpSphereSheet = NULL;

// Handle key press events
void keyEvent( const SoEvent *event )
{
  // On any key press, reset geometry transformation
  gpTransform->rotation.setValue( SbVec3f(0,0,1), 0 );
}

// Handle mouse press/release and move events
void mouseEvent( const SoEvent *pEvent )
{
  // If first time, create the sphere sheet projector.
  // In this case we can use a simple orthographic view volume.
  if (! gpSphereSheet) {
    SbViewVolume vv;
    vv.ortho( -1, 1, -1, 1, -10, 10 );
    gpSphereSheet = new SbSphereSheetProjector;
    gpSphereSheet->setViewVolume( vv );
    gpSphereSheet->setSphere( SbSphere(SbVec3f(0, 0, 0), .7f) );
  }

  // Get the event position in normalized coordinates [0..1]
  SbVec2f pos = pEvent->getNormalizedPosition( gpViewer->getViewportRegion() );

  // Update the sphere sheet projector and get incremental rotation.
  SbRotation rot;
  gpSphereSheet->projectAndGetRotation( pos, rot );

  // On mouse press and release just remember the state change
  if (SO_MOUSE_PRESS_EVENT( pEvent, BUTTON1 )) {
    buttonDown = true;
  }
  else if (SO_MOUSE_RELEASE_EVENT( pEvent, BUTTON1 )) {
    buttonDown = false;
  }

  // Mouse move (only interesting if button is down)
  else {
    if (buttonDown) {
      // Set center of rotation to desired point on geometry
      gpTransform->recenter(SbVec3f(1,1,1));
      // Concatenate additional rotation
      gpTransform->rotation = gpTransform->rotation.getValue() * rot;
    }
  }
}

/////////////////////////////////////////////////////////////

// Classes to translate raw events into SoEvents
static SoXtKeyboard *gpKeybDevice  = NULL;
static SoXtMouse    *gpMouseDevice = NULL;

SbBool rawEventCB( void *userData, XAnyEvent *anyevent )
{
  // First time create the event translation objects
  if (! gpMouseDevice) {
    gpKeybDevice  = new SoWinKeyboard();
    gpMouseDevice = new SoWinMouse();
  }
  // If this translates to any kind of mouse event, handle it.
  // (Mouse device needs to window size to invert Y coords.)
  gpMouseDevice->setWindowSize( gpViewer->getViewportRegion().getViewportSizePixels() );
  const SoEvent *pMouseEvent = gpMouseDevice->translateEvent( anyevent );
  if (pMouseEvent) {
    mouseEvent( pMouseEvent );
    return TRUE;
  }
  // Else if this translates to any kind of keyboard event, handle it
  const SoEvent *pKeyEvent = gpKeybDevice->translateEvent( anyevent );
  if (pKeyEvent) {
    keyEvent( pKeyEvent );
    return TRUE;
  }
  return FALSE;
}

/////////////////////////////////////////////////////////////

void
main(int argc, char **argv)
{
  HWND myWindow = SoXt::init(argv[0]);

  // "Markers" to show where 0,0,0 and 1,1,1 are
  SoTransform *pTrn2 = new SoTransform();
  SoMaterial  *pMat1 = new SoMaterial();
  SoMaterial  *pMat2 = new SoMaterial();
  SoSphere    *pSph1 = new SoSphere();
  SoSphere    *pSph2 = new SoSphere();
  pTrn2->translation  = SbVec3f(1,1,1);
  pMat1->diffuseColor = SbColor(1,0,0);
  pMat2->diffuseColor = SbColor(0,1,0);
  pSph1->radius = 0.1f;
  pSph2->radius = 0.1f;
  SoSeparator *pMarkSep = new SoSeparator();
  pMarkSep->addChild( pMat1 );
  pMarkSep->addChild( pSph1 );
  pMarkSep->addChild( pMat2 );
  pMarkSep->addChild( pTrn2 );
  pMarkSep->addChild( pSph2 );

  // Geometry
  SoTransform *pTrn3 = new SoTransform();
  SoMaterial  *pMat3 = new SoMaterial();
  SoCube      *pCube = new SoCube();
  SoDrawStyle *pStyl = new SoDrawStyle();
  pMat3->diffuseColor = SbColor(0,0,1);
  pStyl->style = SoDrawStyle::LINES;
  pStyl->lineWidth = 3;
  SoSeparator *pCubeSep = new SoSeparator();
  pCubeSep->addChild( pMat3 );
  pCubeSep->addChild( pStyl );
  pCubeSep->addChild( pTrn3 );
  pCubeSep->addChild( pCube );

  // Assemble scenegraph
  SoSeparator *pRoot = new SoSeparator();
  pRoot->addChild( pMarkSep );
  pRoot->addChild( pCubeSep );

  // Create viewer
  SoXtExaminerViewer *pViewer = new SoXtExaminerViewer(myWindow);
  pViewer->setSceneGraph( pRoot );
  pViewer->setBackgroundColor( SbColor(0,0.2f,0.2f) );
  pViewer->setTitle( "LMoust to rotate, any key to reset..." );
  pViewer->show();

  // Global variables used in event callback
  gpViewer      = pViewer;  // Viewer (to get viewport etc)
  gpTransform   = pTrn3;    // Geometry transform to modify

  // Catch raw system events before they're handled by the viewer
  pViewer->setEventCallback( rawEventCB, NULL );

  // Loop, then cleanup
  SoXt::show(myWindow);
  SoXt::mainLoop();
  delete pViewer;
  SoXt::finish();

I want to change the code so that when right click on the model, the center of rotation changes to that point. Currently I’m using the following code in mouseEvent for that purpose:


if (SO_MOUSE_PRESS_EVENT( pEvent, BUTTON2 ))
  pos = pEvent->getNormalizedPosition( gpViewer->getViewportRegion() )
  SoRayPickAction *rpa = new SoRayPickAction( gpViewer->getViewportRegion() )
  rpa->setNormalizedPoint( pos )
  rpa.apply(gpViewer->getSceneManager()->getSceneGraph())
  SoPickedPoint *pickedPoint = rpa->getPickedPoint()

  if (pickedPoint != NULL)
    gpTransform->recenter(pickedPoint.getPoint())

But it does not work well, i.e. it rotates around another unknown point.

Do you any better way to pick the point?

Thanks in advance,
artmansoft