/*
 * Copyright (C) 1997-2009, R3vis Corporation.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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, or visit http://www.gnu.org/copyleft/lgpl.html.
 *
 * Original Contributor:
 *   Wes Bethel, R3vis Corporation, Marin County, California
 *   http://www.r3vis.com/
 * Additional Contributor(s):
 *
 * The OpenRM project is located at http://openrm.sourceforge.net/.
 */
/*
 * $Id: rmpick.c,v 1.11 2007/07/22 17:36:05 wes Exp $
 * Version: $Name: v180-alpha-02 $
 * $Revision: 1.11 $
 * $Log: rmpick.c,v $
 * Revision 1.11  2007/07/22 17:36:05  wes
 * Fix off-by-one Opengl matrix stack push/pop error in rmFramePick
 *
 * Revision 1.10  2005/06/06 02:04:29  wes
 * Lots of small additions to clean up compiler warnings.
 *
 * Revision 1.9  2005/02/19 16:22:50  wes
 * Distro sync and consolidation.
 *
 * Revision 1.8  2005/01/23 17:00:22  wes
 * Copyright updated to 2005.
 *
 * Revision 1.7  2004/09/28 00:46:56  wes
 * Use calloc to clear out the pick buffer before doing the pick.
 *
 * Revision 1.6  2004/03/30 14:13:31  wes
 * Fixed declarations and man page docs for several routines.
 *
 * Revision 1.5  2004/01/16 16:46:35  wes
 * Updated copyright line for 2004.
 *
 * Revision 1.4  2003/12/12 00:33:47  wes
 * Removed a bunch of dead code, commented out diagnostic messages.
 *
 * Revision 1.3  2003/11/16 16:19:40  wes
 * Removed "serial table" from picking operations. rmserial.c can probably
 * be removed from the source tree, and rmpick.c needs to have a bunch
 * of dead code surrounded by if 0's removed.
 *
 * Revision 1.2  2003/02/02 02:07:15  wes
 * Updated copyright to 2003.
 *
 * Revision 1.1.1.1  2003/01/28 02:15:23  wes
 * Manual rebuild of rm150 repository.
 *
 * Revision 1.13  2003/01/16 22:21:17  wes
 * Updated all source files to reflect new organization of header files:
 * all header files formerly located in include/rmaux, include/rmi, include/rmv
 * are now located in include/rm.
 *
 * Revision 1.12  2002/09/23 13:42:36  wes
 *
 * Established minimum size for feedback buffer; one user reported problems
 * when picking with a very small number of objects. Establishing a
 * minimum feedback buffer size appears to have fixed the problem. There
 * may still be a linger problem.
 *
 * Revision 1.11  2002/06/17 01:01:39  wes
 * Replaced fixed-size pick table with one that is completely dynamic -
 * no more realloc error messages. Replaced wonky #if DEBUG statements
 * with those that are consistent with the rest of the distribution.
 *
 * Revision 1.10  2002/04/30 19:33:05  wes
 * Updated copyright dates.
 *
 * Revision 1.9  2001/06/03 20:50:04  wes
 * Removed dead code.
 *
 * Revision 1.8  2001/05/26 14:37:49  wes
 * Added RMnode parameter to serialization code used in picking - this
 * will permit picking of scene graph subtrees that are disconnected
 * from rmRootNode().
 *
 * Revision 1.7  2001/03/31 17:12:39  wes
 * v1.4.0-alpha-2 checkin.
 *
 * Revision 1.6  2000/12/03 22:35:38  wes
 * Mods for thread safety.
 *
 * Revision 1.5  2000/08/23 23:27:10  wes
 * Added RM_TRUE as the default matrix stack initialization mode
 * to all pick routines. This is a placeholder.
 *
 * Revision 1.4  2000/05/14 23:38:38  wes
 * New param to private_rmSubTreeFrame() for OpenGL matrix stack
 * initialization during rendering/picking frame operations.
 *
 * Revision 1.3  2000/04/20 16:29:47  wes
 * Documentation additions/enhancements, some code rearragement.
 *
 * Revision 1.2  2000/02/29 23:43:53  wes
 * Compile warning cleanups.
 *
 * Revision 1.1.1.1  2000/02/28 21:29:40  wes
 * OpenRM 1.2 Checkin
 *
 * Revision 1.1.1.1  2000/02/28 17:18:48  wes
 * Initial entry - pre-RM120 release, source base for OpenRM 1.2.
 *
 */

