3 * Selection and feedback modes functions.
7 * Mesa 3-D graphics library
10 * Copyright (C) 1999-2003 Brian Paul All Rights Reserved.
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
19 * The above copyright notice and this permission notice shall be included
20 * in all copies or substantial portions of the Software.
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
47 #define FB_TEXTURE 0X10
52 _mesa_FeedbackBuffer( GLsizei size
, GLenum type
, GLfloat
*buffer
)
54 GET_CURRENT_CONTEXT(ctx
);
55 ASSERT_OUTSIDE_BEGIN_END(ctx
);
57 if (ctx
->RenderMode
==GL_FEEDBACK
) {
58 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glFeedbackBuffer" );
62 _mesa_error( ctx
, GL_INVALID_VALUE
, "glFeedbackBuffer(size<0)" );
66 _mesa_error( ctx
, GL_INVALID_VALUE
, "glFeedbackBuffer(buffer==NULL)" );
67 ctx
->Feedback
.BufferSize
= 0;
73 ctx
->Feedback
._Mask
= 0;
76 ctx
->Feedback
._Mask
= FB_3D
;
79 ctx
->Feedback
._Mask
= (FB_3D
|
80 (ctx
->Visual
.rgbMode
? FB_COLOR
: FB_INDEX
));
82 case GL_3D_COLOR_TEXTURE
:
83 ctx
->Feedback
._Mask
= (FB_3D
|
84 (ctx
->Visual
.rgbMode
? FB_COLOR
: FB_INDEX
) |
87 case GL_4D_COLOR_TEXTURE
:
88 ctx
->Feedback
._Mask
= (FB_3D
| FB_4D
|
89 (ctx
->Visual
.rgbMode
? FB_COLOR
: FB_INDEX
) |
93 _mesa_error( ctx
, GL_INVALID_ENUM
, "glFeedbackBuffer" );
97 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
); /* Always flush */
98 ctx
->Feedback
.Type
= type
;
99 ctx
->Feedback
.BufferSize
= size
;
100 ctx
->Feedback
.Buffer
= buffer
;
101 ctx
->Feedback
.Count
= 0; /* Becaues of this. */
106 _mesa_PassThrough( GLfloat token
)
108 GET_CURRENT_CONTEXT(ctx
);
109 ASSERT_OUTSIDE_BEGIN_END(ctx
);
111 if (ctx
->RenderMode
==GL_FEEDBACK
) {
112 FLUSH_VERTICES(ctx
, 0);
113 FEEDBACK_TOKEN( ctx
, (GLfloat
) (GLint
) GL_PASS_THROUGH_TOKEN
);
114 FEEDBACK_TOKEN( ctx
, token
);
121 * Put a vertex into the feedback buffer.
123 void _mesa_feedback_vertex( GLcontext
*ctx
,
124 const GLfloat win
[4],
125 const GLfloat color
[4],
127 const GLfloat texcoord
[4] )
131 /* snap window x, y to fractional pixel position */
132 const GLint snapMask
= ~((FIXED_ONE
/ (1 << SUB_PIXEL_BITS
)) - 1);
134 x
= FloatToFixed(win
[0]) & snapMask
;
135 y
= FloatToFixed(win
[1]) & snapMask
;
136 FEEDBACK_TOKEN(ctx
, FixedToFloat(x
));
137 FEEDBACK_TOKEN(ctx
, FixedToFloat(y
) );
140 FEEDBACK_TOKEN( ctx
, win
[0] );
141 FEEDBACK_TOKEN( ctx
, win
[1] );
143 if (ctx
->Feedback
._Mask
& FB_3D
) {
144 FEEDBACK_TOKEN( ctx
, win
[2] );
146 if (ctx
->Feedback
._Mask
& FB_4D
) {
147 FEEDBACK_TOKEN( ctx
, win
[3] );
149 if (ctx
->Feedback
._Mask
& FB_INDEX
) {
150 FEEDBACK_TOKEN( ctx
, (GLfloat
) index
);
152 if (ctx
->Feedback
._Mask
& FB_COLOR
) {
153 FEEDBACK_TOKEN( ctx
, color
[0] );
154 FEEDBACK_TOKEN( ctx
, color
[1] );
155 FEEDBACK_TOKEN( ctx
, color
[2] );
156 FEEDBACK_TOKEN( ctx
, color
[3] );
158 if (ctx
->Feedback
._Mask
& FB_TEXTURE
) {
159 FEEDBACK_TOKEN( ctx
, texcoord
[0] );
160 FEEDBACK_TOKEN( ctx
, texcoord
[1] );
161 FEEDBACK_TOKEN( ctx
, texcoord
[2] );
162 FEEDBACK_TOKEN( ctx
, texcoord
[3] );
169 /**********************************************************************/
170 /** \name Selection */
174 * Establish a buffer for selection mode values.
176 * \param size buffer size.
177 * \param buffer buffer.
179 * \sa glSelectBuffer().
181 * \note this function can't be put in a display list.
183 * Verifies we're not in selection mode, flushes the vertices and initialize
184 * the fields in __GLcontextRec::Select with the given buffer.
187 _mesa_SelectBuffer( GLsizei size
, GLuint
*buffer
)
189 GET_CURRENT_CONTEXT(ctx
);
190 ASSERT_OUTSIDE_BEGIN_END(ctx
);
192 if (ctx
->RenderMode
==GL_SELECT
) {
193 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glSelectBuffer" );
194 return; /* KW: added return */
197 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
198 ctx
->Select
.Buffer
= buffer
;
199 ctx
->Select
.BufferSize
= size
;
200 ctx
->Select
.BufferCount
= 0;
201 ctx
->Select
.HitFlag
= GL_FALSE
;
202 ctx
->Select
.HitMinZ
= 1.0;
203 ctx
->Select
.HitMaxZ
= 0.0;
208 * Write a value of a record into the selection buffer.
210 * \param CTX GL context.
213 * Verifies there is free space in the buffer to write the value and
214 * increments the pointer.
216 #define WRITE_RECORD( CTX, V ) \
217 if (CTX->Select.BufferCount < CTX->Select.BufferSize) { \
218 CTX->Select.Buffer[CTX->Select.BufferCount] = (V); \
220 CTX->Select.BufferCount++;
224 * Update the hit flag and the maximum and minimum depth values.
226 * \param ctx GL context.
229 * Sets gl_selection::HitFlag and updates gl_selection::HitMinZ and
230 * gl_selection::HitMaxZ.
232 void _mesa_update_hitflag( GLcontext
*ctx
, GLfloat z
)
234 ctx
->Select
.HitFlag
= GL_TRUE
;
235 if (z
< ctx
->Select
.HitMinZ
) {
236 ctx
->Select
.HitMinZ
= z
;
238 if (z
> ctx
->Select
.HitMaxZ
) {
239 ctx
->Select
.HitMaxZ
= z
;
245 * Write the hit record.
247 * \param ctx GL context.
249 * Write the hit record, i.e., the number of names in the stack, the minimum and
250 * maximum depth values and the number of names in the name stack at the time
251 * of the event. Resets the hit flag.
255 static void write_hit_record( GLcontext
*ctx
)
258 GLuint zmin
, zmax
, zscale
= (~0u);
260 /* HitMinZ and HitMaxZ are in [0,1]. Multiply these values by */
261 /* 2^32-1 and round to nearest unsigned integer. */
263 assert( ctx
!= NULL
); /* this line magically fixes a SunOS 5.x/gcc bug */
264 zmin
= (GLuint
) ((GLfloat
) zscale
* ctx
->Select
.HitMinZ
);
265 zmax
= (GLuint
) ((GLfloat
) zscale
* ctx
->Select
.HitMaxZ
);
267 WRITE_RECORD( ctx
, ctx
->Select
.NameStackDepth
);
268 WRITE_RECORD( ctx
, zmin
);
269 WRITE_RECORD( ctx
, zmax
);
270 for (i
= 0; i
< ctx
->Select
.NameStackDepth
; i
++) {
271 WRITE_RECORD( ctx
, ctx
->Select
.NameStack
[i
] );
275 ctx
->Select
.HitFlag
= GL_FALSE
;
276 ctx
->Select
.HitMinZ
= 1.0;
277 ctx
->Select
.HitMaxZ
= -1.0;
282 * Initialize the name stack.
284 * Verifies we are in select mode and resets the name stack depth and resets
285 * the hit record data in gl_selection. Marks new render mode in
286 * __GLcontextRec::NewState.
289 _mesa_InitNames( void )
291 GET_CURRENT_CONTEXT(ctx
);
292 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
);
294 /* Record the hit before the HitFlag is wiped out again. */
295 if (ctx
->RenderMode
== GL_SELECT
) {
296 if (ctx
->Select
.HitFlag
) {
297 write_hit_record( ctx
);
300 ctx
->Select
.NameStackDepth
= 0;
301 ctx
->Select
.HitFlag
= GL_FALSE
;
302 ctx
->Select
.HitMinZ
= 1.0;
303 ctx
->Select
.HitMaxZ
= 0.0;
304 ctx
->NewState
|= _NEW_RENDERMODE
;
309 * Load the top-most name of the name stack.
313 * Verifies we are in selection mode and that the name stack is not empty.
314 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
315 * and replace the top-most name in the stack.
317 * sa __GLcontextRec::Select.
320 _mesa_LoadName( GLuint name
)
322 GET_CURRENT_CONTEXT(ctx
);
323 ASSERT_OUTSIDE_BEGIN_END(ctx
);
325 if (ctx
->RenderMode
!= GL_SELECT
) {
328 if (ctx
->Select
.NameStackDepth
== 0) {
329 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glLoadName" );
333 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
335 if (ctx
->Select
.HitFlag
) {
336 write_hit_record( ctx
);
338 if (ctx
->Select
.NameStackDepth
< MAX_NAME_STACK_DEPTH
) {
339 ctx
->Select
.NameStack
[ctx
->Select
.NameStackDepth
-1] = name
;
342 ctx
->Select
.NameStack
[MAX_NAME_STACK_DEPTH
-1] = name
;
348 * Push a name into the name stack.
352 * Verifies we are in selection mode and that the name stack is not full.
353 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
354 * and adds the name to the top of the name stack.
356 * sa __GLcontextRec::Select.
359 _mesa_PushName( GLuint name
)
361 GET_CURRENT_CONTEXT(ctx
);
362 ASSERT_OUTSIDE_BEGIN_END(ctx
);
364 if (ctx
->RenderMode
!= GL_SELECT
) {
368 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
369 if (ctx
->Select
.HitFlag
) {
370 write_hit_record( ctx
);
372 if (ctx
->Select
.NameStackDepth
>= MAX_NAME_STACK_DEPTH
) {
373 _mesa_error( ctx
, GL_STACK_OVERFLOW
, "glPushName" );
376 ctx
->Select
.NameStack
[ctx
->Select
.NameStackDepth
++] = name
;
381 * Pop a name into the name stack.
383 * Verifies we are in selection mode and that the name stack is not empty.
384 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
385 * and removes top-most name in the name stack.
387 * sa __GLcontextRec::Select.
390 _mesa_PopName( void )
392 GET_CURRENT_CONTEXT(ctx
);
393 ASSERT_OUTSIDE_BEGIN_END(ctx
);
395 if (ctx
->RenderMode
!= GL_SELECT
) {
399 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
400 if (ctx
->Select
.HitFlag
) {
401 write_hit_record( ctx
);
403 if (ctx
->Select
.NameStackDepth
== 0) {
404 _mesa_error( ctx
, GL_STACK_UNDERFLOW
, "glPopName" );
407 ctx
->Select
.NameStackDepth
--;
413 /**********************************************************************/
414 /** \name Render Mode */
418 * Set rasterization mode.
420 * \param mode rasterization mode.
422 * \note this function can't be put in a display list.
424 * \sa glRenderMode().
426 * Flushes the vertices and do the necessary cleanup according to the previous
427 * rasterization mode, such as writing the hit record or resent the select
428 * buffer index when exiting the select mode. Updates
429 * __GLcontextRec::RenderMode and notifies the driver via the
430 * dd_function_table::RenderMode callback.
433 _mesa_RenderMode( GLenum mode
)
435 GET_CURRENT_CONTEXT(ctx
);
437 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx
, 0);
439 if (MESA_VERBOSE
& VERBOSE_API
)
440 _mesa_debug(ctx
, "glRenderMode %s\n", _mesa_lookup_enum_by_nr(mode
));
442 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
444 switch (ctx
->RenderMode
) {
449 if (ctx
->Select
.HitFlag
) {
450 write_hit_record( ctx
);
452 if (ctx
->Select
.BufferCount
> ctx
->Select
.BufferSize
) {
455 _mesa_warning(ctx
, "Feedback buffer overflow");
460 result
= ctx
->Select
.Hits
;
462 ctx
->Select
.BufferCount
= 0;
463 ctx
->Select
.Hits
= 0;
464 ctx
->Select
.NameStackDepth
= 0;
468 if (ctx
->Feedback
.Count
> ctx
->Feedback
.BufferSize
) {
473 result
= ctx
->Feedback
.Count
;
475 ctx
->Feedback
.Count
= 0;
479 _mesa_error( ctx
, GL_INVALID_ENUM
, "glRenderMode" );
487 if (ctx
->Select
.BufferSize
==0) {
488 /* haven't called glSelectBuffer yet */
489 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glRenderMode" );
494 if (ctx
->Feedback
.BufferSize
==0) {
495 /* haven't called glFeedbackBuffer yet */
496 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glRenderMode" );
501 _mesa_error( ctx
, GL_INVALID_ENUM
, "glRenderMode" );
505 ctx
->RenderMode
= mode
;
506 if (ctx
->Driver
.RenderMode
)
507 ctx
->Driver
.RenderMode( ctx
, mode
);
515 /**********************************************************************/
516 /** \name Initialization */
520 * Initialize context feedback data.
522 void _mesa_init_feedback( GLcontext
* ctx
)
525 ctx
->Feedback
.Type
= GL_2D
; /* TODO: verify */
526 ctx
->Feedback
.Buffer
= NULL
;
527 ctx
->Feedback
.BufferSize
= 0;
528 ctx
->Feedback
.Count
= 0;
530 /* Selection/picking */
531 ctx
->Select
.Buffer
= NULL
;
532 ctx
->Select
.BufferSize
= 0;
533 ctx
->Select
.BufferCount
= 0;
534 ctx
->Select
.Hits
= 0;
535 ctx
->Select
.NameStackDepth
= 0;
538 ctx
->RenderMode
= GL_RENDER
;