Skip to content
Snippets Groups Projects
Gview.cc 18.8 KiB
Newer Older
teuben's avatar
teuben committed
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "geometry.h"
#include <memory.h>
teuben's avatar
teuben committed

#include "Gview.H"


static void translation(Matrix *T, float x, float y, float z);
static void rotation(Matrix *T, float x, float y, float z);

Fl_Gview::Fl_Gview(int x, int y, int w, int h, const char *label)
		: Fl_Gl_Window(x,y,w,h,label) {
  init();
  end();
}

void Fl_Gview::lookvec(int axis, const Point *vec) {
  Point v = *vec;
  if(axis < 0 || axis > 3) return;
  if(axis == 2) vscale(&v, -1,&v);	/* cam looks toward -Z */
  /* XXX fill in orthogonalization, invert, load into Tw2c */
  /* Don't need this right now */
  Fl::warning("Fl_Gview::lookvec() not implemented yet");
  redraw();
}

const Matrix *Fl_Gview::Tc2w() const {
  return &Tc2w_;
}

const Matrix *Fl_Gview::Tw2c() const {
  return &Tw2c_;
}

void Fl_Gview::Tc2w( const Matrix *newTc2w ) {
  Tc2w_ = *newTc2w;
  eucinv(&Tw2c_, &Tc2w_);
  notify();
  redraw();
}

void Fl_Gview::Tw2c( const Matrix *newTw2c ) {
  Tw2c_ = *newTw2c;
  eucinv(&Tc2w_, &Tw2c_);
  notify();
  redraw();
}

void Fl_Gview::reset( int id ) {
  if(id == GV_ID_CAMERA) {
    translation( &Tc2w_, 0, 0, focallen_ );
    Tc2w( &Tc2w_ );
  } else {
    const Matrix *o2w = To2w( id );
    if(o2w && o2w != &Tidentity) {
	*(Matrix *)o2w = Tidentity;	/* Reset! */
    }
  }
  notify();
  redraw();
}

void Fl_Gview::focallen(float flen) {
  if(flen == 0) {
    Fl::warning("Can't set Fl_Gview::focallen() to zero");
  } else {
    if(persp_)
	halfyfov_ *= flen / focallen_;
    focallen_ = flen;
  }
  notify();
  redraw();
}

float Fl_Gview::angyfov() const {
  return 2*atan(halfyfov_ / focallen_)*180/M_PI;
}

void Fl_Gview::angyfov(float deg) {
  if(deg == 0) Fl::warning("Can't set Fl_Gview::angyfov() to zero");
  else if(deg <= -180 || deg >= 180)
	Fl::warning("Can't set Fl_Gview::angyfov() to >= 180");
  else halfyfov_ = focallen_ * tan((deg/2)*M_PI/180);
  notify();
  redraw();
}

void Fl_Gview::halfyfov( float hyfov ) {
  if(hyfov == 0) Fl::warning("Can't set Fl_Gview::halfyfov() to zero");
  else halfyfov_ = hyfov;
  redraw();
}

void Fl_Gview::perspective( int bepersp ) {
  if((bepersp!=0) == persp_)
    return;
  persp_ = (bepersp != 0);
  redraw();
  notify();
}

void Fl_Gview::farclip( float cfar ) {
  if(cfar == far_) return;
  if(cfar != 0 || !persp_) {
    far_ = cfar;
    notify();
    redraw();
  }
}

void Fl_Gview::nearclip( float cnear ) {
  if(cnear == near_) return;
  if(cnear != 0 || !persp_) {
    near_ = cnear;
    notify();
    redraw();
  }
}

void Fl_Gview::center( const Point *cenw ) {
  if(cenw) pcenw_ = *cenw;
  else pcenw_.x[0] = pcenw_.x[1] = pcenw_.x[2] = 0;
  notify();
}

int Fl_Gview::next_id() const {
  int id;
  for(id = 1; withid(id) >= 0; id++)
    ;
  return id;
}

