[BACK]Return to pfiXformerD.C CVS log [TXT][DIR] Up to [Development] / performer / src / lib / libpfuiD

File: [Development] / performer / src / lib / libpfuiD / pfiXformerD.C (download)

Revision 1.1, Tue Nov 21 21:39:36 2000 UTC (16 years, 10 months ago) by flynnt
Branch: MAIN
CVS Tags: HEAD

Initial check-in based on OpenGL Performer 2.4 tree.
-flynnt

/*
 * WARNING! DO NOT EDIT!
 * pfiXformerD.C automatically generated from ../../lib/libpfui/pfiXformer.C
 */
/*
 * Copyright 1993, 1994, 1995, Silicon Graphics, Inc.
 * ALL RIGHTS RESERVED
 *
 * UNPUBLISHED -- Rights reserved under the copyright laws of the United
 * States.   Use of a copyright notice is precautionary only and does not
 * imply publication or disclosure.
 *
 * U.S. GOVERNMENT RESTRICTED RIGHTS LEGEND:
 * Use, duplication or disclosure by the Government is subject to restrictions
 * as set forth in FAR 52.227.19(c)(2) or subparagraph (c)(1)(ii) of the Rights
 * in Technical Data and Computer Software clause at DFARS 252.227-7013 and/or
 * in similar or successor clauses in the FAR, or the DOD or NASA FAR
 * Supplement.  Contractor/manufacturer is Silicon Graphics, Inc.,
 * 2011 N. Shoreline Blvd. Mountain View, CA 94039-7311.
 *
 * THE CONTENT OF THIS WORK CONTAINS CONFIDENTIAL AND PROPRIETARY
 * INFORMATION OF SILICON GRAPHICS, INC. ANY DUPLICATION, MODIFICATION,
 * DISTRIBUTION, OR DISCLOSURE IN ANY FORM, IN WHOLE, OR IN PART, IS STRICTLY
 * PROHIBITED WITHOUT THE PRIOR EXPRESS WRITTEN PERMISSION OF SILICON
 * GRAPHICS, INC.
 */


/*
 * pfiXformerD.C
 * --------------
 * $Revision: 1.1 $
 * $Date: 2000/11/21 21:39:36 $
 */

#include <math.h>
#include <X11/X.h>

#define PF_C_API 1 // enable C API also 

#include <Performer/pf.h>
#include <Performer/pf/pfDoubleDCS.h>
#include <Performer/pf/pfNode.h>
#include <Performer/pr/pfMemory.h>


#define _PFI_XFORMER_C_
#include <Performer/pfuiD.h>
#include <Performer/pfuiD/pfiXformerD.h>
#include <Performer/pr/pfGeoSet.h>


pfType *pfiXformerD::classType = NULL;

void
pfiXformerD::init()
{
    if (classType == NULL)
    {
	pfMemory::init();
	classType = 
	    new pfType(pfiXformerD::getClassType(), "pfiXformerD");
    }
}


pfiXformerD::pfiXformerD(void)
{
    void *arena		= pfMemory::getArena(this);
    
    setType(classType);
    xDefaultModelIndex = 0;
    xInputDChan             = NULL;
    xPosChan		   = NULL;
    xNode		   = NULL;
    xPosDCS		   = NULL;
    xMouse                 = NULL;
    xIx			   = NULL;
    xModelList = new(arena) pfList;
    xCollide = pfiNewCollideD(arena);
}

pfiXformerD::~pfiXformerD(void)
{
}

void
pfiXformerD::reset(void)
{
    int i, numModels;
    pfiInputXformD	*icx;
    pfiInputXformD::reset();
    xCurModelIndex            = xDefaultModelIndex;
    if ((xDefaultModelIndex > -1) && (xModelList->getNum() > xDefaultModelIndex))
	xIx = (pfiInputXformD*) xModelList->get(xDefaultModelIndex);
    else
	xIx = NULL;
    numModels = xModelList->getNum();
    for (i=0; i < numModels; i++)
    {
	icx = (pfiInputXformD*) xModelList->get(i);
	if (icx)
	    icx->reset();
    }

    xIx->getMotionCoordD(&(xMotionCoordD));
    xCollide->reset();
}

void
pfiXformerD::resetPosition(void)
{
    xIx->resetPosition();
    xIx->getMotionCoordD(&(xMotionCoordD));
    xCollide->setCurMotionParams(&xMotionCoordD.pos, &xMotionCoordD.prevPos, 
		xMotionCoordD.speed);
    if (xIx->isOfType(pfiInputXformDDrive::getClassType()))
    {
	double dh = ((pfiInputXformDDrive *)xIx)->getDriveHeight();
	setCollision(PFUCOLLIDE_GROUND, dh, getNode());
    }
    
    if (xPosChan)
     	pfChanViewMatD(xPosChan, xMotionCoordD.mat);

    if (xPosDCS)
	pfDoubleDCSMat(xPosDCS, pfIdentMat4d);
}

void
pfiXformerD::center(void)
{
    pfCoordd center;

    PFCOPY_VEC3(center.xyz, xDBSphere.center);
    PFSET_VEC3(center.hpr, 0.0f, 0.0f, 0.0f);
    xIx->setCoord(&center);
    xIx->getMotionCoordD(&(xMotionCoordD));
    xCollide->setCurMotionParams(&xMotionCoordD.pos, &xMotionCoordD.prevPos, 
		xMotionCoordD.speed);
    if (xIx->isOfType(pfiInputXformDDrive::getClassType()))
    {
	double dh = ((pfiInputXformDDrive *)xIx)->getDriveHeight();
	setCollision(PFUCOLLIDE_GROUND, dh, getNode());
    }
		
    if (xPosChan)
	pfChanViewMatD(xPosChan, xMotionCoordD.mat);
}

