// 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();