/*
 * this file contains all the routines in RM that support picking.
 */

#include <rm/rm.h>
#include "rmprivat.h"
#include <GL/glu.h>

/* PRIVATE declarations */
static int     private_processHits (int nhits, GLuint *pick_buffer, RMpick *srmpick);
static int     private_processHitsList (int nhits, GLuint *pick_buffer, RMpick *list);
void           private_rmSetupPickMatrix (void);
void           private_rmNodePrimPickName (RMnode *r, int primindex);
void           private_rmNodeOnlyPickName (const RMnode *r);
static RMpick *private_rmPickListNew (int n);

/* pick coordinates */
static int      xpick_location = 0, ypick_location = 0;
static GLdouble pick_width = 5.0, pick_height = 5.0;

/* tmp 11/9/03 until we move the following routine to somewhere
   more permanent */
RMnode *
private_rmNodeFromIndex(int indx)
{
    extern RMcompMgrHdr *global_RMnodePool;
    int pageNum, offset;
    RMnode *t;

    pageNum = indx / NUM_ITEMS_PER_PAGE;
    offset = indx % NUM_ITEMS_PER_PAGE;

    /* put sanity check here */

    t = (RMnode *)(global_RMnodePool->objectPool[pageNum]) + offset;
    return t;
}

/*
 * ----------------------------------------------------
 * @Name rmFramePick
 @pstart
 RMpick * rmFramePick (RMpipe *renderPipe,
                       RMnode *subTree,
                       int xpick,
	               int ypick)
 @pend

 @astart

 RMpipe *renderPipe - the pipe upon which rendering and picking will occur.

 RMnode *subTree- the subtree to be drawn, and picked.
 
 int xpick, ypick - integer values indicating an (x,y) window
    location.
 @aend

 @dstart

 rmFramePick() performs object picking. A single RMpick object is
 returned representing the object closest to the viewer at the pixel
 location (xpick, ypick) in the display window. To obtain a list of
 all objects that appear at (xpick, ypick), not just the frontmost,
 use rmFramePickList().

 If no objects were picked, NULL is returned.

 Some scene parameters will generate pick hits. These include 2D and
 3D cameras. When the pick occurs at some (x,y) location that is not
 covered by any objects, a pick hit will be returned. In that case,
 the returned RMpick object's RMnode attribute will point to the
 RMnode that has a camera that generated the pick hit.

 With the RMpick object returned by rmPickFrame or rmPickFrameList,
 use the routine rmPickedNode to obtain a handle to the RMnode that
 was picked; rmPickedPrimitive to obtain a handle to the RMprimitive
 that was picked; rmPickedNodeName() to obtain the (character string)
 name of the node that was picked; and rmPickedPrimitiveZval to obtain
 the NDC z-coordinate of the primitive that was picked.

 June 2002: the RMpick object returned by rmFramePick should be deleted
 with rmPickDelete when it is no longer needed. (In versions of RM earlier
 than 1.4.2, a pointer to static memory was returned, and apps were 
 prohibited from free'ing that memory. This has changed as of v1.4.2).

 See the RM demo programs for example usage, particularly "trans2d.c", 
 which performs picking in 2D, and "pickTest.c", which performs 
 picking in 3D. The RM demo program "pickListTest.c" exercises
 rmFramePickList() in 3D.

 @dend
 * ----------------------------------------------------
 */