void
pfiXformerD::setModel(int _which, pfiInputXformD *model)
{
    xModelList->set(_which, model) ;
}

void
pfiXformerD::selectModel(int _which)
{
    if (_which > xModelList->getNum())
    {
	pfNotify(PFNFY_WARN, PFNFY_RESOURCE, 
	    "pfiSelectXformerDModel - index %d is more than number of models (%d)", 
	    _which, xModelList->getNum());
	return;
    }
    stop();
    xIx = (pfiInputXformD *) xModelList->get(_which) ;
}

pfiInputXformD *
pfiXformerD::getModel(int _which)
{
    if (_which > xModelList->getNum())
    {
	pfNotify(PFNFY_WARN, PFNFY_RESOURCE, 
	    "pfiGetXformerDModel - index %d is more than number of models (%d)", 
	    _which, xModelList->getNum());
	return NULL;
    }
    return (pfiInputXformD*) xModelList->get(_which) ;
}

void	
pfiXformerD::removeModelIndex(int _which)
{
    if (_which > xModelList->getNum())
    {
	pfNotify(PFNFY_WARN, PFNFY_RESOURCE, 
	    "pfiRemoveXformerDModel - index %d is more than number of models (%d)", 
	    _which, xModelList->getNum());
	return;
    }
    xModelList->set(_which, NULL);
}

void	
pfiXformerD::removeModel(pfiInputXformD *_icx)
{
    int i, numModels, found = 0;
    pfiInputXformD	*icx;
    numModels = xModelList->getNum();
    for (i=0; i < numModels; i++)
    {
	icx = (pfiInputXformD*) xModelList->get(i);
	if (icx == _icx)
	{
	    xModelList->set(i, NULL);
	    found = 1;
	    break;
	}
    }
    if (!found)
	pfNotify(PFNFY_WARN, PFNFY_RESOURCE, 
	    "pfiRemoveXformerDModel - couldn't find model 0x%p to remove.", _icx);
}

void
pfiXformerD::stop(void)
{
    pfiInputXformD::stop();
    if (xIx)
    {
	xIx->stop();
	xIx->getMotionCoordD(&(xMotionCoordD));
    }
    xCollide->setCurMotionParams(&xMotionCoordD.pos, &xMotionCoordD.prevPos, 
		xMotionCoordD.speed);
}

void
pfiXformerD::update(void)
{    

    // process input events
    if (xInputD)
	xInputD->processEvents();
	
    if (xIx)
    {
	/* update motion model with info from collide */
	xIx->setMotionCoordD(&(xMotionCoordD));
	xIx->update();
    }
    
    xIx->getMotionCoordD(&(xMotionCoordD));
}

void
pfiXformerD::setAutoInputD(pfChannel *_chan, pfuMouse *_mouse, 
    pfuEventStream *_events)
{
    xMouse  = _mouse;
    xInputDChan   = _chan;
    if (xInputD)
	xInputD->setEventStream(_events);
    pfuCollisionChan(xInputDChan);
}

void
pfiXformerD::getAutoInputD(pfChannel **_chan, pfuMouse **_mouse, 
    pfuEventStream **_events)
{
    *_mouse = xMouse;
    *_chan = xInputDChan;
    if (xInputD)
	*_events = xInputD->getEventStream();
    else
	*_events = NULL;
}

void
pfiXformerD::setMat(pfMatrix4d& _mat)
{
    int i, numModels;
    pfiInputXformD	*icx;
    
    numModels = xModelList->getNum();
    for (i=0; i < numModels; i++)
    {
	icx = (pfiInputXformD*) xModelList->get(i);
	if (icx)
	    icx->setMat(_mat);
    }
    if (xIx)
	xIx->getMotionCoordD(&(xMotionCoordD));
    xCollide->setCurMotionParams(&xMotionCoordD.pos, &xMotionCoordD.prevPos, 
		xMotionCoordD.speed);
}

void
pfiXformerD::getMat(pfMatrix4d& _mat)
{
    PFCOPY_MAT(_mat,  xMotionCoordD.mat);
}



void
pfiXformerD::setModelMat(pfMatrix4d& _mat)
{	    
    if (xIx)
    {
	xIx->setMat(_mat);
	xIx->getMotionCoordD(&(xMotionCoordD));
    }
    xCollide->setCurMotionParams(&xMotionCoordD.pos, &xMotionCoordD.prevPos, 
		xMotionCoordD.speed);
}

void
pfiXformerD::getModelMat(pfMatrix4d& _mat)
{
    if (xIx)
	xIx->getMat(_mat);
}


void
pfiXformerD::setResetCoord(pfCoordd *_coord) 
{
    int i, numModels;
    pfiInputXformD	*icx;
    
    xMotionCoordD.startPos = *_coord;
    numModels = xModelList->getNum();
    for (i=0; i < numModels; i++)
    {
	icx = (pfiInputXformD*) xModelList->get(i);
	if (icx)
	    icx->setResetCoord(_coord);
    }
	
    xUserSetMask |= _PFIX_USET_RESET_POS;
}