int Fl_Gview::add_drawer( void (*func)( Fl_Gview *, void *obj, void *arg ),
	void *obj, void *arg, char *name, int id )
{
  if(id < 0) id = next_id();
  int dno = withid( id );
  if(dno < 0) {
    if(ndrawers_ >= maxdrawers_) {
	maxdrawers_ = ndrawers_*2 + 15;
	int room = maxdrawers_ * sizeof(struct drawer);
	drawers_ = (struct drawer *)
		    (drawers_==NULL ? malloc(room) : realloc(drawers_, room));
    }
    dno = ndrawers_++;
  }
  struct drawer *dp = &drawers_[dno];
  dp->func = func;
  dp->obj = obj;
  dp->arg = arg;
  dp->To2w = Tidentity;
  dp->name = name;
  dp->id = id;
teuben's avatar
teuben committed
  notify();
  redraw();
  return id;
}

int Fl_Gview::withid( int id ) const {	// Which drawer[] slot is id in?
teuben's avatar
teuben committed
  for(int dno = 0; dno < ndrawers_; dno++)
    if(drawers_[dno].id == id)
	return dno;
  return -1;
}

const Matrix *Fl_Gview::To2w( int id ) const {
  if(id == GV_ID_CAMERA)
    return Tc2w();
  int dno = withid( id );
  return (dno < 0) ? &Tidentity : &drawers_[dno].To2w;
}

int Fl_Gview::To2w( int id, const Matrix *newT ) {
  if(id == GV_ID_CAMERA) {
    Tc2w( newT );
    return -1;
  }
  int drawerno = withid( id );
  if(drawerno < 0) return 0;
  drawers_[drawerno].To2w = newT ? *newT : Tidentity;
  notify();
  redraw();
  return 1;
}

void Fl_Gview::objparent( int id, int parent ) {
  int drawerno = withid( id );
  if(drawerno >= 0)
    drawers_[drawerno].parent = parent;
}

int Fl_Gview::objparent( int id ) const {
  int drawerno = withid( id );
  return (drawerno >= 0) ? drawers_[drawerno].parent : 0;
}

void Fl_Gview::subc_lrbt( float subclrbt[4] ) {
  for(int k = 0; k < 4; k++)
    subclrbt_[k] = subclrbt[k];
}

teuben's avatar
teuben committed
#define VIEW_CLEAR  0x1
#define	VIEW_RED    0x2
#define	VIEW_CYAN   0x4

void Fl_Gview::glprojection( float nearclip, float farclip, const Matrix *postproj )
{
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    if(inpick()) {
	GLint vp[4] = {0, 0, w(), h()};
	gluPickMatrix( pickx_, picky_, pickwidth_, pickheight_, vp );
    }
    if(use_subc_) {
	glFrustum( nearclip * subclrbt_[0], nearclip * subclrbt_[1],
		   nearclip * subclrbt_[2], nearclip * subclrbt_[3],
		   nearclip, farclip );

    } else if(persp_) {
	float nthf = nearclip * halfyfov_ / focallen_;
	glFrustum( -nthf * aspect_, nthf * aspect_,
		   -nthf, nthf,
		   nearclip, farclip );
    } else {
	glOrtho( -aspect_*halfyfov_, aspect_*halfyfov_,
		 -halfyfov_, halfyfov_,
		  nearclip, farclip );
    }
    if(postproj)
	glMultMatrixf( postproj->m );
    glMatrixMode( GL_MODELVIEW );
}