RMpick *
rmFramePick (RMpipe *renderPipe,
	     RMnode *subTree,
	     int xpick,
	     int ypick)
{
    int    i, hits = 0;
    RMenum render3DOpaqueEnable = RM_TRUE;
    RMenum render3DTransparentEnable = RM_TRUE;
    RMenum render2DEnable = RM_TRUE;
    RMpipe *usePipe;
    RMpick *pickReturn=NULL;
    int pickBufferSize;
    int totalNodes, totalPrims;
    unsigned int *pick_buffer;
    
    int    private_rmTrueFilterfunc(RMnode *r);

    xpick_location = xpick;
    ypick_location = ypick;


    /* 11/9/03 wes - removed serial table from picking process */
    {
	extern RMcompMgrHdr *global_RMnodePool, *global_RMprimitivePool;
	totalNodes = global_RMnodePool->numAlloc;
	totalPrims = global_RMprimitivePool->numAlloc;
    }


#if (DEBUG_LEVEL & DEBUG_TRACE)
    printf(" rmFramePick: #nodes = %d, #prims = %d \n", totalNodes, totalPrims);
#endif

    pickBufferSize = totalNodes + totalPrims;
    pickBufferSize = RM_MAX(32, pickBufferSize+1);
    
    pick_buffer = (unsigned int *)calloc(sizeof(unsigned int)*pickBufferSize, sizeof(unsigned int));

    /* set the render mode to pick objects when the scene is rendered. */
    glSelectBuffer(pickBufferSize, pick_buffer);
    glRenderMode(GL_SELECT);

    glInitNames();
    
    glPushName(-1);

    /* render stuff */
    usePipe = renderPipe;
    private_rmSubTreeFrame(usePipe, subTree, GL_SELECT, private_rmNodeOnlyPickName, private_rmNodePrimPickName, private_rmTrueFilterfunc, NULL, render3DOpaqueEnable, render3DTransparentEnable, render2DEnable, RM_TRUE); /* always initialize matrix stack here? */

    /* get the hits back from OpenGL */
    hits = glRenderMode(GL_RENDER);

    glMatrixMode(GL_MODELVIEW);

#if (DEBUG_LEVEL & DEBUG_TRACE)
    fprintf(stderr, " %d hits from pick operation. \n", hits);
#endif
    /* process hits */
    if (hits > 0)
    {
       pickReturn = private_rmPickListNew(1);
       i = private_processHits(hits, pick_buffer, pickReturn);
       pickReturn->node = private_rmNodeFromIndex(pickReturn->index);
    }

    free((void *)pick_buffer);

    return(pickReturn);
}


/*
 * ----------------------------------------------------
 * @Name rmFramePickList
 @pstart
 int rmFramePickList (RMpipe *renderPipe,
                      RMnode *subTree,
                      int xpick,
		      int ypick,
		      RMpick **listReturn)
 @pend

 @astart

 RMpipe *renderPipe - the pipe upon which rendering and picking will occur.

 RMnode *subTree- the subtree to be drawn, and picked.
 
 int xpick, ypick - integer values indicating an (x,y) pixel
    coordinate within a window (input).

 RMpick **listReturn - a handle to an RMpick pointer (modified,
    return).
 @aend

 @dstart

 Performs a pick operation, returning a list of all objects
 encountered at the (x,y) pixel location in the render window through
 the parameter listReturn. The number of objects in that list is
 returned on the stack, or zero is returned if there were no objects
 picked.

 The list of objects that is returned to the caller is sorted in
 ascending order of the z-coordinate of the picked object. Therefore,
 listReturn[0] contains information about the object closest to the
 viewer, and listReturn[nhits-1] contains info about the object
 farthest from the viewer.

 The RMpick objects returned through listReturn should be freed when
 no longer needed by using rmPickListDelete().

 @dend
 * ----------------------------------------------------
 */