void
pfiXformerD::getResetCoord(pfCoordd *_coord) 
{
    *_coord = xMotionCoordD.startPos;
}
    
void
pfiXformerD::setCoord(pfCoordd *_coord)
{
    int i, numModels;
    pfiInputXformD	*icx;
    
    if (_coord == NULL)
	return;
  
    numModels = xModelList->getNum();
    for (i=0; i < numModels; i++)
    {
	icx = (pfiInputXformD*) xModelList->get(i);
	if (icx)
	    icx->setCoord(_coord);
    }
    if (xIx)
	xIx->getMotionCoordD(&(xMotionCoordD));
	
    xCollide->setCurMotionParams(&xMotionCoordD.pos, &xMotionCoordD.prevPos, 
		xMotionCoordD.speed);
}

void
pfiXformerD::setMotionLimits(double _maxSpeed, double _angularVel, double _accel)
{
    int i, numModels;
    pfiInputXformD	*icx;
    
    pfiInputXformD::setMotionLimits(_maxSpeed, _angularVel, _accel);
    numModels = xModelList->getNum();
    for (i=0; i < numModels; i++)
    {
	icx = (pfiInputXformD*) xModelList->get(i);
	if (icx)
	    icx->setMotionLimits(_maxSpeed, _angularVel, _accel);
    }
}

void
pfiXformerD::setAutoPosition(pfChannel *_chan, pfDoubleDCS *_dcs)
{ 
    if (_dcs)
	_dcs->ref(); 
    if (xPosDCS)
	xPosDCS->unref();
    xPosChan = _chan; 
    xPosDCS = _dcs; 
}

void
pfiXformerD::setBSphere(pfSphere *_sphere)
{
    int i, numModels;
    pfiInputXformD	*icx;
    
    xDBSphere = *_sphere;
    xUserSetMask |= _PFIX_USET_BSPHERE;
    
    numModels = xModelList->getNum();
    for (i=0; i < numModels; i++)
    {
	icx = (pfiInputXformD*) xModelList->get(i);
	if (icx)
	    icx->setBSphere(_sphere);
    }
}

void
pfiXformerD::setNode(pfNode *_node)
{ 
    pfBox bbox;
    pfSphere sph;
    int bound;
    double bmax=0.0f;
    
    if (_node)
	_node->ref(); 
    if (xNode)
	xNode->unref();
    xNode = _node; 
    
    // See if the user has set a bounding sphere on the node
    // handle has fixed sphere on the XformerD
    bound = _node->getBound(&sph);

    if (bound == PFBOUND_STATIC) // fixes bounding sphere
	setBSphere(&sph);
	
    if (!(xUserSetMask & _PFIX_USET_DB_LIMITS))
    {
	if (sph.radius > 0.0f)
	{
	    pfuTravCalcBBox(xNode, &bbox);
	    bmax = PF_MAX2(bbox.max[0] - bbox.min[0], 
		    PF_MAX2(bbox.max[1] - bbox.min[1], 
			bbox.max[2] - bbox.min[2]));
	}
	// see if the bbox is bogus and if so, use huge bbox
	if ((sph.radius < 0.0f) || (bmax < (sph.radius * 0.01f)) && (bmax < 1e-6f))
	{
	    // if box was bogus but sphere is ok, use sphere radius and center
	    if (sph.radius > 0.0f)
	    {
		bmax = sph.radius;
		setBSphere(&sph);
	    }
	    else
		sph.radius = PFI_BIGDB;
	    PFSET_VEC3(bbox.min, sph.center[0] - sph.radius, 
		sph.center[1] - sph.radius, 
		sph.center[2] - sph.radius);
	    PFSET_VEC3(bbox.max,  sph.center[0] + sph.radius, 
		sph.center[1] + sph.radius, 
		sph.center[2] + sph.radius);
	}
	_setDBLimits(&bbox);
    }
}

void
pfiXformerD::_setDBLimits(pfBox *_dbLimits)
{
    int i, numModels;
    pfiInputXformD	*icx;
    
    if (!_dbLimits)
	return;

    pfiInputXformD::_setDBLimits(_dbLimits); // udpates the BSphere
    numModels = xModelList->getNum();
    for (i=0; i < numModels; i++)
    {
	icx = (pfiInputXformD*) xModelList->get(i);
	if (icx)
	    icx->_setDBLimits(_dbLimits);
    }
}

void
pfiXformerD::setCollision(int _mode, double _val, pfNode *_node)
{
    int cmode;
	
    cmode = pfiGetCollideDMode(xCollide, PFIC_TARGET);
    switch (_mode) 
    {
    case PFUCOLLIDE_GROUND:
	xCollide->setHeightAboveGrnd( _val);
	xCollide->setGroundNode(_node);

	if (_node != NULL && _val > 0.0f)
	    xCollide->setMode(PFIC_TARGET, cmode | PFIC_TARGET_GROUND);
	else
	    xCollide->setMode(PFIC_TARGET, cmode & ~PFIC_TARGET_GROUND);
	break;

    case PFUCOLLIDE_OBJECT:
	xCollide->setDist(_val);
	xCollide->setObjNode(_node);

	if (_node != NULL && _val > 0.0f)
	    xCollide->setMode(PFIC_TARGET, cmode | PFIC_TARGET_OBJ);
	else
	    xCollide->setMode(PFIC_TARGET, cmode & ~PFIC_TARGET_OBJ);
	break;
    }
}
int
pfiXformerD::collide(void)
{
    int ret;
    
    pfuCollisionChan(xInputDChan);
    
    /* update the collider with new position and motion from motion model */
    xCollide->setCurMotionParams(&xMotionCoordD.pos, &xMotionCoordD.prevPos, 
		xMotionCoordD.speed);
    ret = xCollide->update();
    
    /* If collisions are enabled,
     * get prev position back from collider (may be running in 
     * a separate asynch process but partial updates are probably ok).
     */
    if (xCollide->getMode(PFIC_TARGET))
    {
	xCollide->getCurMotionParams(&xMotionCoordD.pos, &xMotionCoordD.prevPos, 
		&xMotionCoordD.speed);
	xMotionCoordD.makeMat();
    }
    return ret;
}