teuben's avatar
teuben committed
void Fl_Gview::draw_scene( int how, const Matrix *postproj ) {
  /* draw scene */

  glClearDepth( 1.0 );
  if(how & VIEW_CLEAR) {
    glColorMask( 1, 1, 1, 1 );
    glClearColor( bgcolor_.x[0], bgcolor_.x[1], bgcolor_.x[2], 0 );
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
  } else {
    glClear( GL_DEPTH_BUFFER_BIT );
  }

  if(how & VIEW_RED)
    glColorMask( 1, 0, 0, 1 );
  else if(how & VIEW_CYAN)
    glColorMask( 0, 1, 1, 1 );

  glEnable( GL_DEPTH_TEST );
  glDisable( GL_LIGHTING );
  glDisable( GL_TEXTURE_2D );
  glDisable( GL_COLOR_MATERIAL );

  if(how || postproj)
    glprojection( near_, far_, postproj );
teuben's avatar
teuben committed

  if(predraw)
    (*predraw)( this );

  if(how || postproj) {
teuben's avatar
teuben committed
    glMatrixMode( GL_MODELVIEW );
    if(use_subc_) {
	glLoadMatrixf( Tc2subc()->m );
	glMultMatrixf( Tw2c()->m );
    } else {
	glLoadMatrixf( Tw2c()->m );
    }
teuben's avatar
teuben committed
  }

teuben's avatar
teuben committed
  for(int i = 0; i < ndrawers_; i++) {
    struct drawer *dp = &drawers_[i];
    if(dp->func != NULL) {
	int wantclip = dp->objclip;
	if(wantclip)
	    glprojection( dp->objnear, dp->objfar, postproj );
	else if(curclip)
	    glprojection( near_, far_, postproj );
	curclip = wantclip;

teuben's avatar
teuben committed
	glPushMatrix();
	if(dp->parent == GV_ID_CAMERA) {
	    if(use_subc_)
		glLoadMatrixf( Tc2subc()->m );
	    else
		glLoadIdentity();
	}
	glMultMatrixf( dp->To2w.m );
teuben's avatar
teuben committed
	if(inpick_) {
teuben's avatar
teuben committed
	    glPushName(0);
	}
	(*dp->func)(this, dp->obj, dp->arg);
teuben's avatar
teuben committed
	if(inpick_)
	     glPopName();
	glPopMatrix();
    }
  }
  if(postdraw)
    (*postdraw)( this );
teuben's avatar
teuben committed
}

static void stereoeye( Matrix *dst, float stereosep, float focallen ) {
  *dst = Tidentity;
  dst->m[4*2+0] = stereosep;
  dst->m[4*3+0] = stereosep * focallen;
}

void Fl_Gview::draw() {

  if(!valid() || damage() || inpick() || (stereo_ != GV_MONO)) {
    /* Assume reshaped */
    valid(1);
    glViewport( 0, 0, w(), h() );

    aspect_ = h() > 0 ? (float)w() / (float)h() : 1.0;

    Matrix postproj;

    switch(stereo_) {
    case GV_MONO:
    default:
	draw_scene( VIEW_CLEAR, NULL );
	break;

    case GV_REDCYAN:
	stereoeye( &postproj, stereosep_, focallen_ );
	draw_scene( VIEW_CLEAR|VIEW_RED, &postproj );
	stereoeye( &postproj, -stereosep_, focallen_ );
	draw_scene( VIEW_CYAN, &postproj );
	glColorMask( 1, 1, 1, 1 );
	break;

    case GV_QUADBUFFERED:
	stereoeye( &postproj, -stereosep_, focallen_ );
	glDrawBuffer( GL_BACK_RIGHT );
	draw_scene( VIEW_CLEAR, &postproj );

	stereoeye( &postproj, stereosep_, focallen_ );
	glDrawBuffer( GL_BACK_LEFT );
	draw_scene( VIEW_CLEAR, &postproj );

	break;
    }
  } else {
    draw_scene( VIEW_CLEAR, NULL );
  }

  /* draw (I hope) any children lying on top of us */
  if(children() > 0) Fl_Gl_Window::draw();
}

int Fl_Gview::snapshot( int x, int y, int w, int h,  void *packedrgb )
{
  make_current();
  glPixelStorei( GL_PACK_ALIGNMENT, 1 );
  glReadBuffer( GL_FRONT );
  glReadPixels(x, y, w, h, GL_RGB, GL_UNSIGNED_BYTE, packedrgb);
  return 1; // Might return whether this window was properly uncovered?
}

static int verbose;	/* for debugging! */

#define	XYBUTTON	FL_BUTTON1
#define	PICKBUTTON	FL_BUTTON2
#define	ZBUTTON		FL_BUTTON3

#define	SLOWKEY		FL_SHIFT
#define	CONSTRAINKEY	FL_CTRL


