1 /*
2  * Copyright (C) 1997-2005, R3vis Corporation.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17  * USA, or visit http://www.gnu.org/copyleft/lgpl.html.
18  *
19  * Original Contributor:
20  *   Wes Bethel, R3vis Corporation, Marin County, California
21  * Additional Contributor(s):
22  *
23  * The OpenRM project is located at http://openrm.sourceforge.net/.
24  */
25 /*
26  * $Id: rmpick.c,v 1.10 2005/06/06 02:04:29 wes Exp $
27  * Version: $Name: OpenRM-1-6-0-2-RC2 $
28  * $Revision: 1.10 $
29  * $Log: rmpick.c,v $
30  * Revision 1.10  2005/06/06 02:04:29  wes
31  * Lots of small additions to clean up compiler warnings.
32  *
33  * Revision 1.9  2005/02/19 16:22:50  wes
34  * Distro sync and consolidation.
35  *
36  * Revision 1.8  2005/01/23 17:00:22  wes
37  * Copyright updated to 2005.
38  *
39  * Revision 1.7  2004/09/28 00:46:56  wes
40  * Use calloc to clear out the pick buffer before doing the pick.
41  *
42  * Revision 1.6  2004/03/30 14:13:31  wes
43  * Fixed declarations and man page docs for several routines.
44  *
45  * Revision 1.5  2004/01/16 16:46:35  wes
46  * Updated copyright line for 2004.
47  *
48  * Revision 1.4  2003/12/12 00:33:47  wes
49  * Removed a bunch of dead code, commented out diagnostic messages.
50  *
51  * Revision 1.3  2003/11/16 16:19:40  wes
52  * Removed "serial table" from picking operations. rmserial.c can probably
53  * be removed from the source tree, and rmpick.c needs to have a bunch
54  * of dead code surrounded by if 0's removed.
55  *
56  * Revision 1.2  2003/02/02 02:07:15  wes
57  * Updated copyright to 2003.
58  *
59  * Revision 1.1.1.1  2003/01/28 02:15:23  wes
60  * Manual rebuild of rm150 repository.
61  *
62  * Revision 1.13  2003/01/16 22:21:17  wes
63  * Updated all source files to reflect new organization of header files:
64  * all header files formerly located in include/rmaux, include/rmi, include/rmv
65  * are now located in include/rm.
66  *
67  * Revision 1.12  2002/09/23 13:42:36  wes
68  *
69  * Established minimum size for feedback buffer; one user reported problems
70  * when picking with a very small number of objects. Establishing a
71  * minimum feedback buffer size appears to have fixed the problem. There
72  * may still be a linger problem.
73  *
74  * Revision 1.11  2002/06/17 01:01:39  wes
75  * Replaced fixed-size pick table with one that is completely dynamic -
76  * no more realloc error messages. Replaced wonky #if DEBUG statements
77  * with those that are consistent with the rest of the distribution.
78  *
79  * Revision 1.10  2002/04/30 19:33:05  wes
80  * Updated copyright dates.
81  *
82  * Revision 1.9  2001/06/03 20:50:04  wes
83  * Removed dead code.
84  *
85  * Revision 1.8  2001/05/26 14:37:49  wes
86  * Added RMnode parameter to serialization code used in picking - this
87  * will permit picking of scene graph subtrees that are disconnected
88  * from rmRootNode().
89  *
90  * Revision 1.7  2001/03/31 17:12:39  wes
91  * v1.4.0-alpha-2 checkin.
92  *
93  * Revision 1.6  2000/12/03 22:35:38  wes
94  * Mods for thread safety.
95  *
96  * Revision 1.5  2000/08/23 23:27:10  wes
97  * Added RM_TRUE as the default matrix stack initialization mode
98  * to all pick routines. This is a placeholder.
99  *
100  * Revision 1.4  2000/05/14 23:38:38  wes
101  * New param to private_rmSubTreeFrame() for OpenGL matrix stack
102  * initialization during rendering/picking frame operations.
103  *
104  * Revision 1.3  2000/04/20 16:29:47  wes
105  * Documentation additions/enhancements, some code rearragement.
106  *
107  * Revision 1.2  2000/02/29 23:43:53  wes
108  * Compile warning cleanups.
109  *
110  * Revision 1.1.1.1  2000/02/28 21:29:40  wes
111  * OpenRM 1.2 Checkin
112  *
113  * Revision 1.1.1.1  2000/02/28 17:18:48  wes
114  * Initial entry - pre-RM120 release, source base for OpenRM 1.2.
115  *
116  */
117 
118 /*
119  * this file contains all the routines in RM that support picking.
120  */
121 
122 #include <rm/rm.h>
123 #include "rmprivat.h"
124 #include <GL/glu.h>
125 
126 /* PRIVATE declarations */
127 static int     private_processHits (int nhits, GLuint *pick_buffer, RMpick *srmpick);
128 static int     private_processHitsList (int nhits, GLuint *pick_buffer, RMpick *list);
129 void           private_rmSetupPickMatrix (void);
130 void           private_rmNodePrimPickName (RMnode *r, int primindex);
131 void           private_rmNodeOnlyPickName (const RMnode *r);
132 static RMpick *private_rmPickListNew (int n);
133 
134 /* pick coordinates */
135 static int      xpick_location = 0, ypick_location = 0;
136 static GLdouble pick_width = 5.0, pick_height = 5.0;
137 
138 /* tmp 11/9/03 until we move the following routine to somewhere
139    more permanent */
140 RMnode *
private_rmNodeFromIndex(int indx)141 private_rmNodeFromIndex(int indx)
142 {
143     extern RMcompMgrHdr *global_RMnodePool;
144     int pageNum, offset;
145     RMnode *t;
146 
147     pageNum = indx / NUM_ITEMS_PER_PAGE;
148     offset = indx % NUM_ITEMS_PER_PAGE;
149 
150     /* put sanity check here */
151 
152     t = (RMnode *)(global_RMnodePool->objectPool[pageNum]) + offset;
153     return t;
154 }
155 
156 /*
157  * ----------------------------------------------------
158  * @Name rmFramePick
159  @pstart
160  RMpick * rmFramePick (RMpipe *renderPipe,
161                        RMnode *subTree,
162                        int xpick,
163 	               int ypick)
164  @pend
165 
166  @astart
167 
168  RMpipe *renderPipe - the pipe upon which rendering and picking will occur.
169 
170  RMnode *subTree- the subtree to be drawn, and picked.
171 
172  int xpick, ypick - integer values indicating an (x,y) window
173     location.
174  @aend
175 
176  @dstart
177 
178  rmFramePick() performs object picking. A single RMpick object is
179  returned representing the object closest to the viewer at the pixel
180  location (xpick, ypick) in the display window. To obtain a list of
181  all objects that appear at (xpick, ypick), not just the frontmost,
182  use rmFramePickList().
183 
184  If no objects were picked, NULL is returned.
185 
186  Some scene parameters will generate pick hits. These include 2D and
187  3D cameras. When the pick occurs at some (x,y) location that is not
188  covered by any objects, a pick hit will be returned. In that case,
189  the returned RMpick object's RMnode attribute will point to the
190  RMnode that has a camera that generated the pick hit.
191 
192  With the RMpick object returned by rmPickFrame or rmPickFrameList,
193  use the routine rmPickedNode to obtain a handle to the RMnode that
194  was picked; rmPickedPrimitive to obtain a handle to the RMprimitive
195  that was picked; rmPickedNodeName() to obtain the (character string)
196  name of the node that was picked; and rmPickedPrimitiveZval to obtain
197  the NDC z-coordinate of the primitive that was picked.
198 
199  June 2002: the RMpick object returned by rmFramePick should be deleted
200  with rmPickDelete when it is no longer needed. (In versions of RM earlier
201  than 1.4.2, a pointer to static memory was returned, and apps were
202  prohibited from free'ing that memory. This has changed as of v1.4.2).
203 
204  See the RM demo programs for example usage, particularly "trans2d.c",
205  which performs picking in 2D, and "pickTest.c", which performs
206  picking in 3D. The RM demo program "pickListTest.c" exercises
207  rmFramePickList() in 3D.
208 
209  @dend
210  * ----------------------------------------------------
211  */
212 RMpick *
rmFramePick(RMpipe * renderPipe,RMnode * subTree,int xpick,int ypick)213 rmFramePick (RMpipe *renderPipe,
214 	     RMnode *subTree,
215 	     int xpick,
216 	     int ypick)
217 {
218     int    i, hits = 0;
219     RMenum render3DOpaqueEnable = RM_TRUE;
220     RMenum render3DTransparentEnable = RM_TRUE;
221     RMenum render2DEnable = RM_TRUE;
222     RMpipe *usePipe;
223     RMpick *pickReturn=NULL;
224     int pickBufferSize;
225     int totalNodes, totalPrims;
226     unsigned int *pick_buffer;
227 
228     int    private_rmTrueFilterfunc(RMnode *r);
229 
230     xpick_location = xpick;
231     ypick_location = ypick;
232 
233 
234     /* 11/9/03 wes - removed serial table from picking process */
235     {
236 	extern RMcompMgrHdr *global_RMnodePool, *global_RMprimitivePool;
237 	totalNodes = global_RMnodePool->numAlloc;
238 	totalPrims = global_RMprimitivePool->numAlloc;
239     }
240 
241 
242 #if (DEBUG_LEVEL & DEBUG_TRACE)
243     printf(" rmFramePick: #nodes = %d, #prims = %d \n", totalNodes, totalPrims);
244 #endif
245 
246     pickBufferSize = totalNodes + totalPrims;
247     pickBufferSize = RM_MAX(32, pickBufferSize+1);
248 
249     pick_buffer = (unsigned int *)calloc(sizeof(unsigned int)*pickBufferSize, sizeof(unsigned int));
250 
251     /* set the render mode to pick objects when the scene is rendered. */
252     glSelectBuffer(pickBufferSize, pick_buffer);
253     glRenderMode(GL_SELECT);
254 
255     glInitNames();
256 
257     glPushName(-1);
258 
259     /* render stuff */
260     usePipe = renderPipe;
261     private_rmSubTreeFrame(usePipe, subTree, GL_SELECT, private_rmNodeOnlyPickName, private_rmNodePrimPickName, private_rmTrueFilterfunc, NULL, render3DOpaqueEnable, render3DTransparentEnable, render2DEnable, RM_TRUE); /* always initialize matrix stack here? */
262 
263     /* get the hits back from OpenGL */
264     hits = glRenderMode(GL_RENDER);
265 
266     glMatrixMode(GL_MODELVIEW);
267 
268 #if (DEBUG_LEVEL & DEBUG_TRACE)
269     fprintf(stderr, " %d hits from pick operation. \n", hits);
270 #endif
271     /* process hits */
272     if (hits > 0)
273     {
274        pickReturn = private_rmPickListNew(1);
275        i = private_processHits(hits, pick_buffer, pickReturn);
276        pickReturn->node = private_rmNodeFromIndex(pickReturn->index);
277     }
278 
279     free((void *)pick_buffer);
280 
281     return(pickReturn);
282 }
283 
284 
285 /*
286  * ----------------------------------------------------
287  * @Name rmFramePickList
288  @pstart
289  int rmFramePickList (RMpipe *renderPipe,
290                       RMnode *subTree,
291                       int xpick,
292 		      int ypick,
293 		      RMpick **listReturn)
294  @pend
295 
296  @astart
297 
298  RMpipe *renderPipe - the pipe upon which rendering and picking will occur.
299 
300  RMnode *subTree- the subtree to be drawn, and picked.
301 
302  int xpick, ypick - integer values indicating an (x,y) pixel
303     coordinate within a window (input).
304 
305  RMpick **listReturn - a handle to an RMpick pointer (modified,
306     return).
307  @aend
308 
309  @dstart
310 
311  Performs a pick operation, returning a list of all objects
312  encountered at the (x,y) pixel location in the render window through
313  the parameter listReturn. The number of objects in that list is
314  returned on the stack, or zero is returned if there were no objects
315  picked.
316 
317  The list of objects that is returned to the caller is sorted in
318  ascending order of the z-coordinate of the picked object. Therefore,
319  listReturn[0] contains information about the object closest to the
320  viewer, and listReturn[nhits-1] contains info about the object
321  farthest from the viewer.
322 
323  The RMpick objects returned through listReturn should be freed when
324  no longer needed by using rmPickListDelete().
325 
326  @dend
327  * ----------------------------------------------------
328  */
329 int
rmFramePickList(RMpipe * renderPipe,RMnode * subTree,int xpick,int ypick,RMpick ** list_return)330 rmFramePickList (RMpipe *renderPipe,
331 		 RMnode *subTree,
332 		 int xpick,
333 		 int ypick,
334 		 RMpick **list_return)
335 {
336     int     hits = 0;
337     RMpick *list;
338     RMenum  render3DOpaqueEnable = RM_TRUE;
339     RMenum  render3DTransparentEnable = RM_TRUE;
340     RMenum  render2DEnable = RM_TRUE;
341     unsigned int *pick_buffer;
342     int totalNodes, totalPrims, pickBufSize;
343 
344     xpick_location = xpick;
345     ypick_location = ypick;
346 
347     {
348 	extern RMcompMgrHdr *global_RMnodePool, *global_RMprimitivePool;
349 	totalNodes = global_RMnodePool->numAlloc;
350 	totalPrims = global_RMprimitivePool->numAlloc;
351     }
352 
353     pickBufSize = totalNodes + totalPrims;
354     pickBufSize = RM_MAX(32, pickBufSize);
355 
356 #if (DEBUG_LEVEL & DEBUG_TRACE)
357     printf(" rmFramePickList totalNodes = %d, totalPrims = %d \n", totalNodes, totalPrims);
358 #endif
359 
360     pick_buffer = (unsigned int *)malloc((sizeof(unsigned int)*pickBufSize));
361 
362     /* set the render mode to pick objects when the scene is rendered */
363     glSelectBuffer(pickBufSize, pick_buffer);
364 
365     glRenderMode(GL_SELECT);
366 
367     glInitNames();
368 
369     glPushName(-1);
370 
371     /* render stuff */
372     private_rmSubTreeFrame(renderPipe, subTree, GL_SELECT, private_rmNodeOnlyPickName, private_rmNodePrimPickName, private_rmTrueFilterfunc, NULL, render3DOpaqueEnable, render3DTransparentEnable, render2DEnable, RM_TRUE); /* always initialize matrix stack? */
373 
374     /* get the hits back from OpenGL */
375     hits = glRenderMode(GL_RENDER);
376 
377     /* pop off projection matrix */
378     glPopMatrix();
379     glMatrixMode(GL_MODELVIEW);
380 
381 #if (DEBUG_LEVEL & DEBUG_TRACE)
382     fprintf(stderr, " %d hits from pick operation. \n", hits);
383 #endif
384 
385     /* process hits */
386     if (hits > 0)
387     {
388         int i;
389 
390         list = private_rmPickListNew(hits);
391 	private_processHitsList(hits, pick_buffer, list);
392 
393 #if (DEBUG_LEVEL & DEBUG_TRACE)
394 	fprintf(stderr, " there were %d hits. \n", hits);
395 #endif
396 	for (i = 0; i < hits; i++)
397 	{
398 	    list[i].node = private_rmNodeFromIndex(list[i].index);
399 
400 #if (DEBUG_LEVEL & DEBUG_TRACE)
401 	    fprintf(stderr, " %s %g \n", rmNodeGetName(list[i].node), list[i].zval);
402 #endif
403 	}
404     }
405     else
406         list = NULL;
407 
408     free((void *)pick_buffer);
409     *list_return = list;
410     return(hits);
411 }
412 
413 
414 /*
415  * ----------------------------------------------------
416  * @Name  rmPickDelete
417  @pstart
418  RMenum rmPickDelete (RMpick *toDelete)
419  @pend
420 
421  @astart
422  RMpick *toDelete - a handle to a single or flat array of RMpick
423     objects.
424  @aend
425 
426  @dstart
427 
428  Use this routine to free resources associated with an RMpick object
429  or flat array of RMpick objects.
430 
431  When rmFramePick() and rmFramePickList() finish, each returns an RMpick *
432  object (rmFramePick) or flat array of RMpick objects (rmFramePickList).
433  When applications are finished with these RMpick objects, they should be
434  deleted using rmPickDelete().
435 
436  @dend
437  * ----------------------------------------------------
438  */
439 RMenum
rmPickDelete(RMpick * list)440 rmPickDelete (RMpick *list)
441 {
442     if (RM_ASSERT(list, "rmPickDelete() error: the input RMpick list is NULL") == RM_WHACKED)
443 	return(RM_WHACKED);
444 
445     free((void *)list);
446     return(RM_CHILL);
447 }
448 
449 
450 /*
451  * ----------------------------------------------------
452  * @Name rmPickedNode
453  @pstart
454  RMnode * rmPickedNode (const RMpick *toQuery)
455  @pend
456 
457  @astart
458  const RMpick *toQuery - a handle to an RMpick object (input).
459  @aend
460 
461  @dstart
462 
463  Returns to the caller the RMnode handle contained within an RMpick
464  object, or NULL upon failure.
465 
466  The RMpick object returned from rmFramePick (or objects returned from
467  rmFramePickList) can be queried to determine the node, primitive,
468  node name or z-coordinate of the picked object.
469 
470  @dend
471  * ----------------------------------------------------
472  */
473 RMnode *
rmPickedNode(const RMpick * p)474 rmPickedNode (const RMpick *p)
475 {
476     if (p == NULL)
477 	return(NULL);
478 
479     return(p->node);
480 }
481 
482 
483 /*
484  * ----------------------------------------------------
485  * @Name rmPickedPrimitive
486  @pstart
487  int rmPickedPrimitive (const RMpick *toQuery)
488  @pend
489 
490  @astart
491  const RMpick *toQuery - a handle to an RMpick object (input).
492  @aend
493 
494  @dstart
495 
496  The RMpick object returned from rmFramePick (or objects returned from
497  rmFramePickList) can be queried to determine the node, primitive,
498  node name or z-coordinate of the picked object.
499 
500  This routine returns the index of the primitive that was
501  picked. Combining the primitive index with the RMnode handle of the
502  picked object allows applications to determine which primitive was
503  picked.
504 
505  Returns a non-negative integer upon success, or -1 upon failure.
506 
507  @dend
508  * ----------------------------------------------------
509  */
510 int
rmPickedPrimitive(const RMpick * p)511 rmPickedPrimitive (const RMpick *p)
512 {
513     if (p == NULL)
514         return(-1);
515     else
516         return(p->prim_index);
517 }
518 
519 
520 /*
521  * ----------------------------------------------------
522  * @Name rmPickedNodeName
523  @pstart
524  char * rmPickedNodeName (const RMpick *toQuery)
525  @pend
526 
527  @astart
528  const RMpick *toQuery - a handle to an RMpick object (input).
529  @aend
530 
531  @dstart
532 
533  Returns to the caller the node name of the RMnode handle contained
534  within an RMpick object, or NULL upon failure.
535 
536  The RMpick object returned from rmFramePick (or objects returned from
537  rmFramePickList) can be queried to determine the node, primitive,
538  node name or z-coordinate of the picked object.
539 
540  This routine is functionally equivalent to first obtaining the RMnode
541  handle using rmPickedNode() followed by rmNodeGetName().
542 
543  @dend
544  * ----------------------------------------------------
545  */
546 char *
rmPickedNodeName(const RMpick * p)547 rmPickedNodeName (const RMpick *p)
548 {
549     if (p == NULL)
550 	return(NULL);
551 
552     return(rmNodeGetName(rmPickedNode(p)));
553 
554 }
555 
556 
557 /*
558  * ----------------------------------------------------
559  * @Name rmPickedPrimitiveZval
560  @pstart
561  float rmPickedPrimitiveZval (const RMpick *toQuery)
562  @pend
563 
564  @astart
565  const RMpick *toQuery - a handle to an RMpick object (input).
566  @aend
567 
568  @dstart
569 
570  Returns to the caller the NDC z-coordinate of the RMprimitive at the
571  (x,y) screen location where picking occured. A value of zero is
572  returned upon error, when no picking occurred (RMpick NULL).
573 
574  The RMpick object returned from rmFramePick (or objects returned from
575  rmFramePickList) can be queried to determine the node, primitive,
576  node name or z-coordinate of the picked object.
577 
578  @dend
579  * ----------------------------------------------------
580  */
581 float
rmPickedPrimitiveZval(const RMpick * p)582 rmPickedPrimitiveZval (const RMpick *p)
583 {
584     if (RM_ASSERT(p, "rmPrimPickedZval() error: the input RMpick object is NULL") == RM_WHACKED)
585 	return(0.0);
586 
587     return(p->zval);
588 }
589 
590 
591 /* PRIVATE
592  *
593  * this routine takes a pile of pick hits and returns the one with
594  * the smallest z-coordinate. nhits and pick_buffer are provided by
595  * the GL_FEEDBACK mechanism, srmpick is the destination RMpick object
596  * that is filled in by this routine.
597  */
598 static int
private_processHits(int nhits,GLuint * pick_buffer,RMpick * srmpick)599 private_processHits (int nhits,
600 		     GLuint *pick_buffer,
601 		     RMpick *srmpick)
602 {
603     int     i, j;
604     int     use_it;
605     float   zval;
606     float   zcur = RM_MAXFLOAT;
607     GLuint *ptr;
608     GLuint  names;
609     GLuint  zscale = (~0u);
610     GLuint  ival = 0;
611 
612     ptr = pick_buffer;
613     for (i = 0; i < nhits; i++)
614     {
615 	use_it = 0;
616         names = *ptr;
617 #if (DEBUG_LEVEL & DEBUG_TRACE)
618 	fprintf(stderr, " number of names for this hit = %d \n", names);
619 #endif
620 	ptr++;
621 
622 	zval = *ptr;
623 	zval = zval / (float)zscale;
624 #if (DEBUG_LEVEL & DEBUG_TRACE)
625 	fprintf(stderr, "\tz1 = %g;", zval);
626 	fprintf(stderr, "\tz1 = %u;", *ptr);
627 #endif
628 	ptr++;
629 
630 	if (zval <= zcur)	/* if they're the same, use the one that was drawn last */
631 	{
632 	    use_it = 1;
633 	    zcur = zval;
634 	}
635 
636 	zval = *ptr;
637 	zval = zval / (float)zscale;
638 #if (DEBUG_LEVEL & DEBUG_TRACE)
639 	fprintf(stderr, " z2 = %g \n", zval);
640 	fprintf(stderr, " z2 = %u \n", *ptr);
641 #endif
642 	ptr++;
643 
644 	/* i'm going to assume that we'll use the first name and
645 	 ignore the rest, and that if we got here, that "names" >= 1*/
646 #if (DEBUG_LEVEL & DEBUG_TRACE)
647 	fprintf(stderr, "\tthe names are: \n");
648 #endif
649 	if (use_it)
650 	    ival = *ptr;
651 	for (j = 0; j < (int)(names); j++)
652 	{
653 #if (DEBUG_LEVEL & DEBUG_TRACE)
654 	    fprintf(stderr,"  %d ", *ptr);
655 #endif
656 	    ptr++;
657 	}
658 #if (DEBUG_LEVEL & DEBUG_TRACE)
659 	fprintf(stderr," \n");
660 #endif
661     }
662 
663     /* from ival, we need to extract the object index and the prim index */
664     {
665 	int opcode = RMPASSTHRU_GET_OPCODE(ival);
666 
667 	if (opcode != RM_PASSTHRU_OPCODE_IDENTIFIER)
668 	    rmError(" expected an identifier opcode in a pick operation. \n");
669     }
670     srmpick->index = RMPASSTHRU_GET_OBJECT_ID(ival);
671     srmpick->prim_index = RMPASSTHRU_GET_PRIM_ID(ival);
672     srmpick->zval = zcur;
673 
674     return(1);
675 }
676 
677 /* PRIVATE */
678 static int
sortPickFunc(const void * p1,const void * p2)679 sortPickFunc( const void *p1, const void *p2)
680 {
681     RMpick *a, *b;
682     float z1, z2;
683 
684     a = (RMpick *)p1;
685     b = (RMpick *)p2;
686 
687     z1 = rmPickedPrimitiveZval(a);
688     z2 = rmPickedPrimitiveZval(b);
689 
690     if (z1 < z2)
691 	return -1;
692     else if (z1 > z2)
693 	return 1;
694     else
695 	return 0;
696 }
697 
698 /* PRIVATE
699  *
700  * analagous to processHits, private_processHitsList will return in RMpick *list
701  * _all_ objects that were picked regardless of z-value. nhits and pick_buffer
702  * are supplied by the OpenGL feedback mechanism, and list[] must be
703  * "nhits" in size.
704  */
705 static int
private_processHitsList(int nhits,GLuint * pick_buffer,RMpick * list)706 private_processHitsList (int nhits,
707 			 GLuint *pick_buffer,
708 			 RMpick *list)
709 {
710     int     i, j;
711     int     ival, use_it;
712     float   zval;
713     GLuint *ptr;
714     GLuint  names;
715     GLuint  zscale = (~0u);
716 
717     ptr = pick_buffer;
718 
719     for (i = 0; i < nhits; i++)
720     {
721 	use_it = 0;
722         names = *ptr;
723 #if (DEBUG_LEVEL & DEBUG_TRACE)
724 	fprintf(stderr, " number of names for this hit = %d \n", names);
725 #endif
726 	ptr++;
727 
728 	zval = *ptr;
729 	zval = zval / (float)zscale;
730 #if (DEBUG_LEVEL & DEBUG_TRACE)
731 	fprintf(stderr, "\tz1 = %g;", zval);
732 	fprintf(stderr, "\tz1 = %u;", *ptr);
733 #endif
734 	ptr++;
735 
736 #if (DEBUG_LEVEL & DEBUG_TRACE)
737 	fprintf(stderr, " z2 = %g \n", zval);
738 	fprintf(stderr, " z2 = %u \n", *ptr);
739 #endif
740 	ptr++;
741 
742 #if (DEBUG_LEVEL & DEBUG_TRACE)
743 	fprintf(stderr, "\tthe names are: \n");
744 #endif
745 	ival = *ptr;
746 	{
747 	    int opcode = RMPASSTHRU_GET_OPCODE(ival);
748 
749 	    if (opcode != RM_PASSTHRU_OPCODE_IDENTIFIER)
750 		rmError(" expected an identifier opcode in a pick operation. \n");
751 	}
752 	list[i].index = RMPASSTHRU_GET_OBJECT_ID(ival);
753 	list[i].prim_index = RMPASSTHRU_GET_PRIM_ID(ival);
754 	list[i].zval = zval;
755 
756 	for (j = 0; j < (int)(names); j++)
757 	{
758 #if (DEBUG_LEVEL & DEBUG_TRACE)
759 	    fprintf(stderr, " %d ", *ptr);
760 #endif
761 	    ptr++;
762 	}
763 #if (DEBUG_LEVEL & DEBUG_TRACE)
764 	fprintf(stderr, " \n");
765 #endif
766     }
767 
768     /* OK. we now have the hits list. sort it in increasing Z */
769     qsort(list, nhits,  sizeof(RMpick), sortPickFunc);
770 
771     return(1);
772 }
773 
774 
775 /* PRIVATE */
776 void
private_rmComputePickMatrix(RMstate * s,RMmatrix * pickReturn)777 private_rmComputePickMatrix (RMstate *s,
778 			     RMmatrix *pickReturn)
779 {
780     float    sx, sy, tx, ty;
781     RMmatrix m;
782 
783     rmMatrixIdentity(&m);
784 
785     sx = s->vp[2] / pick_width;
786     sy = s->vp[3] / pick_height;
787     tx = (s->vp[2] + (2.0F * (s->vp[0] - (float)xpick_location))) / pick_width;
788     ty = (s->vp[3] + (2.0F * (s->vp[1] - (s->h - (float)ypick_location)))) / pick_height;
789 
790     m.m[0][0] = sx;
791     m.m[1][1] = sy;
792     m.m[3][0] = tx;
793     m.m[3][1] = ty;
794 
795     *pickReturn = m;
796 }
797 
798 
799 /* PRIVATE
800  *
801  * private routine to set up the "pick matrix". see the man page
802  * for gluPickMatrix. the basic idea is that the projection matrix
803  * is tweaked so that only those objects that fall within a very
804  * small region are "rendered." rendering, in the case of picking,
805  * means that a GL_FEEDBACK token is generated, rather than pixels
806  * being painted.
807  */
808 void
private_rmSetupPickMatrix(void)809 private_rmSetupPickMatrix (void)
810 {
811     rmError(" private_rmSetupPickMatrix is deprecated!");
812 #if 0
813     int     w, h;
814     GLint   viewport[4];
815     RMpipe *pipe = private_rmPipeGetCurrent();
816 
817     glGetIntegerv(GL_VIEWPORT, viewport);
818     rmPipeGetWindowSize(pipe, &w, &h);
819 
820     glMatrixMode(GL_PROJECTION);
821     glLoadIdentity();
822 
823     gluPickMatrix((GLdouble)xpick_location, (GLdouble)ypick_location, pick_width, pick_height, viewport);
824 
825     /* while we're here before any rendering happens, set up the serialized list of objects */
826     glMatrixMode(GL_MODELVIEW);
827 #endif
828 }
829 
830 
831 /* PRIVATE
832  *
833  * the GL_FEEDBACK mechanism allows for applications to place
834  * application-specific information in the "feedback token."
835  * RM builds a serialized representation of the scene graph, then
836  * as each node & primitive is rendered, a "bread crumb" is
837  * encoded in the feedback token. the routine below will encode
838  * the node & prim info into a token (glLoadName).
839  *
840  * the current_lod parm is deprecated and needs to be removed.
841  */
842 void
private_rmNodePrimPickName(RMnode * r,int primindex)843 private_rmNodePrimPickName (RMnode *r,
844 			    int primindex)
845 {
846     int          indx;
847     unsigned int token = 0;
848 
849     indx = r->compListIndx;
850 
851     RMPASSTHRU_ENCODE_OPCODE(token, RM_PASSTHRU_OPCODE_IDENTIFIER);
852     RMPASSTHRU_ENCODE_OBJECT_ID(token, indx);
853     RMPASSTHRU_ENCODE_PRIM_ID(token, primindex);
854 
855     glLoadName((GLuint)token);
856 }
857 
858 
859 /* PRIVATE
860  *
861  * private_rmNodeOnlyPickName encodes ONLY the node name into the feedback token
862  */
863 void
private_rmNodeOnlyPickName(const RMnode * r)864 private_rmNodeOnlyPickName (const RMnode *r)
865 {
866     int          indx;
867     unsigned int token = 0;
868 
869     indx = r->compListIndx;
870 
871     RMPASSTHRU_ENCODE_OPCODE(token, RM_PASSTHRU_OPCODE_IDENTIFIER);
872     RMPASSTHRU_ENCODE_OBJECT_ID(token, indx);
873 
874     glLoadName((GLuint)token);
875 }
876 
877 
878 /* PRIVATE
879  *
880  * this routine, while public in appearance, is used at the present time
881  * only within RM itself to allocate space for RMpick objects. the routine
882  * rmFramePickList uses this routine. applications will probably never
883  * need to use this routine, as rmFramePickList() calls this routine on
884  * behalf of the application.
885  *
886  * January 2000, there is presently no scenario in which applications using
887  * RM will ever need to call private_rmPickListNew(). Because of this
888  * condition, this function is static to this file.
889  * There is no reason why it couldn't be moved to rmpublic.h if needed.
890  */
891 static RMpick *
private_rmPickListNew(int n)892 private_rmPickListNew (int n)
893 {
894     RMpick *t = (RMpick *)malloc(sizeof(RMpick) * n);
895     memset((void *)t, 0, sizeof(RMpick) * n);
896 
897     return(t);
898 }
899 /* EOF */
900