void
pfiXformerD::enableCollision(void)
{
    if (xCollide)
	xCollide->enable();
}

void
pfiXformerD::disableCollision(void)
{
    if (xCollide)
	xCollide->disable();
}

int
pfiXformerD::getCollisionEnable(void)
{
    if (xCollide)
	return xCollide->getEnable();
    else
	return 0;
}

int
pfiXformerD::getCollisionStatus(void)
{
    if (xCollide)
	return xCollide->getStatus();
    else
	return 0;
}

/*
 * pfiHaveFastMouseClickD() detects fast mouse clicks 
 */
int
pfiHaveFastMouseClickD(pfuMouse *mouse, int button, double msecs)
{
    int dist;
    double etime=0.0f;
    static int prevClick;
    static double prevClickStamp=-1.0f;
    
    if (msecs < 0.0f)
	return 0;
    
    /* msecs between click and release */
    if ((prevClick == button) && (prevClickStamp > 0.0f))
	etime = mouse->releaseTime[button] - prevClickStamp;
	
    /* if no middle click  or release last frame, no  fast stop */
    if (!((mouse->click & button) || (mouse->release & button)))
    {
	if ((!(mouse->flags & button)) ||
	    (etime > msecs))
	{
	    prevClick = 0;
	    prevClickStamp = -1.0f;
	}
	return 0;
    }
    
    /* msecs between click and release */
    if ((mouse->releaseTime[button] - mouse->clickTime[button]) > 1e-5f)
	etime = mouse->releaseTime[button] - mouse->clickTime[button];
	
    /* max pixels moved in x or y */
    dist = PF_MAX2(PF_ABS((mouse->releasePos[button][0] - mouse->clickPos[button][0])), 
		    PF_ABS((mouse->releasePos[button][1] - mouse->clickPos[button][1])));


    /* middle mouse was pushed & released in last frame and total distance
     * moved and elapsed was very small then have fast click
     */
    if ((mouse->release & button) && 
	    (mouse->click & button) &&
	    (etime >= 0.0f))
    {
	if ((dist < 10) && (etime < msecs))
	{
	    prevClick = 0;
	    prevClickStamp = -1.0f;
	    return 1;
	}
    }
    
    /* if middle mouse was released last frame and click was prev fram
     * then call it a fast stop
     */
    if (mouse->release & button)
    {
	if ((dist < 10) && ((etime < msecs) && (prevClick == button)))
	{
	    prevClick = button;
	    return 1;
	}
    }
    
    if (mouse->click & button)
    {
	prevClick = button;
	prevClickStamp = mouse->clickTime[button];
    }
    
    return 0;
}



pfType *pfiTDFXformerD::classType = NULL;

void
pfiTDFXformerD::init()
{
    if (classType == NULL)
    {
	pfiXformerD::init();
	classType = 
	    new pfType(pfiXformerD::getClassType(), "pfiTDFXformerD");
    }
}

pfiTDFXformerD::pfiTDFXformerD(void)
{
    void *arena		= pfMemory::getArena(this);
    
    setType(classType);
    
    xFastClickTime		   = 300.0f; /* msecs */
    xInputCoordD = new(arena) pfi2DInputCoordD;
    
    xInputD = new(arena) pfiInputD;
    xInputD->setEventStreamProcessor(pfiProcessTDFXformerDMouseEvents, this);
    
    xTrackball = new(arena) pfiInputXformDTrackball;
    xTrackball->setInputCoordDPtr(xInputCoordD);
    xTrackball->setUpdateFunc(pfiUpdate2DIXformDTrackball, NULL);
    xTrackball->xMotionCoordD.startSpeed	= 30.0f;
    
    xDrive = new(arena) pfiInputXformDDrive;
    xDrive->setInputCoordDPtr(xInputCoordD);
    xDrive->setUpdateFunc(pfiUpdate2DIXformDDrive, NULL);
    xDrive->xMotionCoordD.startSpeed	= 0.0f;
    xDrive->xMotionCoordD.minSpeed = -xDrive->xMotionCoordD.maxSpeed;
    xDrive->xMotionCoordD.minAccel = -xDrive->xMotionCoordD.maxAccel;
    
    xFly = new(arena) pfiInputXformDFly;
    xFly->setInputCoordDPtr(xInputCoordD);
    xFly->setUpdateFunc(pfiUpdate2DIXformDFly, NULL);
    xFly->xMotionCoordD.startSpeed	= 0.0f;
    xFly->xMotionCoordD.minSpeed = -xFly->xMotionCoordD.maxSpeed;
    xFly->xMotionCoordD.minAccel = -xFly->xMotionCoordD.maxAccel;
    
    // our traditional trackball had pitch "follow" the mouse
    xFly->setMode(PFIXFLY_MODE_PITCH, PFIXFLY_PITCH_FOLLOW);
    
    xSpheric = new(arena) pfiInputXformDSpheric;
    xSpheric->setInputCoordDPtr(xInputCoordD);
    xSpheric->setUpdateFunc(pfiUpdate2DIXformDSpheric, NULL);
    xSpheric->xMotionCoordD.startSpeed	= 0.0f;
    xSpheric->xMotionCoordD.minSpeed = -xSpheric->xMotionCoordD.maxSpeed;
    xSpheric->xMotionCoordD.minAccel = -xSpheric->xMotionCoordD.maxAccel;

    xDefaultModelIndex = PFITDF_TRACKBALL;
    xModelList->set(PFITDF_TRACKBALL, xTrackball);
    xModelList->set(PFITDF_DRIVE, xDrive);
    xModelList->set(PFITDF_FLY, xFly);
    xModelList->set(PFITDF_SPHERIC, xSpheric);
    reset();
}