int
rmFramePickList (RMpipe *renderPipe,
		 RMnode *subTree,
		 int xpick,
		 int ypick,
		 RMpick **list_return)
{
    int     hits = 0;
    RMpick *list;
    RMenum  render3DOpaqueEnable = RM_TRUE;
    RMenum  render3DTransparentEnable = RM_TRUE;
    RMenum  render2DEnable = RM_TRUE;
    unsigned int *pick_buffer;
    int totalNodes, totalPrims, pickBufSize;
    
    xpick_location = xpick;
    ypick_location = ypick;

    {
	extern RMcompMgrHdr *global_RMnodePool, *global_RMprimitivePool;
	totalNodes = global_RMnodePool->numAlloc;
	totalPrims = global_RMprimitivePool->numAlloc;
    }
    
    pickBufSize = totalNodes + totalPrims;
    pickBufSize = RM_MAX(32, pickBufSize);

#if (DEBUG_LEVEL & DEBUG_TRACE)
    printf(" rmFramePickList totalNodes = %d, totalPrims = %d \n", totalNodes, totalPrims);
#endif

    pick_buffer = (unsigned int *)malloc((sizeof(unsigned int)*pickBufSize));

    /* set the render mode to pick objects when the scene is rendered */
    glSelectBuffer(pickBufSize, pick_buffer);

    glRenderMode(GL_SELECT);

    glInitNames();
    
    glPushName(-1);

    /* render stuff */
    private_rmSubTreeFrame(renderPipe, subTree, GL_SELECT, private_rmNodeOnlyPickName, private_rmNodePrimPickName, private_rmTrueFilterfunc, NULL, render3DOpaqueEnable, render3DTransparentEnable, render2DEnable, RM_TRUE); /* always initialize matrix stack? */

    /* get the hits back from OpenGL */
    hits = glRenderMode(GL_RENDER);

#if (DEBUG_LEVEL & DEBUG_TRACE)
    fprintf(stderr, " %d hits from pick operation. \n", hits);
#endif

    /* process hits */
    if (hits > 0)
    {
        int i;

        list = private_rmPickListNew(hits);
	private_processHitsList(hits, pick_buffer, list);

#if (DEBUG_LEVEL & DEBUG_TRACE)
	fprintf(stderr, " there were %d hits. \n", hits);
#endif
	for (i = 0; i < hits; i++)
	{
	    list[i].node = private_rmNodeFromIndex(list[i].index);
	    
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr, " %s %g \n", rmNodeGetName(list[i].node), list[i].zval);
#endif
	}
    }
    else
        list = NULL;

    free((void *)pick_buffer);
    *list_return = list;
    return(hits);
}


/*
 * ----------------------------------------------------
 * @Name  rmPickDelete
 @pstart
 RMenum rmPickDelete (RMpick *toDelete)
 @pend

 @astart
 RMpick *toDelete - a handle to a single or flat array of RMpick
    objects.
 @aend

 @dstart

 Use this routine to free resources associated with an RMpick object
 or flat array of RMpick objects.

 When rmFramePick() and rmFramePickList() finish, each returns an RMpick *
 object (rmFramePick) or flat array of RMpick objects (rmFramePickList).  
 When applications are finished with these RMpick objects, they should be 
 deleted using rmPickDelete().

 @dend
 * ----------------------------------------------------
 */
RMenum
rmPickDelete (RMpick *list)
{
    if (RM_ASSERT(list, "rmPickDelete() error: the input RMpick list is NULL") == RM_WHACKED)
	return(RM_WHACKED);
    
    free((void *)list);
    return(RM_CHILL);
}


/*
 * ----------------------------------------------------
 * @Name rmPickedNode
 @pstart
 RMnode * rmPickedNode (const RMpick *toQuery)
 @pend

 @astart
 const RMpick *toQuery - a handle to an RMpick object (input).
 @aend

 @dstart

 Returns to the caller the RMnode handle contained within an RMpick
 object, or NULL upon failure.

 The RMpick object returned from rmFramePick (or objects returned from
 rmFramePickList) can be queried to determine the node, primitive,
 node name or z-coordinate of the picked object.

 @dend
 * ----------------------------------------------------
 */
RMnode *
rmPickedNode (const RMpick *p)
{
    if (p == NULL)
	return(NULL);

    return(p->node);
}


