69b235a3e574032bd76eafb8b95591e09d6feac3
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] )
129 FEEDBACK_TOKEN( ctx
, win
[0] );
130 FEEDBACK_TOKEN( ctx
, win
[1] );
131 if (ctx
->Feedback
._Mask
& FB_3D
) {
132 FEEDBACK_TOKEN( ctx
, win
[2] );
134 if (ctx
->Feedback
._Mask
& FB_4D
) {
135 FEEDBACK_TOKEN( ctx
, win
[3] );
137 if (ctx
->Feedback
._Mask
& FB_INDEX
) {
138 FEEDBACK_TOKEN( ctx
, (GLfloat
) index
);
140 if (ctx
->Feedback
._Mask
& FB_COLOR
) {
141 FEEDBACK_TOKEN( ctx
, color
[0] );
142 FEEDBACK_TOKEN( ctx
, color
[1] );
143 FEEDBACK_TOKEN( ctx
, color
[2] );
144 FEEDBACK_TOKEN( ctx
, color
[3] );
146 if (ctx
->Feedback
._Mask
& FB_TEXTURE
) {
147 FEEDBACK_TOKEN( ctx
, texcoord
[0] );
148 FEEDBACK_TOKEN( ctx
, texcoord
[1] );
149 FEEDBACK_TOKEN( ctx
, texcoord
[2] );
150 FEEDBACK_TOKEN( ctx
, texcoord
[3] );
157 /**********************************************************************/
158 /** \name Selection */
162 * Establish a buffer for selection mode values.
164 * \param size buffer size.
165 * \param buffer buffer.
167 * \sa glSelectBuffer().
169 * \note this function can't be put in a display list.
171 * Verifies we're not in selection mode, flushes the vertices and initialize
172 * the fields in __GLcontextRec::Select with the given buffer.
175 _mesa_SelectBuffer( GLsizei size
, GLuint
*buffer
)
177 GET_CURRENT_CONTEXT(ctx
);
178 ASSERT_OUTSIDE_BEGIN_END(ctx
);
180 if (ctx
->RenderMode
==GL_SELECT
) {
181 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glSelectBuffer" );
182 return; /* KW: added return */
185 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
186 ctx
->Select
.Buffer
= buffer
;
187 ctx
->Select
.BufferSize
= size
;
188 ctx
->Select
.BufferCount
= 0;
189 ctx
->Select
.HitFlag
= GL_FALSE
;
190 ctx
->Select
.HitMinZ
= 1.0;
191 ctx
->Select
.HitMaxZ
= 0.0;
196 * Write a value of a record into the selection buffer.
198 * \param CTX GL context.
201 * Verifies there is free space in the buffer to write the value and
202 * increments the pointer.
204 #define WRITE_RECORD( CTX, V ) \
205 if (CTX->Select.BufferCount < CTX->Select.BufferSize) { \
206 CTX->Select.Buffer[CTX->Select.BufferCount] = (V); \
208 CTX->Select.BufferCount++;
212 * Update the hit flag and the maximum and minimum depth values.
214 * \param ctx GL context.
217 * Sets gl_selection::HitFlag and updates gl_selection::HitMinZ and
218 * gl_selection::HitMaxZ.
220 void _mesa_update_hitflag( GLcontext
*ctx
, GLfloat z
)
222 ctx
->Select
.HitFlag
= GL_TRUE
;
223 if (z
< ctx
->Select
.HitMinZ
) {
224 ctx
->Select
.HitMinZ
= z
;
226 if (z
> ctx
->Select
.HitMaxZ
) {
227 ctx
->Select
.HitMaxZ
= z
;
233 * Write the hit record.
235 * \param ctx GL context.
237 * Write the hit record, i.e., the number of names in the stack, the minimum and
238 * maximum depth values and the number of names in the name stack at the time
239 * of the event. Resets the hit flag.
243 static void write_hit_record( GLcontext
*ctx
)
246 GLuint zmin
, zmax
, zscale
= (~0u);
248 /* HitMinZ and HitMaxZ are in [0,1]. Multiply these values by */
249 /* 2^32-1 and round to nearest unsigned integer. */
251 assert( ctx
!= NULL
); /* this line magically fixes a SunOS 5.x/gcc bug */
252 zmin
= (GLuint
) ((GLfloat
) zscale
* ctx
->Select
.HitMinZ
);
253 zmax
= (GLuint
) ((GLfloat
) zscale
* ctx
->Select
.HitMaxZ
);
255 WRITE_RECORD( ctx
, ctx
->Select
.NameStackDepth
);
256 WRITE_RECORD( ctx
, zmin
);
257 WRITE_RECORD( ctx
, zmax
);
258 for (i
= 0; i
< ctx
->Select
.NameStackDepth
; i
++) {
259 WRITE_RECORD( ctx
, ctx
->Select
.NameStack
[i
] );
263 ctx
->Select
.HitFlag
= GL_FALSE
;
264 ctx
->Select
.HitMinZ
= 1.0;
265 ctx
->Select
.HitMaxZ
= -1.0;
270 * Initialize the name stack.
272 * Verifies we are in select mode and resets the name stack depth and resets
273 * the hit record data in gl_selection. Marks new render mode in
274 * __GLcontextRec::NewState.
277 _mesa_InitNames( void )
279 GET_CURRENT_CONTEXT(ctx
);
280 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
);
282 /* Record the hit before the HitFlag is wiped out again. */
283 if (ctx
->RenderMode
== GL_SELECT
) {
284 if (ctx
->Select
.HitFlag
) {
285 write_hit_record( ctx
);
288 ctx
->Select
.NameStackDepth
= 0;
289 ctx
->Select
.HitFlag
= GL_FALSE
;
290 ctx
->Select
.HitMinZ
= 1.0;
291 ctx
->Select
.HitMaxZ
= 0.0;
292 ctx
->NewState
|= _NEW_RENDERMODE
;
297 * Load the top-most name of the name stack.
301 * Verifies we are in selection mode and that the name stack is not empty.
302 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
303 * and replace the top-most name in the stack.
305 * sa __GLcontextRec::Select.
308 _mesa_LoadName( GLuint name
)
310 GET_CURRENT_CONTEXT(ctx
);
311 ASSERT_OUTSIDE_BEGIN_END(ctx
);
313 if (ctx
->RenderMode
!= GL_SELECT
) {
316 if (ctx
->Select
.NameStackDepth
== 0) {
317 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glLoadName" );
321 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
323 if (ctx
->Select
.HitFlag
) {
324 write_hit_record( ctx
);
326 if (ctx
->Select
.NameStackDepth
< MAX_NAME_STACK_DEPTH
) {
327 ctx
->Select
.NameStack
[ctx
->Select
.NameStackDepth
-1] = name
;
330 ctx
->Select
.NameStack
[MAX_NAME_STACK_DEPTH
-1] = name
;
336 * Push a name into the name stack.
340 * Verifies we are in selection mode and that the name stack is not full.
341 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
342 * and adds the name to the top of the name stack.
344 * sa __GLcontextRec::Select.
347 _mesa_PushName( GLuint name
)
349 GET_CURRENT_CONTEXT(ctx
);
350 ASSERT_OUTSIDE_BEGIN_END(ctx
);
352 if (ctx
->RenderMode
!= GL_SELECT
) {
356 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
357 if (ctx
->Select
.HitFlag
) {
358 write_hit_record( ctx
);
360 if (ctx
->Select
.NameStackDepth
>= MAX_NAME_STACK_DEPTH
) {
361 _mesa_error( ctx
, GL_STACK_OVERFLOW
, "glPushName" );
364 ctx
->Select
.NameStack
[ctx
->Select
.NameStackDepth
++] = name
;
369 * Pop a name into the name stack.
371 * Verifies we are in selection mode and that the name stack is not empty.
372 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
373 * and removes top-most name in the name stack.
375 * sa __GLcontextRec::Select.
378 _mesa_PopName( void )
380 GET_CURRENT_CONTEXT(ctx
);
381 ASSERT_OUTSIDE_BEGIN_END(ctx
);
383 if (ctx
->RenderMode
!= GL_SELECT
) {
387 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
388 if (ctx
->Select
.HitFlag
) {
389 write_hit_record( ctx
);
391 if (ctx
->Select
.NameStackDepth
== 0) {
392 _mesa_error( ctx
, GL_STACK_UNDERFLOW
, "glPopName" );
395 ctx
->Select
.NameStackDepth
--;
401 /**********************************************************************/
402 /** \name Render Mode */
406 * Set rasterization mode.
408 * \param mode rasterization mode.
410 * \note this function can't be put in a display list.
412 * \sa glRenderMode().
414 * Flushes the vertices and do the necessary cleanup according to the previous
415 * rasterization mode, such as writing the hit record or resent the select
416 * buffer index when exiting the select mode. Updates
417 * __GLcontextRec::RenderMode and notifies the driver via the
418 * dd_function_table::RenderMode callback.
421 _mesa_RenderMode( GLenum mode
)
423 GET_CURRENT_CONTEXT(ctx
);
425 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx
, 0);
427 if (MESA_VERBOSE
& VERBOSE_API
)
428 _mesa_debug(ctx
, "glRenderMode %s\n", _mesa_lookup_enum_by_nr(mode
));
430 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
432 switch (ctx
->RenderMode
) {
437 if (ctx
->Select
.HitFlag
) {
438 write_hit_record( ctx
);
440 if (ctx
->Select
.BufferCount
> ctx
->Select
.BufferSize
) {
443 _mesa_warning(ctx
, "Feedback buffer overflow");
448 result
= ctx
->Select
.Hits
;
450 ctx
->Select
.BufferCount
= 0;
451 ctx
->Select
.Hits
= 0;
452 ctx
->Select
.NameStackDepth
= 0;
456 if (ctx
->Feedback
.Count
> ctx
->Feedback
.BufferSize
) {
461 result
= ctx
->Feedback
.Count
;
463 ctx
->Feedback
.Count
= 0;
467 _mesa_error( ctx
, GL_INVALID_ENUM
, "glRenderMode" );
475 if (ctx
->Select
.BufferSize
==0) {
476 /* haven't called glSelectBuffer yet */
477 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glRenderMode" );
482 if (ctx
->Feedback
.BufferSize
==0) {
483 /* haven't called glFeedbackBuffer yet */
484 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glRenderMode" );
489 _mesa_error( ctx
, GL_INVALID_ENUM
, "glRenderMode" );
493 ctx
->RenderMode
= mode
;
494 if (ctx
->Driver
.RenderMode
)
495 ctx
->Driver
.RenderMode( ctx
, mode
);
503 /**********************************************************************/
504 /** \name Initialization */
508 * Initialize context feedback data.
510 void _mesa_init_feedback( GLcontext
* ctx
)
513 ctx
->Feedback
.Type
= GL_2D
; /* TODO: verify */
514 ctx
->Feedback
.Buffer
= NULL
;
515 ctx
->Feedback
.BufferSize
= 0;
516 ctx
->Feedback
.Count
= 0;
518 /* Selection/picking */
519 ctx
->Select
.Buffer
= NULL
;
520 ctx
->Select
.BufferSize
= 0;
521 ctx
->Select
.BufferCount
= 0;
522 ctx
->Select
.Hits
= 0;
523 ctx
->Select
.NameStackDepth
= 0;
526 ctx
->RenderMode
= GL_RENDER
;