pfiTDFXformerD::pfiTDFXformerD(pfiInputXformDTrackball *_tb,
		  pfiInputXformDDrive *_drive,
		  pfiInputXformDFly *_fly,
		  pfiInputXformDSpheric *_spheric)
{
    xTrackball = _tb;
    xDrive = _drive;
    xFly = _fly;
    xSpheric = _spheric;
    xIx = xTrackball;
    xDefaultModelIndex = PFITDF_TRACKBALL;
    xModelList->set(PFITDF_TRACKBALL, xTrackball);
    xModelList->set(PFITDF_DRIVE, xDrive);
    xModelList->set(PFITDF_FLY, xFly);
    xModelList->set(PFITDF_SPHERIC, xSpheric);
    reset();
}

pfiTDFXformerD::~pfiTDFXformerD(void)
{
}

void
pfiTDFXformerD::reset(void)
{
    pfiXformerD::reset();
}

void
pfiTDFXformerD::_setDBLimits(pfBox *_dbLimits)
{
    if (!_dbLimits)
	return;

    pfiXformerD::_setDBLimits(_dbLimits);
}

void	
pfiTDFXformerD::_setBSphere(pfSphere* _sphere)
{
    xDBSphere = *_sphere;
    xDBSize =  2.0f * xDBSphere.radius;
    xDBSizeLg = logf(xDBSize);
    if (!(xUserSetMask & _PFIX_USET_RESET_POS))
    {
	PFCOPY_VEC3(xMotionCoordD.startPos.xyz, xDBSphere.center);
	xMotionCoordD.startPos.xyz[1] -= xDBSize*1.5f;
	xMotionCoordD.startPos.xyz[2] += xDBSphere.radius*0.5f;
	PFSET_VEC3(xMotionCoordD.startPos.hpr, 0.0f, 0.0f, 0.0f);
    }
    if  (!(xUserSetMask & _PFIX_USET_START_MOTION))
    {
	xAccelRate = 2*xDBSizeLg;
    }
}

void
pfiTDFXformerD::setTrackball(pfiInputXformDTrackball *_tb) 
{
    xTrackball = _tb;
    if (xCurModelIndex == PFITDF_TRACKBALL)
	xIx = _tb;
}

void
pfiTDFXformerD::setDrive(pfiInputXformDDrive *_drive) 
{
    xDrive = _drive;
    if (xCurModelIndex == PFITDF_DRIVE)
	xIx = _drive;
}

void
pfiTDFXformerD::setFly(pfiInputXformDFly *_fly) 
{
    xFly = _fly;
    if (xCurModelIndex == PFITDF_FLY)
	xIx = _fly;
}
    

void
pfiTDFXformerD::setSpheric(pfiInputXformDSpheric *_spheric)
{
    xSpheric = _spheric;
    if (xCurModelIndex == PFITDF_SPHERIC)
	xIx = _spheric;
}