/*
 * ----------------------------------------------------
 * @Name rmPickedPrimitive
 @pstart
 int rmPickedPrimitive (const RMpick *toQuery)
 @pend

 @astart
 const RMpick *toQuery - a handle to an RMpick object (input).
 @aend

 @dstart

 The RMpick object returned from rmFramePick (or objects returned from
 rmFramePickList) can be queried to determine the node, primitive,
 node name or z-coordinate of the picked object.

 This routine returns the index of the primitive that was
 picked. Combining the primitive index with the RMnode handle of the
 picked object allows applications to determine which primitive was
 picked.

 Returns a non-negative integer upon success, or -1 upon failure.

 @dend
 * ----------------------------------------------------
 */
int
rmPickedPrimitive (const RMpick *p)
{
    if (p == NULL)
        return(-1);
    else
        return(p->prim_index);
}


/*
 * ----------------------------------------------------
 * @Name rmPickedNodeName
 @pstart
 char * rmPickedNodeName (const RMpick *toQuery)
 @pend

 @astart
 const RMpick *toQuery - a handle to an RMpick object (input).
 @aend

 @dstart

 Returns to the caller the node name of the RMnode handle contained
 within an RMpick object, or NULL upon failure.

 The RMpick object returned from rmFramePick (or objects returned from
 rmFramePickList) can be queried to determine the node, primitive,
 node name or z-coordinate of the picked object.

 This routine is functionally equivalent to first obtaining the RMnode
 handle using rmPickedNode() followed by rmNodeGetName().

 @dend
 * ----------------------------------------------------
 */
char *
rmPickedNodeName (const RMpick *p)
{
    if (p == NULL)
	return(NULL);

    return(rmNodeGetName(rmPickedNode(p)));

}


/*
 * ----------------------------------------------------
 * @Name rmPickedPrimitiveZval
 @pstart
 float rmPickedPrimitiveZval (const RMpick *toQuery)
 @pend

 @astart
 const RMpick *toQuery - a handle to an RMpick object (input).
 @aend

 @dstart

 Returns to the caller the NDC z-coordinate of the RMprimitive at the
 (x,y) screen location where picking occured. A value of zero is
 returned upon error, when no picking occurred (RMpick NULL).

 The RMpick object returned from rmFramePick (or objects returned from
 rmFramePickList) can be queried to determine the node, primitive,
 node name or z-coordinate of the picked object.

 @dend
 * ----------------------------------------------------
 */
float
rmPickedPrimitiveZval (const RMpick *p)
{
    if (RM_ASSERT(p, "rmPrimPickedZval() error: the input RMpick object is NULL") == RM_WHACKED)
	return(0.0);

    return(p->zval);
}


/* PRIVATE
 *
 * this routine takes a pile of pick hits and returns the one with
 * the smallest z-coordinate. nhits and pick_buffer are provided by
 * the GL_FEEDBACK mechanism, srmpick is the destination RMpick object
 * that is filled in by this routine.
 */
static int
private_processHits (int nhits,
		     GLuint *pick_buffer,
		     RMpick *srmpick)
{
    int     i, j;
    int     use_it;
    float   zval;
    float   zcur = RM_MAXFLOAT;
    GLuint *ptr;
    GLuint  names;
    GLuint  zscale = (~0u);
    GLuint  ival = 0;

    ptr = pick_buffer;
    for (i = 0; i < nhits; i++)
    {
	use_it = 0;
        names = *ptr;
#if (DEBUG_LEVEL & DEBUG_TRACE)
	fprintf(stderr, " number of names for this hit = %d \n", names);
#endif
	ptr++;
	
	zval = *ptr;
	zval = zval / (float)zscale;
#if (DEBUG_LEVEL & DEBUG_TRACE)
	fprintf(stderr, "\tz1 = %g;", zval);  
	fprintf(stderr, "\tz1 = %u;", *ptr);  
#endif
	ptr++;

	if (zval <= zcur)	/* if they're the same, use the one that was drawn last */
	{
	    use_it = 1;
	    zcur = zval;
	}
	
	zval = *ptr;
	zval = zval / (float)zscale;
#if (DEBUG_LEVEL & DEBUG_TRACE)
	fprintf(stderr, " z2 = %g \n", zval); 
	fprintf(stderr, " z2 = %u \n", *ptr);  
#endif
	ptr++;

	/* i'm going to assume that we'll use the first name and
	 ignore the rest, and that if we got here, that "names" >= 1*/
#if (DEBUG_LEVEL & DEBUG_TRACE)
	fprintf(stderr, "\tthe names are: \n");
#endif
	if (use_it)
	    ival = *ptr;
	for (j = 0; j < (int)(names); j++)
	{
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr,"  %d ", *ptr);
#endif
	    ptr++;
	}
#if (DEBUG_LEVEL & DEBUG_TRACE)
	fprintf(stderr," \n");
#endif
    }

    /* from ival, we need to extract the object index and the prim index */
    {
	int opcode = RMPASSTHRU_GET_OPCODE(ival);

	if (opcode != RM_PASSTHRU_OPCODE_IDENTIFIER)
	    rmError(" expected an identifier opcode in a pick operation. \n");
    }
    srmpick->index = RMPASSTHRU_GET_OBJECT_ID(ival);
    srmpick->prim_index = RMPASSTHRU_GET_PRIM_ID(ival);
    srmpick->zval = zcur;

    return(1);
}