int Fl_Gview::handle(int ev) {

  if(eventhook) {
    /* Allow clients to pre-screen our events without subclassing */
    switch((*eventhook)(this, ev)) {
    case 1: return 1;	/* pre-screener handled it */
    case -1: return 0;  /* pre-screener commands us to ignore it too */
    default: break;	/* Else just process event normally below */
    }
  }

  switch(ev) {
  case FL_FOCUS:   hasfocus_ = 1; return 1;  // Yes, we want FL_KEYBOARD events
  case FL_UNFOCUS: hasfocus_ = 0; return 1;

  case FL_PUSH:
	{  // Besides navigating, grab keyboard focus if we're clicked-on
	    if(!hasfocus_) take_focus();
	}
  case FL_DRAG:
  case FL_RELEASE:
    if(Fl::event_state(XYBUTTON|ZBUTTON) && !Fl::event_state(PICKBUTTON)) {
	do_nav(ev, Fl::event_state(FL_SHIFT), Fl::event_state(ZBUTTON),
		   Fl::event_state(CONSTRAINKEY));
	return 1;
    } else if(Fl::event_state(PICKBUTTON)
		&& !Fl::event_state(XYBUTTON|ZBUTTON)
		&& pickcb_ != NULL) {

	dpickx_ = Fl::event_x();
	dpicky_ = h() - Fl::event_y();

	if(ev == FL_PUSH) {
	    do_pick( dpickx_, dpicky_ );
	    pickneeded_ = 0;
	} else {
	    if(!pickneeded_) {
		pickneeded_ = 1;
		Fl::add_idle( Fl_Gview::idlepick, (void *)this );
	    }
	}
	return 1;
    }
    /* Maybe do right-button pick? */
    return 0;

  case FL_ENTER:
    take_focus();
    return 1;

  case FL_KEYBOARD:

    if(Fl::event_text() == NULL || Fl::event_text()[0] == '\0')
	return 0;

    int c = Fl::event_text()[0];

    if(num.addchar( c ))
	return 1;


    switch(c) {
    case 'w': reset( retarget() ); notify(); redraw(); break;
    case 'r': retarget(); nav( GV_ROTATE ); break;
    case 'p':
    case 'P': if(num.has) retarget();
	      else {
		do_pick( Fl::event_x_root() - x_root(),
			 y_root() + h() - Fl::event_y_root() );
	      }
	      break;
    case 'f': retarget(); nav( GV_FLY ); break;
    case 't': retarget(); nav( GV_TRANSLATE ); break;
    case 'o': retarget(); nav( GV_ORBIT ); break;
    case 'O': perspective( num.value( !perspective() )  );
slevy's avatar
 
slevy committed
	      if(msg) msg("Perspective %s", persp_?"on":"off");
teuben's avatar
teuben committed
	      notify();
	      break;

    case 'v':
    case 'V':	if(num.has) angyfov( num.fvalue() );
		else halfyfov( halfyfov() * (c=='v' ? 1.25 : 1/1.25) );
slevy's avatar
 
slevy committed
		if(msg) msg("angyfov %g", angyfov());
teuben's avatar
teuben committed
		notify();
		break;

    case 'v'&0x1F: verbose = !verbose; break;

    case '@':
teuben's avatar
teuben committed
	vgettranslation( &cpos, To2w( retarget() ) );
	tfm2quat( &cquat, To2w( retarget() ) );
	if(msg) msg("%s at %.4g %.4g %.4g  quat %.4g %.4g %.4g %.4g",
teuben's avatar
teuben committed
		dname( retarget() ),
		cpos.x[0],cpos.x[1],cpos.x[2],
		cquat.q[0], cquat.q[1], cquat.q[2], cquat.q[3]);
teuben's avatar
teuben committed
	break;

    case '=':
	const float *fp;
	int me;
	me = retarget();
	fp = &To2w(me)->m[0];
slevy's avatar
 
slevy committed
	if(msg) msg("%s To2w():", dname(me));
teuben's avatar
teuben committed
	int i;
	for(i = 0; i < 16; i+=4)
slevy's avatar
 
slevy committed
	    if(msg) msg("\t%9.5g %9.5g %9.5g %9.5g", fp[i],fp[i+1],fp[i+2],fp[i+3]);
teuben's avatar
teuben committed
	Matrix w2o;
	eucinv( &w2o, To2w(me) );
	fp = &w2o.m[0];
slevy's avatar
 
slevy committed
	if(msg) msg("%s Tw2o():", dname(me));
teuben's avatar
teuben committed
	for(i = 0; i < 16; i+=4)
slevy's avatar
 
slevy committed
	    if(msg) msg("\t%9.5g %9.5g %9.5g %9.5g", fp[i],fp[i+1],fp[i+2],fp[i+3]);
teuben's avatar
teuben committed
	float aer[3];
	Point xyz;
	tfm2xyzaer( &xyz, aer, To2w(me) );
slevy's avatar
 
slevy committed
	if(msg) {
	    msg("%s o2w = XYZ Ry Rx Rz FOV:", dname(me));
	    msg("  %g %g %g  %g %g %g  %g",
teuben's avatar
teuben committed
		xyz.x[0],xyz.x[1],xyz.x[2],
		aer[1],aer[0],aer[2],
		perspective() ? angyfov() : -2*halfyfov() );
slevy's avatar
 
slevy committed
	}
teuben's avatar
teuben committed
	break;

    case '\033': exit(0);  /* ESC */
    // case FL_End: Fl::warning("End key!"); return 1; // test!

    default: c = 0;
    }

    num.clear();

    /* Maybe check Fl::event_key(FL_HOME), etc.? */
    return c==0 ? 0 : 1;

  }
  return 0;
}