void
pfiTDFXformerD::selectModel(int _which)
{
    double n, f;
    if (xCurModelIndex == _which)
	return;

    if (_which > xModelList->getNum())
    {
	pfNotify(PFNFY_WARN, PFNFY_RESOURCE, 
	    "pfiTDFSelectXformerDModel - index %d is more than number of models (%d)", 
	    _which, xModelList->getNum());
	return;
    }
    
    stop();
    
    // The XformerD transfers position changes between motion models.
    // The trackball is special because its matrix edits to the
    // scene DCS matrix must now
    // be applied in reverse to the current position.
    
    if (xCurModelIndex == PFITDF_TRACKBALL)
    {
	pfMatrix4d mat, invmat, posmat;
	xIx->getMat(mat);
	invmat.invertOrtho(mat);
	posmat.makeCoordd(&xMotionCoordD.pos);
	posmat *= invmat;
	posmat.getOrthoCoordd(&xMotionCoordD.pos);
	xMotionCoordD.makeMat(); 
	
	if (xPosDCS)
	    pfDoubleDCSMat(xPosDCS, pfIdentMat4d);
    }
    xCurModelIndex = _which;
    xIx = (pfiInputXformD *) xModelList->get(xCurModelIndex);
    /* init new model with position of old model */
    xIx->setCoord(&(xMotionCoordD.pos));
    /* update current position motion parameters to match motion model */
    xIx->getMotionCoordD(&(xMotionCoordD));
    
    /* update collider with new model */
    xCollide->setCurMotionParams(&xMotionCoordD.pos, &xMotionCoordD.prevPos, 
		xMotionCoordD.speed);
    (pfGetChanNearFar(xInputDChan, (float *)(&n), (float *)(&f)), *(&n) = *(float *)(&n), *(&f) = *(float *)(&f));
    
    if (xIx->isOfType(pfiInputXformDTrackball::getClassType()))
    {
	xCollide->setMode(PFIC_RESPONSE, PFIC_RESPONSE_NONE);
	setCollision(PFUCOLLIDE_GROUND, 0.0f, NULL);
	setCollision(PFUCOLLIDE_OBJECT, 0.0f, NULL);
    }
    else if (xIx->isOfType(pfiInputXformDDrive::getClassType()))
    {
	double dh = ((pfiInputXformDDrive *)xIx)->getDriveHeight();
	xCollide->setMode(PFIC_RESPONSE, PFIC_RESPONSE_STOP);
	setCollision(PFUCOLLIDE_GROUND, dh, xNode);
	setCollision(PFUCOLLIDE_OBJECT, n * 2.0f, xNode);
    }
    else if (xIx->isOfType(pfiInputXformDFly::getClassType()))
    {
	xCollide->setMode(PFIC_RESPONSE, PFIC_RESPONSE_BOUNCE);
	/* Do not terrain follow */
	setCollision(PFUCOLLIDE_GROUND, 0.0f, xNode);
	setCollision(PFUCOLLIDE_OBJECT, n * 2.0f, xNode);
    }    
    else if (xIx->isOfType(pfiInputXformDSpheric::getClassType()))
    {
	xCollide->setMode(PFIC_RESPONSE, PFIC_RESPONSE_STOP);
	    /* Do not terrain follow */
	setCollision(PFUCOLLIDE_GROUND, 0.0f, xNode);
	setCollision(PFUCOLLIDE_OBJECT, n * 2.0f, xNode);
    }
}

void
pfiTDFXformerD::update(void)
{
    if (xInputD)
	xInputD->processEvents();
	
    if (!xIx)
	return;
    
    /* update motion model with info from collide */
    xIx->setMotionCoordD(&(xMotionCoordD));

    if (xIx)
    {
	xIx->update();
	xIx->getMotionCoordD(&(xMotionCoordD));
    }
    
    if (xPosDCS || xPosChan) 
    {
	if (xIx->isOfType(pfiInputXformDTrackball::getClassType()))
	{
	    if (xPosDCS)
	    {
		pfMatrix4d _mat;
		xIx->getMat(_mat);
		pfDoubleDCSMat(xPosDCS, _mat);
	    }
	}
	if (xIx->isOfType(pfiInputXformDTravel::getClassType()))
	{
	    if (xPosChan)
		pfChanViewMatD(xPosChan, xMotionCoordD.mat);
	}
    }
}

int
pfiProcessTDFXformerDMouseEvents(pfiInputD *, pfuEventStream *,  void *data)
{
    pfiTDFXformerD *xf = (pfiTDFXformerD *) data;
    pfuMouse *mouse;
    pfuEventStream *ev;
    pfChannel *chan;
    
    xf->getAutoInputD(&chan, &mouse, &ev);
    
    pfiProcessTDFXformerDMouse(xf, mouse, chan);
    
    return PFI_CB_CONT;
}

void
pfiProcessTDFXformerDMouse(pfiTDFXformerD *xf, pfuMouse *mouse, pfChannel *inputChan)
{
    int focus=1;
    pfiInputXformD *ix;
    
    ix = xf->getCurModel();
    
    /* Only listen to mouse if cursor is in channel and GUI doesn't
     * have focus
     */
    /* XXXX mouse and input handling should be separated from model update !!! */
    if (!(mouse->inWin) || 
	    pfuInGUI(mouse->xpos, mouse->ypos) ||
	    !pfuMouseInChan(mouse, inputChan))
	focus = 0;
    xf->setFocus(focus);
    
    if (!ix)
	return;
	
    ix->setFocus(focus);
    ix->setMode(PFIX_MODE_AUTO, !focus);
    
    if (focus && (mouse->flags & PFUDEV_MOUSE_DOWN_MASK))
    {
	pfiInputCoordD *icoord;
	pfVec2d pos;
	
	pfuCalcNormalizedChanXYd(&pos[0], &pos[1], inputChan, mouse->xpos, mouse->ypos);
	icoord = xf->getInputCoordDPtr();
	icoord->setVec(pos.vec);
    }
    
    /* update the motion model */
    
    if (ix->isOfType(pfiInputXformDTrackball::getClassType()))
	pfiProcessTDFTrackballDMouse(xf, (pfiInputXformDTrackball*)ix, mouse);
    
    else if (ix->isOfType(pfiInputXformDSpheric::getClassType()))
	pfiProcessTDFSphericDMouse(xf, (pfiInputXformDSpheric*)ix, mouse);
    
    else if (ix->isOfType(pfiInputXformDTravel::getClassType()))
	pfiProcessTDFTravelDMouse(xf, (pfiInputXformDTravel*)ix, mouse);
    
    else
    {
	pfNotify(PFNFY_NOTICE, PFNFY_PRINT, "pfiTDFXformerD - Unrecognized type %s - 0x%x", 
	    ix->getTypeName(), ix->getClassType());
	ix->update();
    }
}

