/*
*
* Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan/
*
*/
/*
* Copyright (C) 1990,91 Silicon Graphics, Inc.
*
_______________________________________________________________________
______________ S I L I C O N G R A P H I C S I N C . ____________
|
| $Revision: 1.1 $
|
| Classes : SoXtWalkViewer
|
| Author(s) : Alain Dumesny
|
______________ S I L I C O N G R A P H I C S I N C . ____________
_______________________________________________________________________
*/
#include <inttypes.h>
#include <math.h>
#include <X11/Intrinsic.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <Xm/LabelG.h>
#include <Xm/Form.h>
#include <Xm/PushBG.h>
#include <Sgm/ThumbWheel.h>
#include <Inventor/SoDB.h>
#include <Inventor/SoPickedPoint.h>
#include <Inventor/sensors/SoFieldSensor.h>
#include <Inventor/fields/SoSFTime.h>
#include <Inventor/nodes/SoCamera.h>
#include <Inventor/nodes/SoOrthographicCamera.h>
#include <Inventor/nodes/SoPerspectiveCamera.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/actions/SoRayPickAction.h>
#include <Inventor/Xt/SoXtCursors.h>
#include <Inventor/Xt/viewers/SoXtWalkViewer.h>
#include <Inventor/Xt/SoXtResource.h>
#include <GL/gl.h>
/*
* Defines
*/
enum ViewerModes {
PICK_MODE,
VIEW_MODE,
WALK_MODE_ACTIVE,
PAN_MODE,
PAN_MODE_ACTIVE,
TILT_MODE_ACTIVE,
SEEK_MODE,
SET_UP_MODE
};
#define REC_SIZE 12 // size of the square region on the screen
#define CROSS 12 // cross feedback size
#define TURN_SPEED 5.0
#define WALK_SPEED_FACTOR 6.0
#define WHEEL_DOLLY_FACTOR 0.5
#define WHEEL_HEIGHT_FACTOR 0.01
// Resources for labels.
typedef struct {
char *walkViewer;
char *wvPrefSheet;
} RES_LABELS;
static RES_LABELS rl;
static char *defaultLabel[]={
"Walk Viewer",
"Walk Viewer Preference Sheet",
};
////////////////////////////////////////////////////////////////////////
//
// Public constructor - build the widget right now
//
SoXtWalkViewer::SoXtWalkViewer(
Widget parent,
const char *name,
SbBool buildInsideParent,
SoXtFullViewer::BuildFlag b,
SoXtViewer::Type t)
: SoXtConstrainedViewer(
parent,
name,
buildInsideParent,
b,
t,
FALSE) // tell base class not to build just yet
//
////////////////////////////////////////////////////////////////////////
{
// In this case, render area is what the app wants, so buildNow = TRUE
constructorCommon(TRUE);
}
////////////////////////////////////////////////////////////////////////
//
// SoEXTENDER constructor - the subclass tells us whether to build or not
//
SoXtWalkViewer::SoXtWalkViewer(
Widget parent,
const char *name,
SbBool buildInsideParent,
SoXtFullViewer::BuildFlag b,
SoXtViewer::Type t,
SbBool buildNow)
: SoXtConstrainedViewer(
parent,
name,
buildInsideParent,
b,
t,
FALSE) // tell base class not to build just yet
//
////////////////////////////////////////////////////////////////////////
{
// In this case, render area may be what the app wants,
// or it may want a subclass of render area. Pass along buildNow
// as it was passed to us.
constructorCommon(buildNow);
}
////////////////////////////////////////////////////////////////////////
//
// Called by the constructors
//
// private
//
void
SoXtWalkViewer::constructorCommon(SbBool buildNow)
//
////////////////////////////////////////////////////////////////////////
{
// init local vars
mode = isViewing() ? VIEW_MODE : PICK_MODE;
createdCursors = FALSE;
walkCursor = panCursor = tiltCursor = seekCursor = upCursor = 0;
setClassName("SoXtWalkViewer");
// init animation variables
animatingFlag = FALSE;
animationSensor = new SoFieldSensor(SoXtWalkViewer::animationSensorCB, this);
addFinishCallback(SoXtWalkViewer::setFocalPointFinishCallback, this);
// Build the widget tree, and let SoXtComponent know about our base widget.
if (buildNow) {
// get resources...
SoXtResource xr(getParentWidget());
if (!xr.getResource( "walkViewer", "WalkViewer", rl.walkViewer ))
rl.walkViewer = defaultLabel[0];
if (!xr.getResource( "walkViewerPreferenceSheet","WalkViewerPreferenceSheet",rl.wvPrefSheet ))
rl.wvPrefSheet = defaultLabel[1];
// assign decoration titles
setPopupMenuString( rl.walkViewer );
setPrefSheetString( rl.wvPrefSheet );
Widget w = buildWidget(getParentWidget());
setBaseWidget(w);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Destructor.
//
// Use: public
SoXtWalkViewer::~SoXtWalkViewer()
//
////////////////////////////////////////////////////////////////////////
{
delete animationSensor;
// free the viewer cursors
if (getDisplay()) {
Display *display = getDisplay();
if (walkCursor) XFreeCursor(display, walkCursor);
if (panCursor) XFreeCursor(display, panCursor);
if (tiltCursor) XFreeCursor(display, tiltCursor);
if (seekCursor) XFreeCursor(display, seekCursor);
if (upCursor) XFreeCursor(display, upCursor);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Call the base class and sets the correct cursors on the window
//
// Use: virtual public
void
SoXtWalkViewer::setViewing(SbBool flag)
//
////////////////////////////////////////////////////////////////////////
{
if (flag == viewingFlag)
return;
// call the base class
SoXtConstrainedViewer::setViewing(flag);
switchMode(isViewing() ? VIEW_MODE : PICK_MODE);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Enables/Disable the viewer cursor on the window.
//
// Use: virtual public
void
SoXtWalkViewer::setCursorEnabled(SbBool flag)
//
////////////////////////////////////////////////////////////////////////
{
if (flag == cursorEnabledFlag)
return;
cursorEnabledFlag = flag;
if (! isViewing())
return;
Display *display = getDisplay();
Widget w = getRenderAreaWidget();
Window window = (w != NULL) ? XtWindow(w) : 0;
if (! window)
return;
updateCursor();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// redefines this to force the camera to be perspective (since
// orthographic camera don't any sense in this viewer).
//
// Use: virtual public
void
SoXtWalkViewer::setCamera(SoCamera *newCamera)
//
////////////////////////////////////////////////////////////////////////
{
if (camera == newCamera)
return;
// call parent class
SoXtConstrainedViewer::setCamera(newCamera);
// now make sure the camera is not orthographic, else switch to
// perspective.
if (camera != NULL &&
camera->isOfType(SoOrthographicCamera::getClassTypeId()))
toggleCameraType();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This is redefined to prevent the camera type from being changed.
//
// Use: virtual public
void
#ifdef DEBUG
SoXtWalkViewer::setCameraType(SoType type)
#else
SoXtWalkViewer::setCameraType(SoType)
#endif
//
////////////////////////////////////////////////////////////////////////
{
#ifdef DEBUG
if (! type.isDerivedFrom(SoPerspectiveCamera::getClassTypeId()))
SoDebugError::post("SoXtWalkViewer::setCameraType()",
"ignored - must be perspective camera");
#endif
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Call the base class and switches to the correct viewer state.
//
// Use: virtual public
void
SoXtWalkViewer::setSeekMode(SbBool flag)
//
////////////////////////////////////////////////////////////////////////
{
if (!isViewing())
return;
// call the base class
SoXtConstrainedViewer::setSeekMode(flag);
switchMode(isSeekMode() ? SEEK_MODE : VIEW_MODE);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Brings the viewer help card (called by "?" push button)
//
// Use: virtual protected
void
SoXtWalkViewer::openViewerHelpCard()
//
////////////////////////////////////////////////////////////////////////
{
// tell the base class to open the file for us
openHelpCard("SoXtWalkViewer.help");
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Process the given event to do viewing stuff
//
// Use: virtual protected
void
SoXtWalkViewer::processEvent(XAnyEvent *xe)
//
////////////////////////////////////////////////////////////////////////
{
if ( processCommonEvents(xe) )
return;
if (!createdCursors)
updateCursor();
XButtonEvent *be;
XMotionEvent *me;
XKeyEvent *ke;
XCrossingEvent *ce;
KeySym keysym;
SbVec2s raSize = getGlxSize();
switch(xe->type) {
case ButtonPress:
case ButtonRelease:
be = (XButtonEvent *)xe;
if (be->button != Button1 && be->button != Button2)
break;
locator[0] = be->x;
locator[1] = raSize[1] - be->y;
if (mode == SEEK_MODE) {
if (xe->type == ButtonPress)
seekToPoint(locator);
}
else if (mode == SET_UP_MODE) {
if (xe->type == ButtonPress) {
findUpDirection(locator);
switchMode(VIEW_MODE);
}
}
else
updateViewerMode(be->state);
break;
case KeyPress:
case KeyRelease:
ke = (XKeyEvent *)xe;
keysym = XLookupKeysym(ke, 0);
locator[0] = ke->x;
locator[1] = raSize[1] - ke->y;
if (keysym == XK_Control_L || keysym == XK_Control_R)
updateViewerMode(ke->state);
else if (keysym == XK_u && xe->type == KeyPress) {
if (isSeekMode())
setSeekMode(FALSE);
switchMode( (mode == SET_UP_MODE) ? VIEW_MODE : SET_UP_MODE );
}
break;
case MotionNotify:
me = (XMotionEvent *)xe;
locator[0] = me->x;
locator[1] = raSize[1] - me->y;
if (mode == TILT_MODE_ACTIVE)
rotateCamera();
break;
case LeaveNotify:
case EnterNotify:
ce = (XCrossingEvent *)xe;
//
// because the application might use Ctrl-key for motif menu
// accelerators we might not receive a key-up event, so make sure
// to reset any Ctrl mode if we loose focus, but don't do anything
// if Ctrl-key is not down (nothing to do) or if a mouse button
// is down (we will get another leaveNotify).
//
if (! (ce->state & ControlMask))
break;
if (ce->state & Button1Mask || ce->state & Button2Mask)
break;
if (xe->type == LeaveNotify)
switchMode(VIEW_MODE);
else
updateViewerMode(ce->state);
break;
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// sets the viewer mode based on what keys and buttons are being pressed
//
// Use: private
void
SoXtWalkViewer::updateViewerMode(unsigned int state)
//
////////////////////////////////////////////////////////////////////////
{
// ??? WARNING - this routine ONLY works because of
// ??? SoXtViewer::updateEventState() which is called for us
// ??? by SoXtViewer::processCommonEvents().
// ??? (XEvent state for button and modifier keys is not updated
// ??? until after the event is received. WEIRD)
// LEFT+MIDDLE down
if (state & Button1Mask && state & Button2Mask) {
switchMode(TILT_MODE_ACTIVE);
}
// LEFT down
else if (state & Button1Mask) {
if (state & ControlMask)
switchMode(PAN_MODE_ACTIVE);
else
switchMode(WALK_MODE_ACTIVE);
}
// MIDDLE DOWN
else if (state & Button2Mask) {
if (state & ControlMask)
switchMode(TILT_MODE_ACTIVE);
else
switchMode(PAN_MODE_ACTIVE);
}
// no buttons down...
else {
if (state & ControlMask)
switchMode(PAN_MODE);
else
switchMode(VIEW_MODE);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// switches to the specified viewer mode
//
// Use: private
void
SoXtWalkViewer::switchMode(int newMode)
//
////////////////////////////////////////////////////////////////////////
{
// assign new mode
SbBool redrawNeeded = FALSE;
int prevMode = mode;
mode = newMode;
// update the cursor
updateCursor();
// check the old viewer mode
switch (prevMode) {
case WALK_MODE_ACTIVE:
case PAN_MODE_ACTIVE:
stopAnimating();
redrawNeeded = TRUE;
break;
case TILT_MODE_ACTIVE:
redrawNeeded = TRUE;
interactiveCountDec();
break;
}
// switch to new viewer mode
switch (newMode) {
case PICK_MODE:
stopAnimating();
break;
case WALK_MODE_ACTIVE:
case PAN_MODE_ACTIVE:
startPos = locator;
startAnimating();
redrawNeeded = TRUE;
break;
case TILT_MODE_ACTIVE:
startPos = prevPos = locator;
redrawNeeded = TRUE;
interactiveCountInc();
break;
}
if (redrawNeeded)
scheduleRedraw();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// updates the viewer cursor
//
// Use: private
void
SoXtWalkViewer::updateCursor()
//
////////////////////////////////////////////////////////////////////////
{
Widget w = getRenderAreaWidget();
Display *display = w ? XtDisplay(w) : NULL;
Window window = w ? XtWindow(w) : 0;
if (! window)
return;
if (! createdCursors)
defineCursors();
// the viewer cursor are not enabled, then we don't set a new cursor.
// Instead erase the old viewer cursor.
if (! cursorEnabledFlag) {
XUndefineCursor(display, window);
return;
}
// ...else set the right cursor for the viewer mode....
switch(mode) {
case PICK_MODE:
XUndefineCursor(display, window);
break;
case VIEW_MODE:
case WALK_MODE_ACTIVE:
XDefineCursor(display, window, walkCursor);
break;
case PAN_MODE:
case PAN_MODE_ACTIVE:
XDefineCursor(display, window, panCursor);
break;
case TILT_MODE_ACTIVE:
XDefineCursor(display, window, tiltCursor);
break;
case SEEK_MODE:
XDefineCursor(display, window, seekCursor);
break;
case SET_UP_MODE:
XDefineCursor(display, window, upCursor);
break;
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Starts ongoing animation
//
// Use: private
void
SoXtWalkViewer::startAnimating()
//
////////////////////////////////////////////////////////////////////////
{
if (! animatingFlag) {
animationSensor->attach(viewerRealTime);
animationSensor->schedule();
prevAnimTime = viewerRealTime->getValue();
interactiveCountInc();
animatingFlag = TRUE;
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Stops ongoing animation (if any)
//
// Use: private
void
SoXtWalkViewer::stopAnimating()
//
////////////////////////////////////////////////////////////////////////
{
if (animatingFlag) {
animatingFlag = FALSE;
animationSensor->detach();
animationSensor->unschedule();
interactiveCountDec();
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// draws viewer feedback during a render area redraw of the scene.
//
// Use: virtual protected
void
SoXtWalkViewer::actualRedraw()
//
////////////////////////////////////////////////////////////////////////
{
// have the base class draw the scene
SoXtConstrainedViewer::actualRedraw();
// now draw the viewer feedback
if (isViewing() && (mode == WALK_MODE_ACTIVE || mode == PAN_MODE_ACTIVE
|| mode == TILT_MODE_ACTIVE)) {
setFeedbackOrthoProjection(getGlxSize());
// draw simple starting point feedback
glLineWidth(1);
glColor3ub(215,215,215);
if (mode == WALK_MODE_ACTIVE || mode == PAN_MODE_ACTIVE) {
glBegin(GL_LINE_LOOP);
glVertex2s(startPos[0] - REC_SIZE, startPos[1] - REC_SIZE);
glVertex2s(startPos[0] - REC_SIZE, startPos[1] + REC_SIZE);
glVertex2s(startPos[0] + REC_SIZE, startPos[1] + REC_SIZE);
glVertex2s(startPos[0] + REC_SIZE, startPos[1] - REC_SIZE);
glEnd();
}
else { // TILT_MODE_ACTIVE
glBegin(GL_LINES);
glVertex2s(startPos[0] - CROSS, startPos[1]);
glVertex2s(startPos[0] + CROSS, startPos[1]);
glVertex2s(startPos[0], startPos[1] - CROSS);
glVertex2s(startPos[0], startPos[1] + CROSS);
glEnd();
}
// now restore state
restoreGLStateAfterFeedback();
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Redefine this to add an extra thumbwheel on the left side to change
// the eye level.
//
// Use: virtual protected
Widget
SoXtWalkViewer::buildLeftTrim(Widget parent)
//
////////////////////////////////////////////////////////////////////////
{
int n;
Arg args[12];
// create a form to hold all the parts
Widget form = XtCreateWidget("LeftTrimForm", xmFormWidgetClass, parent, NULL, 0);
// create all the parts
buildLeftWheel(form);
// allocate extra thumbwheel
n = 0;
XtSetArg(args[n], XmNvalue, 0); n++;
XtSetArg(args[n], SgNangleRange, 0); n++;
XtSetArg(args[n], SgNunitsPerRotation, 360); n++;
XtSetArg(args[n], SgNshowHomeButton, FALSE); n++;
XtSetArg(args[n], XmNhighlightThickness, 0); n++;
XtSetArg(args[n], XmNorientation, XmVERTICAL); n++;
Widget wheel = SgCreateThumbWheel(form, NULL, args, n);
XtAddCallback(wheel, XmNvalueChangedCallback,
(XtCallbackProc) SoXtWalkViewer::heightWheelCB, (XtPointer) this);
XtAddCallback(wheel, XmNdragCallback,
(XtCallbackProc) SoXtWalkViewer::heightWheelCB, (XtPointer) this);
heightWheelVal = 0;
Widget label = XtCreateWidget("H", xmLabelGadgetClass, form, NULL, 0);
Widget butForm = buildAppButtons(form);
//
// layout
//
n = 0;
XtSetArg(args[n], XmNrightAttachment, XmNONE); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftOffset, 3); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNtopAttachment, XmNONE); n++;
XtSetValues(leftWheel, args, n);
n = 0;
XtSetArg(args[n], XmNrightAttachment, XmNONE); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftOffset, 7); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNbottomWidget, leftWheel); n++;
XtSetArg(args[n], XmNbottomOffset, 10); n++;
XtSetArg(args[n], XmNtopAttachment, XmNONE); n++;
XtSetValues(label, args, n);
n = 0;
XtSetArg(args[n], XmNrightAttachment, XmNONE); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftOffset, 3); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNbottomWidget, label); n++;
XtSetArg(args[n], XmNtopAttachment, XmNONE); n++;
XtSetValues(wheel, args, n);
n = 0;
XtSetArg(args[n], XmNrightAttachment, XmNONE); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNbottomWidget, wheel); n++;
XtSetValues(butForm, args, n);
// manage children
XtManageChild(leftWheel);
XtManageChild(label);
XtManageChild(wheel);
XtManageChild(butForm);
return form;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Redefine this routine to add some viewer specific stuff.
//
// Use: virtual protected
void
SoXtWalkViewer::createPrefSheet()
//
////////////////////////////////////////////////////////////////////////
{
// create the preference sheet shell and form widget
Widget shell, form;
createPrefSheetShellAndForm(shell, form);
// create all of the parts
Widget widgetList[10];
int num = 0;
createDefaultPrefSheetParts(widgetList, num, form);
widgetList[num++] = createSpeedPrefSheetGuts(form);
layoutPartsAndMapPrefSheet(widgetList, num, form, shell);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This routine is used to define cursors (can only be called after
// window has been realized).
//
// Use: private
void
SoXtWalkViewer::defineCursors()
//
////////////////////////////////////////////////////////////////////////
{
XColor foreground, background;
Pixmap source, mask;
Display *display = getDisplay();
Drawable d = DefaultRootWindow(display);
// set color
foreground.red = 65535;
foreground.green = foreground.blue = 0;
background.red = background.green = background.blue = 65535;
// walk cursor
source = XCreateBitmapFromData(display, d,
so_xt_walk_bits, so_xt_walk_width, so_xt_walk_height);
mask = XCreateBitmapFromData(display, d,
so_xt_walk_mask_bits, so_xt_walk_width, so_xt_walk_height);
walkCursor = XCreatePixmapCursor(display, source, mask,
&foreground, &background, so_xt_walk_x_hot, so_xt_walk_y_hot);
XFreePixmap(display, source);
XFreePixmap(display, mask);
// panning cursor
source = XCreateBitmapFromData(display, d,
so_xt_pan_bits, so_xt_pan_width, so_xt_pan_height);
panCursor = XCreatePixmapCursor(display, source, source,
&foreground, &foreground, so_xt_pan_x_hot, so_xt_pan_y_hot);
XFreePixmap(display, source);
// tilt cursor
source = XCreateBitmapFromData(display, d,
so_xt_tilt_bits, so_xt_tilt_width, so_xt_tilt_height);
mask = XCreateBitmapFromData(display, d,
so_xt_tilt_mask_bits, so_xt_tilt_width, so_xt_tilt_height);
tiltCursor = XCreatePixmapCursor(display, source, mask,
&foreground, &background, so_xt_tilt_x_hot, so_xt_tilt_y_hot);
XFreePixmap(display, source);
XFreePixmap(display, mask);
// seek cursor
source = XCreateBitmapFromData(display, d,
so_xt_target_bits, so_xt_target_width, so_xt_target_height);
seekCursor = XCreatePixmapCursor(display, source, source,
&foreground, &foreground, so_xt_target_x_hot, so_xt_target_y_hot);
XFreePixmap(display, source);
// up direction cursor
source = XCreateBitmapFromData(display, d,
so_xt_normal_vec_bits, so_xt_normal_vec_width, so_xt_normal_vec_height);
upCursor = XCreatePixmapCursor(display, source, source,
&foreground, &foreground, so_xt_normal_vec_x_hot, so_xt_normal_vec_y_hot);
XFreePixmap(display, source);
createdCursors = TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Called when we rotate the camera (tilt up/down and rotate sideway)
//
// Use: private
void
SoXtWalkViewer::rotateCamera()
//
////////////////////////////////////////////////////////////////////////
{
if (camera == NULL)
return;
SbVec2s raSize = getGlxSize();
//
// tilt the head up/down (constrained to 180 degree)
//
float mouseY = (locator[1] - prevPos[1]) / float(raSize[1]);
if (mouseY)
SoXtConstrainedViewer::tiltCamera(mouseY * M_PI);
//
// rotate the camera left/right around the Up direction (no roll alowed)
//
float mouseX = (locator[0] - prevPos[0]) / float(raSize[0]);
if (mouseX) {
float angle = - mouseX * M_PI;
SbRotation rot(upDirection, angle);
camera->orientation = camera->orientation.getValue() * rot;
}
prevPos = locator;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Called by the animation sensor to change the camera position
//
// Use: private
void
SoXtWalkViewer::doCameraAnimation()
//
////////////////////////////////////////////////////////////////////////
{
if (camera == NULL)
return;
SbVec2s raSize = getGlxSize();
//
// get time interval since last call
//
SbTime time = viewerRealTime->getValue();
float sec = float((time - prevAnimTime).getValue());
prevAnimTime = time;
// make sure to have at least a delta time for the first call.
if (sec == 0.0)
sec = 1.0/72.0;
//
// move forward/backward and sideway
//
if (mode == WALK_MODE_ACTIVE) {
// turn the camera left/right using the distance^2 which gives a nice
// gradual speedup.
float mouseX = (locator[0] - startPos[0]) / float(raSize[0]);
float angle = TURN_SPEED * (mouseX * mouseX) * sec;
if (angle != 0.0) {
if (mouseX < 0)
angle = -angle;
SbRotation rot(upDirection, -angle);
camera->orientation = camera->orientation.getValue() * rot;
}
// move forward/backward
float mouseY = (locator[1] - startPos[1]) / float(raSize[1]);
float dist = viewerSpeed * sceneSize * (mouseY * mouseY) * sec
* WALK_SPEED_FACTOR;
if (mouseY < 0)
dist = -dist;
if (dist != 0.0)
dollyCamera(dist);
}
//
// else pan in the viewer focal plane.
//
else if (mode == PAN_MODE_ACTIVE) {
// move the camera up/down/left/right using the distance^2 which
// gives a nice gradual speedup.
float mouseX = (locator[0] - startPos[0]) / float(raSize[0]);
float mouseY = (locator[1] - startPos[1]) / float(raSize[1]);
float distX = viewerSpeed * sceneHeight * (mouseX * mouseX) * sec
* WALK_SPEED_FACTOR;
float distY = viewerSpeed * sceneHeight * (mouseY * mouseY) * sec
* WALK_SPEED_FACTOR;
if (mouseX < 0)
distX = -distX;
if (mouseY < 0)
distY = -distY;
// figure out the up and right vector
SbMatrix mx;
mx = camera->orientation.getValue();
SbVec3f upVector(mx[1][0], mx[1][1], mx[1][2]);
SbVec3f rightVector(mx[0][0], mx[0][1], mx[0][2]);
// finally move the camera
if (distX)
camera->position = camera->position.getValue() + distX * rightVector;
if (distY)
camera->position = camera->position.getValue() + distY * upVector;
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Redefines this to move at the eye level (default) rather than
// directly forward.
//
// Use: virtual protected
void
SoXtWalkViewer::rightWheelMotion(float newVal)
//
////////////////////////////////////////////////////////////////////////
{
float dist = (newVal - rightWheelVal) * sceneSize * viewerSpeed
* WHEEL_DOLLY_FACTOR;
dollyCamera(dist);
rightWheelVal = newVal;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Move the camera in the eye level plane (default
// way of walking) or in the lookat direction which is an alternate way
// of walking (base class)
//
// Use: private
void
SoXtWalkViewer::dollyCamera(float dist)
//
////////////////////////////////////////////////////////////////////////
{
if (camera == NULL)
return;
// compute the eye level direction vector. This is simply
// the cross produc between the up direction and the
// camera right vector
SbMatrix mx;
mx = camera->orientation.getValue();
SbVec3f eyeLevel = upDirection.cross( SbVec3f(mx[0][0], mx[0][1], mx[0][2]) );
eyeLevel.normalize();
// move camera by distance
camera->position = camera->position.getValue() + eyeLevel * dist;
camera->focalDistance = camera->focalDistance.getValue() - dist;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This routine will pick a few times around the center of the
// screen to get an estimate of what the camera focal distance should
// be. Some viewers (like the examiner viewer) depend a resonable
// focal distance, so we need to have it point at objects in front of us.
//
// Use: private
void
SoXtWalkViewer::updateCameraFocalPoint()
//
////////////////////////////////////////////////////////////////////////
{
int numPicked = 0;
float distance = 0;
float locations[4][2] = {{.5, .3}, {.5, .7}, {.3, .5}, {.7, .5}};
SbVec3f camPos = camera->position.getValue();
SoPickedPoint *pp;
SoRayPickAction pick = SoRayPickAction(SbViewportRegion(getGlxSize()));
pick.setRadius(1.0);
pick.setPickAll(FALSE); // pick only the closest object
//
// pick around the center point, and average
// all the picks to get a more resonable value.
//
for (int i=0; i<4; i++) {
pick.setNormalizedPoint(SbVec2f(locations[i]));
pick.apply(sceneRoot);
pp = pick.getPickedPoint();
if (pp) {
distance += (pp->getPoint() - camPos).length();
numPicked++;
// only need two good picks...(faster)
if (numPicked == 2)
break;
}
}
if (numPicked > 1)
distance /= float(numPicked);
// now update the camera focal distance
if (distance != 0) {
camera->enableNotify(FALSE); // don't cause a redraw
camera->focalDistance = distance;
camera->enableNotify(TRUE);
}
}
//
// redefine those generic virtual functions
//
const char *
SoXtWalkViewer::getDefaultWidgetName() const
{ return "SoXtWalkViewer"; }
const char *
SoXtWalkViewer::getDefaultTitle() const
{ return rl.walkViewer; }
const char *
SoXtWalkViewer::getDefaultIconTitle() const
{ return rl.walkViewer; }
//
////////////////////////////////////////////////////////////////////////
// static callbacks stubs
////////////////////////////////////////////////////////////////////////
//
void
SoXtWalkViewer::animationSensorCB(void *p, SoSensor *)
{ ((SoXtWalkViewer *)p)->doCameraAnimation(); }
void
SoXtWalkViewer::heightWheelCB(Widget, SoXtWalkViewer *p, XtPointer *d)
{
if (p->camera == NULL)
return;
static SbBool firstDrag = TRUE;
SgThumbWheelCallbackStruct *data = (SgThumbWheelCallbackStruct *) d;
if (data->reason == XmCR_DRAG) {
// for the first move, invoke the start callbacks
if (firstDrag) {
p->interactiveCountInc();
firstDrag = FALSE;
}
// move the camera up/down
float d = (p->heightWheelVal - data->value) * p->sceneHeight
* p->viewerSpeed * WHEEL_HEIGHT_FACTOR;
p->camera->position = p->camera->position.getValue() + d * p->upDirection;
p->heightWheelVal = data->value;
}
else {
// reason = XmCR_VALUE_CHANGED, invoke the finish callbacks
p->interactiveCountDec();
firstDrag = TRUE;
}
}
//
// called by the viewer finish callback to update the camera focal point
//
void
SoXtWalkViewer::setFocalPointFinishCallback(void *data, SoXtViewer *)
{
SoXtWalkViewer *p = (SoXtWalkViewer *) data;
// if we are not at the end of a seek, update the camera focal distance
if (! p->isSeekMode())
p->updateCameraFocalPoint();
}