void Fl_Gview::idlepick( void *vthis ) {
  Fl_Gview *me = (Fl_Gview *)vthis;

  if(me->pickneeded_) {
    me->do_pick( me->dpickx_, me->dpicky_ );
    me->pickneeded_ = 0;
    Fl::remove_idle( Fl_Gview::idlepick, vthis );
  }
}


void Fl_Gview::notifier( void (*func)(Fl_Gview*,void*), void *arg ) {
  notify_ = func;
  notifyarg_ = arg;
}

void Fl_Gview::notify() {
  if(notify_ != NULL)
    (*notify_)( this, notifyarg_ );
}

void Fl_Gview::nav( enum Gv_Nav newnav ) {
  if(nav_ != newnav) {
    nav_ = newnav;
    notify();
  }
}

/*
 * rot through theta about vector {x,y,z}, where tan(theta/2) = length(xyz)
 */
static void rotation(Matrix *T, float x, float y, float z)
{

  float chalf = sqrtf(1 / (1 + x*x + y*y + z*z));
  Quat rq = { chalf, x*chalf, y*chalf, z*chalf };
  quat2tfm(T, &rq);
teuben's avatar
teuben committed
}

static void translation(Matrix *T, float x, float y, float z)
{
  Point p = {x,y,z};
  *T = Tidentity;
  vsettranslation( T, &p );
}

void Fl_Gview::start_nav( int mytarget ) {
    evx_ = Fl::event_x();
    evy_ = Fl::event_y();
    evTc2w_ = Tc2w_, evTw2c_ = Tw2c_;
    evTobj2w_ = *To2w( mytarget );
}