void
pfiProcessTDFTrackballDMouse(pfiTDFXformerD *xf, pfiInputXformDTrackball *trackball, pfuMouse *mouse)
{
    int		    buttons, mod;
    int		    accel=PFIXTK_ACCEL_NONE;
    
    buttons = mouse->flags & PFUDEV_MOUSE_DOWN_MASK;
    mod = mouse->flags & PFUDEV_MOD_MASK;
    /* 
     * Button actions:
     *
     *  L  M  R  CODE  INTERPRETATION
     *  -  -  -  ----  ------------------
     *  0  1  0     4  rotate around X and Z axes
     *  0  0  1     2  rotate around Y axis
     *  0  1  1     6  second button accelerates rotate mode chosen by first
     *  1  0  0     1  translate in X and Z planes
     *  1  0  1     5  translate in Y (zoom in and out)
     */
    if (xf->getFocus())
    {
	if (pfiHaveFastMouseClickD(mouse, PFUDEV_MOUSE_MIDDLE_DOWN, xf->getFastClickTime()))
	{
	    trackball->stop();
	}
	else
	{
	    /* hyper acceleration if shift key is down */
	    if (mod & PFUDEV_MOD_SHIFT)
	    {
		accel = PFIXTK_ACCEL_INCREASE;
		trackball->setAccelRate(1.0f);
	    }
	    switch(buttons)
	    {
	    case PFUDEV_MOUSE_MIDDLE_DOWN:
		trackball->setMode(PFIXTK_MODE_MOTION, PFIXTK_MOTION_ROTXZ);
		trackball->setMode(PFIXTK_MODE_AUTO, 0);
		break;
	    case PFUDEV_MOUSE_MIDDLE_DOWN | PFUDEV_MOUSE_RIGHT_DOWN:
		trackball->setMode(PFIXTK_MODE_MOTION, PFIXTK_MOTION_ROTY);
		break;
	    case PFUDEV_MOUSE_LEFT_DOWN:
		trackball->setMode(PFIXTK_MODE_MOTION, PFIXTK_MOTION_TRANSXZ);
		break;
	    case PFUDEV_MOUSE_LEFT_DOWN | PFUDEV_MOUSE_RIGHT_DOWN:
	    case PFUDEV_MOUSE_RIGHT_DOWN:
		trackball->setMode(PFIXTK_MODE_MOTION, PFIXTK_MOTION_TRANSY);
		trackball->setMode(PFIXTK_MODE_ACCEL, accel);
		break;
	    case PFUDEV_MOUSE_LEFT_DOWN | PFUDEV_MOUSE_MIDDLE_DOWN | PFUDEV_MOUSE_RIGHT_DOWN:
		xf->stop();
		trackball->setMode(PFIXTK_MODE_MOTION, PFIXTK_MOTION_STOP);
		break;
	    case 0x0:
	    default:
		trackball->setFocus(0);
		trackball->setMode(PFIX_MODE_AUTO, 1);
		break;
	    }
	}
    }
}


void
pfiProcessTDFSphericDMouse(pfiTDFXformerD *xf, pfiInputXformDSpheric *spheric, pfuMouse *mouse)
{
    int		buttons;

    buttons = mouse->flags & PFUDEV_MOUSE_DOWN_MASK;
    
    /* 
     * Button actions:
     *
     *  L  M  R  CODE  INTERPRETATION
     *  -  -  -  ----  ------------------
     *  1  0  0     ?  in and out
     *  1  1  0     ?  in and out - add rotate
     *	0  1  0	    ?  up and down \(around sphere\), side to side
     *  0  0  1     ?  track in and out
     *  0  1  1     ?  track in and out - add rotate
     */
    if (xf->getFocus())
    {
	if (pfiHaveFastMouseClickD(mouse, PFUDEV_MOUSE_MIDDLE_DOWN, xf->getFastClickTime()))
	{
 	    spheric->setMode(PFIXSPH_MODE_MOTION, PFIXSPH_MOTION_STOP);
	    spheric->stop();
	}
	else
	{
	    spheric->setMode(PFIXSPH_MODE_MOTION_MOD, 0x0);
	    switch(buttons)
	    {
	        case PFUDEV_MOUSE_MIDDLE_DOWN:
	            spheric->xInMotion = TRUE;
		    spheric->xTracking = FALSE;
	            spheric->setMode(PFIXSPH_MODE_MOTION, PFIXSPH_MOTION_AROUND);
		    break;

		case PFUDEV_MOUSE_LEFT_DOWN:
	            spheric->xInMotion = TRUE;
		    spheric->xTracking = FALSE;
		    spheric->setMode(PFIXSPH_MODE_MOTION, PFIXSPH_MOTION_INOUT);
		    break;
		    
	        case PFUDEV_MOUSE_LEFT_DOWN | PFUDEV_MOUSE_MIDDLE_DOWN:
	            spheric->xInMotion = TRUE;
		    spheric->xTracking = FALSE;
		    spheric->setMode(PFIXSPH_MODE_MOTION, PFIXSPH_MOTION_INOUT);
		    spheric->setMode(PFIXSPH_MODE_MOTION_MOD,
				    PFIXSPH_MOTION_MOD_SPIN);
		    break;
		    
		case PFUDEV_MOUSE_RIGHT_DOWN:
	            spheric->xInMotion = TRUE;
		    if (!spheric->xTracking)
			spheric->xTracking = PFIXSPH_PATH_UNKNOWN;
		    spheric->setMode(PFIXSPH_MODE_MOTION,
				    PFIXSPH_MOTION_TRACK_INOUT);
		    break;

		case PFUDEV_MOUSE_RIGHT_DOWN | PFUDEV_MOUSE_MIDDLE_DOWN:
	            spheric->xInMotion = TRUE;
		    if (!spheric->xTracking)
			spheric->xTracking = PFIXSPH_PATH_UNKNOWN;
		    spheric->setMode(PFIXSPH_MODE_MOTION,
				    PFIXSPH_MOTION_TRACK_INOUT);
		    spheric->setMode(PFIXSPH_MODE_MOTION_MOD,
				    PFIXSPH_MOTION_MOD_SPIN);
		    break;
		    
		default:
		case 0x0:
		    // if tracking releasing the button stops
		    spheric->setFocus(0);
		    spheric->setMode(PFIX_MODE_AUTO, 1);
		    break;
		  }
	}
    }
}