/* PRIVATE */
static int
sortPickFunc( const void *p1, const void *p2)
{
    RMpick *a, *b;
    float z1, z2;
    
    a = (RMpick *)p1;
    b = (RMpick *)p2;

    z1 = rmPickedPrimitiveZval(a);
    z2 = rmPickedPrimitiveZval(b);
    
    if (z1 < z2)
	return -1;
    else if (z1 > z2)
	return 1;
    else
	return 0;
}

/* PRIVATE
 *
 * analagous to processHits, private_processHitsList will return in RMpick *list
 * _all_ objects that were picked regardless of z-value. nhits and pick_buffer
 * are supplied by the OpenGL feedback mechanism, and list[] must be
 * "nhits" in size.
 */
static int
private_processHitsList (int nhits,
			 GLuint *pick_buffer,
			 RMpick *list)
{
    int     i, j;
    int     ival, use_it;
    float   zval;
    GLuint *ptr;
    GLuint  names;
    GLuint  zscale = (~0u);

    ptr = pick_buffer;

    for (i = 0; i < nhits; i++)
    {
	use_it = 0;
        names = *ptr;
#if (DEBUG_LEVEL & DEBUG_TRACE)
	fprintf(stderr, " number of names for this hit = %d \n", names);
#endif
	ptr++;
	
	zval = *ptr;
	zval = zval / (float)zscale;
#if (DEBUG_LEVEL & DEBUG_TRACE)
	fprintf(stderr, "\tz1 = %g;", zval);  
	fprintf(stderr, "\tz1 = %u;", *ptr);  
#endif
	ptr++;

#if (DEBUG_LEVEL & DEBUG_TRACE)
	fprintf(stderr, " z2 = %g \n", zval); 
	fprintf(stderr, " z2 = %u \n", *ptr);  
#endif
	ptr++;

#if (DEBUG_LEVEL & DEBUG_TRACE)
	fprintf(stderr, "\tthe names are: \n");
#endif
	ival = *ptr;
	{
	    int opcode = RMPASSTHRU_GET_OPCODE(ival);

	    if (opcode != RM_PASSTHRU_OPCODE_IDENTIFIER)
		rmError(" expected an identifier opcode in a pick operation. \n");
	}
	list[i].index = RMPASSTHRU_GET_OBJECT_ID(ival);
	list[i].prim_index = RMPASSTHRU_GET_PRIM_ID(ival);
	list[i].zval = zval;

	for (j = 0; j < (int)(names); j++)
	{
#if (DEBUG_LEVEL & DEBUG_TRACE)
	    fprintf(stderr, " %d ", *ptr);
#endif
	    ptr++;
	}
#if (DEBUG_LEVEL & DEBUG_TRACE)
	fprintf(stderr, " \n");
#endif
    }

    /* OK. we now have the hits list. sort it in increasing Z */
    qsort(list, nhits,  sizeof(RMpick), sortPickFunc);
	  
    return(1);
}