void Fl_Gview::do_nav(int ev, int slow, int zaxis, int constrained) {

  if((slow != evslow_ || zaxis != evzaxis_ || constrained != evconstrain_)
	&& (ev == FL_DRAG)) {
    /* If conditions changed, pretend button was released
     * (so we commit to this nav update) and
     * pushed again.
     */
    do_nav( FL_RELEASE, evslow_, evzaxis_, evconstrain_ );
    ev = FL_PUSH;
  }

  Gv_Nav curnav = (constrained && nav_==GV_ORBIT)
			? (zaxis ? GV_ROTATE : GV_FLY)
			: nav_;

  int mytarget = (curnav == GV_ORBIT || curnav == GV_FLY || !movingtarget())
		? GV_ID_CAMERA
		: target();

  if(ev == FL_PUSH) {
    start_nav( mytarget );
    evslow_ = slow;
    evzaxis_ = zaxis;
    evconstrain_ = constrained;
    /* and, stop animation (idlefunc)! */
    return;
  }

  if((ev == FL_DRAG || ev == FL_RELEASE) && w() > 0) {
    float slowrate = slow ? 0.05 : 1.0;
    int field = w() > h() ? w() : h();
    float dx = -(Fl::event_x() - evx_);
    float dy =  (Fl::event_y() - evy_);
    float angfield = halfyfov_ / focallen_;

    if(ev == FL_RELEASE && (fabs(dx) < nullthresh_ && fabs(dy) < nullthresh_)) {
	dx = dy = 0;
    }

    dx *= slowrate / field;
    dy *= slowrate / field;

    if(constrained && curnav == nav_) {
	// CTRL key means "constrain to X/Y axis",
	// except in Orbit mode where it means "fly" or "twist"!
	if(fabs(dx) < fabs(dy)) dx = 0;
	else dy = 0;
    }

    Matrix Tincr;
    const Matrix *Tf2w, *Tw2f;
    const Point *pcenterw = NULL;

    if(owncoords_) {
	Tf2w = Tw2f = NULL;
    } else {
	Tf2w = &evTc2w_, Tw2f = &evTw2c_;
    }

    switch(curnav) {
    case GV_ROTATE:
	if(zaxis)
	    rotation(&Tincr, 0, 0, (dx+dy));
	else
	    rotation(&Tincr, -2*dy, 2*dx, 0);
	pcenterw = &pcenw_;
	break;
    case GV_ORBIT:
	if(zaxis) {
	    Point pcamw;
	    vgettranslation( &pcamw, Tc2w() );
	    translation(&Tincr, 0, 0, (dx+dy) * vdist(&pcenw_, &pcamw));
	    start_nav( mytarget );
	} else {
	    rotation(&Tincr, -2*dy, 2*dx, 0);
	}
	pcenterw = &pcenw_;
	break;
    case GV_FLY:
	if(zaxis) {
	    translation(&Tincr, 0, 0, (dx+dy) * zspeed * focallen_);
	} else {
	    rotation(&Tincr, dy*angfield, -dx*angfield, 0);
	}
	pcenterw = NULL;
	break;
    case GV_TRANSLATE:
	if(zaxis) {
	    translation(&Tincr, 0, 0, -(dx+dy) * zspeed * focallen_);
	} else {
	    translation(&Tincr, 2 * dx * halfyfov_ * focallen_, 2 * dy * halfyfov_ * focallen_, 0);
	}
	pcenterw = NULL;
	break;
    default:
	fprintf(stderr, "Fl_Gview::do_nav(): Unknown nav mode %d\n", nav_);
	Tincr = Tidentity;
    }

    if(verbose) {
teuben's avatar
teuben committed
	vgettranslation( &dp, &Tincr );
	tfm2quat( &dq, &Tincr );
	if(msg) msg("dx %.3f dy %.3f  angfield %.3g  dp %.4g %.4g %.4g  dq %.4g %.4g %.4g %.4g",
teuben's avatar
teuben committed
		dx, dy, angfield,
		dp.x[0],dp.x[1],dp.x[2],
		dq.q[0],dq.q[1],dq.q[2],dq.q[3]);
teuben's avatar
teuben committed
    }

    Matrix newTobj2w;
    mconjugate( &newTobj2w, &evTobj2w_, &Tincr,
		Tf2w, Tw2f, pcenterw, NULL );
    float scaling = vlength( (Point *)&evTobj2w_.m[0] );
    if(fabs(scaling - 1) < .01) {
teuben's avatar
teuben committed
	vgettranslation( &p, &newTobj2w );
	tfm2quat( &q, &newTobj2w );
	quat2tfm( &newTobj2w, &q );
	vscale( (Point *)&newTobj2w.m[0], scaling, (Point *)&newTobj2w.m[0] );
	vscale( (Point *)&newTobj2w.m[4], scaling, (Point *)&newTobj2w.m[4] );
	vscale( (Point *)&newTobj2w.m[8], scaling, (Point *)&newTobj2w.m[8] );
	vsettranslation( &newTobj2w, &p );
    }

    To2w( mytarget, &newTobj2w );
    redraw();
  }
}