void
pfiProcessTDFTravelDMouse(pfiTDFXformerD *xf, pfiInputXformDTravel *tr, pfuMouse *mouse)
{
    int		buttons, mod;
    double	rate = 0.0f;
    double	xfAccelRate;

    buttons = mouse->flags & PFUDEV_MOUSE_DOWN_MASK;
    mod = mouse->flags & PFUDEV_MOD_MASK;
    xfAccelRate = xf->getAccelRate();
    
    /* 
     * Button actions:
     *
     *  L  M  R  CODE  INTERPRETATION
     *  -  -  -  ----  ------------------
     *  1  0  0     4  pan,  accelerate
     *  1  1  0     6  pan,  accelerate
     *  1  0  1     5  stop and pan
     *	0  1  0	    2  drive and steer
     *  0  0  1     1  pan,  decelerate
     *  0  1  1     3  pan,  decelerate
     */
    if (xf->getFocus())
    {
	if (pfiHaveFastMouseClickD(mouse, PFUDEV_MOUSE_MIDDLE_DOWN, xf->getFastClickTime()))
	{
	    tr->stop();
	}
	else
	{
	    tr->setMode(PFIXTR_MODE_MOTION_MOD, 0x0);
	    switch(buttons)
	    {
		case PFUDEV_MOUSE_MIDDLE_DOWN:
		    if (mod & PFUDEV_MOD_CTRL)
		    {
			tr->setMode(PFIXTR_MODE_MOTION_MOD, PFIXTR_MOTION_MOD_VERTICAL);
			if (tr->isOfType(pfiInputXformDDrive::getClassType()))
			{
			    double dh = ((pfiInputXformDDrive *)tr)->getDriveHeight();
			    xf->setCollision(PFUCOLLIDE_GROUND, dh, xf->getNode());
			}
		    }
		    else
		    {
			tr->setMode(PFIXTR_MODE_MOTION, PFIXTR_MOTION_FORWARD);
		    }
		    tr->setMode(PFIXTR_MODE_ACCEL, PFIXTR_ACCEL_NONE);
		    break;
		    
		case PFUDEV_MOUSE_LEFT_DOWN | PFUDEV_MOUSE_MIDDLE_DOWN:
		case PFUDEV_MOUSE_LEFT_DOWN:
		    tr->setMode(PFIXTR_MODE_MOTION, PFIXTR_MOTION_FORWARD);
		    tr->setMode(PFIXTR_MODE_ACCEL, PFIXTR_ACCEL_INCREASE);
		    if (mod & PFUDEV_MOD_SHIFT)
		    {
			rate = PF_MAX2(1.0f, xfAccelRate);
		    }
		    tr->setAccelRate(rate);
		    break;
		    
		case PFUDEV_MOUSE_RIGHT_DOWN | PFUDEV_MOUSE_MIDDLE_DOWN:
		case PFUDEV_MOUSE_RIGHT_DOWN: /* decelerate into reverse */
		    tr->setMode(PFIXTR_MODE_MOTION, PFIXTR_MOTION_FORWARD);
		    tr->setMode(PFIXTR_MODE_ACCEL, PFIXTR_ACCEL_DECREASE);
		    if (mod & PFUDEV_MOD_SHIFT)
		    {
			rate = tr->getAccelRate();
			rate = PF_MAX2(1.0f, xfAccelRate);
		    }
		    tr->setAccelRate(rate);
		    break;
		    
		case PFUDEV_MOUSE_LEFT_DOWN | PFUDEV_MOUSE_RIGHT_DOWN:
		    tr->setMode(PFIXTR_MODE_MOTION, PFIXTR_MOTION_DIRECTION);
		    tr->setMode(PFIXTR_MODE_ACCEL, PFIXTR_ACCEL_NONE);
		    break;
		case PFUDEV_MOUSE_LEFT_DOWN | PFUDEV_MOUSE_MIDDLE_DOWN | PFUDEV_MOUSE_RIGHT_DOWN:
		    xf->stop();
		    tr->setMode(PFIXTR_MODE_MOTION, PFIXTR_MOTION_STOP);
		    break;
		default:
		case 0x0:
		    tr->setFocus(0);
		    tr->setMode(PFIX_MODE_AUTO, 1);
		    tr->setMode(PFIXTR_MODE_ACCEL, PFIXTR_ACCEL_NONE);
		    break;
	    }
	}
    }
}