/* PRIVATE */
void
private_rmComputePickMatrix (RMstate *s,
			     RMmatrix *pickReturn)
{
    float    sx, sy, tx, ty;
    RMmatrix m;
 
    rmMatrixIdentity(&m);

    sx = s->vp[2] / pick_width;
    sy = s->vp[3] / pick_height;
    tx = (s->vp[2] + (2.0F * (s->vp[0] - (float)xpick_location))) / pick_width;
    ty = (s->vp[3] + (2.0F * (s->vp[1] - (s->h - (float)ypick_location)))) / pick_height;

    m.m[0][0] = sx;
    m.m[1][1] = sy;
    m.m[3][0] = tx;
    m.m[3][1] = ty;

    *pickReturn = m;
}


/* PRIVATE
 *
 * private routine to set up the "pick matrix". see the man page
 * for gluPickMatrix. the basic idea is that the projection matrix
 * is tweaked so that only those objects that fall within a very
 * small region are "rendered." rendering, in the case of picking,
 * means that a GL_FEEDBACK token is generated, rather than pixels
 * being painted.
 */
void
private_rmSetupPickMatrix (void)
{
    rmError(" private_rmSetupPickMatrix is deprecated!");
#if 0
    int     w, h;
    GLint   viewport[4];
    RMpipe *pipe = private_rmPipeGetCurrent();
    
    glGetIntegerv(GL_VIEWPORT, viewport);
    rmPipeGetWindowSize(pipe, &w, &h);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPickMatrix((GLdouble)xpick_location, (GLdouble)ypick_location, pick_width, pick_height, viewport);

    /* while we're here before any rendering happens, set up the serialized list of objects */
    glMatrixMode(GL_MODELVIEW);
#endif
}


/* PRIVATE
 *
 * the GL_FEEDBACK mechanism allows for applications to place
 * application-specific information in the "feedback token."
 * RM builds a serialized representation of the scene graph, then
 * as each node & primitive is rendered, a "bread crumb" is
 * encoded in the feedback token. the routine below will encode
 * the node & prim info into a token (glLoadName). 
 *
 * the current_lod parm is deprecated and needs to be removed.
 */
void
private_rmNodePrimPickName (RMnode *r, 
			    int primindex)
{
    int          indx;
    unsigned int token = 0;

    indx = r->compListIndx;

    RMPASSTHRU_ENCODE_OPCODE(token, RM_PASSTHRU_OPCODE_IDENTIFIER);
    RMPASSTHRU_ENCODE_OBJECT_ID(token, indx);
    RMPASSTHRU_ENCODE_PRIM_ID(token, primindex);
    
    glLoadName((GLuint)token);
}


/* PRIVATE
 *
 * private_rmNodeOnlyPickName encodes ONLY the node name into the feedback token
 */
void
private_rmNodeOnlyPickName (const RMnode *r)
{
    int          indx;
    unsigned int token = 0;

    indx = r->compListIndx;

    RMPASSTHRU_ENCODE_OPCODE(token, RM_PASSTHRU_OPCODE_IDENTIFIER);
    RMPASSTHRU_ENCODE_OBJECT_ID(token, indx);

    glLoadName((GLuint)token);
}


/* PRIVATE 
 *
 * this routine, while public in appearance, is used at the present time
 * only within RM itself to allocate space for RMpick objects. the routine
 * rmFramePickList uses this routine. applications will probably never
 * need to use this routine, as rmFramePickList() calls this routine on
 * behalf of the application.
 *
 * January 2000, there is presently no scenario in which applications using
 * RM will ever need to call private_rmPickListNew(). Because of this
 * condition, this function is static to this file. 
 * There is no reason why it couldn't be moved to rmpublic.h if needed.
 */
static RMpick *
private_rmPickListNew (int n)
{
    RMpick *t = (RMpick *)malloc(sizeof(RMpick) * n);
    memset((void *)t, 0, sizeof(RMpick) * n);

    return(t);
}
/* EOF */