void Fl_Gview::picksize( float width, float height )
{
  pickwidth_ = width;
  pickheight_ = height;
}

void Fl_Gview::pickbuffer( int nents, GLuint *buf )
{
  picknents_ = nents;
  pickbuf_ = buf;
}

slevy's avatar
slevy committed
void Fl_Gview::picker( void (*pickcb)(Fl_Gl_Window *, int, int, GLuint *, void *), void *arg )
teuben's avatar
teuben committed
{
  pickcb_ = pickcb;
  pickarg_ = arg;
}

slevy's avatar
slevy committed
void (*Fl_Gview::picker(void **argp))(Fl_Gl_Window *, int, int, GLuint *, void *)
teuben's avatar
teuben committed
{
  if(argp) *argp = pickarg_;
  return pickcb_;
}

int Fl_Gview::pickresults( int *nentp, GLuint **bufp )
{
  if(nentp) *nentp = picknents_;
  if(bufp) *bufp = pickbuf_;
  return pickhits_;
}

int Fl_Gview::do_pick( float xpick, float ypick )
{
  make_current();
  glSelectBuffer( picknents_, pickbuf_ );
  int ok = glRenderMode( GL_SELECT );
  if(ok != 0) {
    fprintf(stderr, "Trouble in do_pick: glRenderMode( GL_SELECT ) = %d\n", ok);
    ok = glRenderMode( GL_SELECT );
    fprintf(stderr, "Retry: %d\n", ok);
  }
  glPushName(0);

  inpick_ = 1;
  pickx_ = xpick;
  picky_ = ypick;
  draw();
  inpick_ = 0;

  glPopName();
  pickhits_ = glRenderMode( GL_RENDER );
  if(pickcb_)
    (*pickcb_)( this, pickhits_, picknents_, pickbuf_, pickarg_ );
  valid(0);
  return pickhits_;
}

char *Fl_Gview::dname( int id ) {
  static char name[32];
  if(id < 0) return "[c0]";
  int dno = withid( id );
  if(dno >= 0) {
    sprintf(name, drawers_[dno].name ? "[g%d %.22s]" : "[g%d]",
	id, drawers_[dno].name);
  } else {
    sprintf(name, "[g%d?]", id);
  }
  return name;
}

void Fl_Gview::initfrom( Fl_Gview *v ) {
    msg = v->msg;
    eventhook = v->eventhook;
    predraw = v->predraw;
    postdraw = v->postdraw;
    ndrawers_ = v->ndrawers_;
    maxdrawers_ = v->maxdrawers_;
    drawers_ = (drawer *)malloc(maxdrawers_ * sizeof(drawer));
    memcpy(drawers_, v->drawers_, ndrawers_ * sizeof(drawer));
    owncoords_ = v->owncoords_;
    persp_ = v->persp_;
    stereo_ = v->stereo_;
    stereosep_ = v->stereosep_;
    target_ = v->target_;
    movingtarget_ = v->movingtarget_;
    focallen_ = v->focallen_;
    halfyfov_ = v->halfyfov_;
    near_ = v->near_;
    far_ = v->far_;
    zspeed = v->zspeed;
    inpick_ = v->inpick_;
    pickwidth_ = v->pickwidth_, pickheight_ = v->pickheight_;
    picknents_ = v->picknents_;
    pickbuf_ = v->pickbuf_;
    pickarg_ = v->pickarg_;
    notify_ = v->notify_;
    notifyarg_ = v->notifyarg_;
    pcenw_ = v->pcenw_;
    bgcolor_ = v->bgcolor_;
    nullthresh_ = v->nullthresh_;
    hasfocus_ = 0;
    use_subc_ = v->use_subc_;
    Tc2subc_ = v->Tc2subc_;
    for(int i = 0; i < 4; i++)
	subclrbt_[i] = v->subclrbt_[i];
    nav( v->nav() );
    
    Tc2w( v->Tc2w() );
}