2 * Mesa 3-D graphics library
5 * Copyright (C) 1999-2008 Brian Paul All Rights Reserved.
6 * Copyright (C) 2009 VMware, Inc. All Rights Reserved.
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 * Selection and feedback modes functions.
38 #include "mfeatures.h"
40 #include "main/dispatch.h"
46 #define FB_TEXTURE 0X08
51 _mesa_FeedbackBuffer( GLsizei size
, GLenum type
, GLfloat
*buffer
)
53 GET_CURRENT_CONTEXT(ctx
);
54 ASSERT_OUTSIDE_BEGIN_END(ctx
);
56 if (ctx
->RenderMode
==GL_FEEDBACK
) {
57 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glFeedbackBuffer" );
61 _mesa_error( ctx
, GL_INVALID_VALUE
, "glFeedbackBuffer(size<0)" );
64 if (!buffer
&& size
> 0) {
65 _mesa_error( ctx
, GL_INVALID_VALUE
, "glFeedbackBuffer(buffer==NULL)" );
66 ctx
->Feedback
.BufferSize
= 0;
72 ctx
->Feedback
._Mask
= 0;
75 ctx
->Feedback
._Mask
= FB_3D
;
78 ctx
->Feedback
._Mask
= (FB_3D
| FB_COLOR
);
80 case GL_3D_COLOR_TEXTURE
:
81 ctx
->Feedback
._Mask
= (FB_3D
| FB_COLOR
| FB_TEXTURE
);
83 case GL_4D_COLOR_TEXTURE
:
84 ctx
->Feedback
._Mask
= (FB_3D
| FB_4D
| FB_COLOR
| FB_TEXTURE
);
87 _mesa_error( ctx
, GL_INVALID_ENUM
, "glFeedbackBuffer" );
91 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
); /* Always flush */
92 ctx
->Feedback
.Type
= type
;
93 ctx
->Feedback
.BufferSize
= size
;
94 ctx
->Feedback
.Buffer
= buffer
;
95 ctx
->Feedback
.Count
= 0; /* Becaues of this. */
100 _mesa_PassThrough( GLfloat token
)
102 GET_CURRENT_CONTEXT(ctx
);
103 ASSERT_OUTSIDE_BEGIN_END(ctx
);
105 if (ctx
->RenderMode
==GL_FEEDBACK
) {
106 FLUSH_VERTICES(ctx
, 0);
107 _mesa_feedback_token( ctx
, (GLfloat
) (GLint
) GL_PASS_THROUGH_TOKEN
);
108 _mesa_feedback_token( ctx
, token
);
114 * Put a vertex into the feedback buffer.
117 _mesa_feedback_vertex(struct gl_context
*ctx
,
118 const GLfloat win
[4],
119 const GLfloat color
[4],
120 const GLfloat texcoord
[4])
122 _mesa_feedback_token( ctx
, win
[0] );
123 _mesa_feedback_token( ctx
, win
[1] );
124 if (ctx
->Feedback
._Mask
& FB_3D
) {
125 _mesa_feedback_token( ctx
, win
[2] );
127 if (ctx
->Feedback
._Mask
& FB_4D
) {
128 _mesa_feedback_token( ctx
, win
[3] );
130 if (ctx
->Feedback
._Mask
& FB_COLOR
) {
131 _mesa_feedback_token( ctx
, color
[0] );
132 _mesa_feedback_token( ctx
, color
[1] );
133 _mesa_feedback_token( ctx
, color
[2] );
134 _mesa_feedback_token( ctx
, color
[3] );
136 if (ctx
->Feedback
._Mask
& FB_TEXTURE
) {
137 _mesa_feedback_token( ctx
, texcoord
[0] );
138 _mesa_feedback_token( ctx
, texcoord
[1] );
139 _mesa_feedback_token( ctx
, texcoord
[2] );
140 _mesa_feedback_token( ctx
, texcoord
[3] );
145 /**********************************************************************/
146 /** \name Selection */
150 * Establish a buffer for selection mode values.
152 * \param size buffer size.
153 * \param buffer buffer.
155 * \sa glSelectBuffer().
157 * \note this function can't be put in a display list.
159 * Verifies we're not in selection mode, flushes the vertices and initialize
160 * the fields in __struct gl_contextRec::Select with the given buffer.
163 _mesa_SelectBuffer( GLsizei size
, GLuint
*buffer
)
165 GET_CURRENT_CONTEXT(ctx
);
166 ASSERT_OUTSIDE_BEGIN_END(ctx
);
169 _mesa_error(ctx
, GL_INVALID_VALUE
, "glSelectBuffer(size)");
173 if (ctx
->RenderMode
==GL_SELECT
) {
174 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glSelectBuffer" );
175 return; /* KW: added return */
178 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
179 ctx
->Select
.Buffer
= buffer
;
180 ctx
->Select
.BufferSize
= size
;
181 ctx
->Select
.BufferCount
= 0;
182 ctx
->Select
.HitFlag
= GL_FALSE
;
183 ctx
->Select
.HitMinZ
= 1.0;
184 ctx
->Select
.HitMaxZ
= 0.0;
189 * Write a value of a record into the selection buffer.
191 * \param ctx GL context.
192 * \param value value.
194 * Verifies there is free space in the buffer to write the value and
195 * increments the pointer.
198 write_record(struct gl_context
*ctx
, GLuint value
)
200 if (ctx
->Select
.BufferCount
< ctx
->Select
.BufferSize
) {
201 ctx
->Select
.Buffer
[ctx
->Select
.BufferCount
] = value
;
203 ctx
->Select
.BufferCount
++;
208 * Update the hit flag and the maximum and minimum depth values.
210 * \param ctx GL context.
213 * Sets gl_selection::HitFlag and updates gl_selection::HitMinZ and
214 * gl_selection::HitMaxZ.
217 _mesa_update_hitflag(struct gl_context
*ctx
, GLfloat z
)
219 ctx
->Select
.HitFlag
= GL_TRUE
;
220 if (z
< ctx
->Select
.HitMinZ
) {
221 ctx
->Select
.HitMinZ
= z
;
223 if (z
> ctx
->Select
.HitMaxZ
) {
224 ctx
->Select
.HitMaxZ
= z
;
230 * Write the hit record.
232 * \param ctx GL context.
234 * Write the hit record, i.e., the number of names in the stack, the minimum and
235 * maximum depth values and the number of names in the name stack at the time
236 * of the event. Resets the hit flag.
241 write_hit_record(struct gl_context
*ctx
)
244 GLuint zmin
, zmax
, zscale
= (~0u);
246 /* HitMinZ and HitMaxZ are in [0,1]. Multiply these values by */
247 /* 2^32-1 and round to nearest unsigned integer. */
249 assert( ctx
!= NULL
); /* this line magically fixes a SunOS 5.x/gcc bug */
250 zmin
= (GLuint
) ((GLfloat
) zscale
* ctx
->Select
.HitMinZ
);
251 zmax
= (GLuint
) ((GLfloat
) zscale
* ctx
->Select
.HitMaxZ
);
253 write_record( ctx
, ctx
->Select
.NameStackDepth
);
254 write_record( ctx
, zmin
);
255 write_record( ctx
, zmax
);
256 for (i
= 0; i
< ctx
->Select
.NameStackDepth
; i
++) {
257 write_record( ctx
, ctx
->Select
.NameStack
[i
] );
261 ctx
->Select
.HitFlag
= GL_FALSE
;
262 ctx
->Select
.HitMinZ
= 1.0;
263 ctx
->Select
.HitMaxZ
= -1.0;
268 * Initialize the name stack.
270 * Verifies we are in select mode and resets the name stack depth and resets
271 * the hit record data in gl_selection. Marks new render mode in
272 * __struct gl_contextRec::NewState.
275 _mesa_InitNames( void )
277 GET_CURRENT_CONTEXT(ctx
);
278 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
);
280 /* Record the hit before the HitFlag is wiped out again. */
281 if (ctx
->RenderMode
== GL_SELECT
) {
282 if (ctx
->Select
.HitFlag
) {
283 write_hit_record( ctx
);
286 ctx
->Select
.NameStackDepth
= 0;
287 ctx
->Select
.HitFlag
= GL_FALSE
;
288 ctx
->Select
.HitMinZ
= 1.0;
289 ctx
->Select
.HitMaxZ
= 0.0;
290 ctx
->NewState
|= _NEW_RENDERMODE
;
295 * Load the top-most name of the name stack.
299 * Verifies we are in selection mode and that the name stack is not empty.
300 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
301 * and replace the top-most name in the stack.
303 * sa __struct gl_contextRec::Select.
306 _mesa_LoadName( GLuint name
)
308 GET_CURRENT_CONTEXT(ctx
);
309 ASSERT_OUTSIDE_BEGIN_END(ctx
);
311 if (ctx
->RenderMode
!= GL_SELECT
) {
314 if (ctx
->Select
.NameStackDepth
== 0) {
315 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glLoadName" );
319 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
321 if (ctx
->Select
.HitFlag
) {
322 write_hit_record( ctx
);
324 if (ctx
->Select
.NameStackDepth
< MAX_NAME_STACK_DEPTH
) {
325 ctx
->Select
.NameStack
[ctx
->Select
.NameStackDepth
-1] = name
;
328 ctx
->Select
.NameStack
[MAX_NAME_STACK_DEPTH
-1] = name
;
334 * Push a name into the name stack.
338 * Verifies we are in selection mode and that the name stack is not full.
339 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
340 * and adds the name to the top of the name stack.
342 * sa __struct gl_contextRec::Select.
345 _mesa_PushName( GLuint name
)
347 GET_CURRENT_CONTEXT(ctx
);
348 ASSERT_OUTSIDE_BEGIN_END(ctx
);
350 if (ctx
->RenderMode
!= GL_SELECT
) {
354 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
355 if (ctx
->Select
.HitFlag
) {
356 write_hit_record( ctx
);
358 if (ctx
->Select
.NameStackDepth
>= MAX_NAME_STACK_DEPTH
) {
359 _mesa_error( ctx
, GL_STACK_OVERFLOW
, "glPushName" );
362 ctx
->Select
.NameStack
[ctx
->Select
.NameStackDepth
++] = name
;
367 * Pop a name into the name stack.
369 * Verifies we are in selection mode and that the name stack is not empty.
370 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
371 * and removes top-most name in the name stack.
373 * sa __struct gl_contextRec::Select.
376 _mesa_PopName( void )
378 GET_CURRENT_CONTEXT(ctx
);
379 ASSERT_OUTSIDE_BEGIN_END(ctx
);
381 if (ctx
->RenderMode
!= GL_SELECT
) {
385 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
386 if (ctx
->Select
.HitFlag
) {
387 write_hit_record( ctx
);
389 if (ctx
->Select
.NameStackDepth
== 0) {
390 _mesa_error( ctx
, GL_STACK_UNDERFLOW
, "glPopName" );
393 ctx
->Select
.NameStackDepth
--;
399 /**********************************************************************/
400 /** \name Render Mode */
404 * Set rasterization mode.
406 * \param mode rasterization mode.
408 * \note this function can't be put in a display list.
410 * \sa glRenderMode().
412 * Flushes the vertices and do the necessary cleanup according to the previous
413 * rasterization mode, such as writing the hit record or resent the select
414 * buffer index when exiting the select mode. Updates
415 * __struct gl_contextRec::RenderMode and notifies the driver via the
416 * dd_function_table::RenderMode callback.
419 _mesa_RenderMode( GLenum mode
)
421 GET_CURRENT_CONTEXT(ctx
);
423 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx
, 0);
425 if (MESA_VERBOSE
& VERBOSE_API
)
426 _mesa_debug(ctx
, "glRenderMode %s\n", _mesa_lookup_enum_by_nr(mode
));
428 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
430 switch (ctx
->RenderMode
) {
435 if (ctx
->Select
.HitFlag
) {
436 write_hit_record( ctx
);
438 if (ctx
->Select
.BufferCount
> ctx
->Select
.BufferSize
) {
441 _mesa_warning(ctx
, "Feedback buffer overflow");
446 result
= ctx
->Select
.Hits
;
448 ctx
->Select
.BufferCount
= 0;
449 ctx
->Select
.Hits
= 0;
450 ctx
->Select
.NameStackDepth
= 0;
453 if (ctx
->Feedback
.Count
> ctx
->Feedback
.BufferSize
) {
458 result
= ctx
->Feedback
.Count
;
460 ctx
->Feedback
.Count
= 0;
463 _mesa_error( ctx
, GL_INVALID_ENUM
, "glRenderMode" );
471 if (ctx
->Select
.BufferSize
==0) {
472 /* haven't called glSelectBuffer yet */
473 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glRenderMode" );
477 if (ctx
->Feedback
.BufferSize
==0) {
478 /* haven't called glFeedbackBuffer yet */
479 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glRenderMode" );
483 _mesa_error( ctx
, GL_INVALID_ENUM
, "glRenderMode" );
487 ctx
->RenderMode
= mode
;
488 if (ctx
->Driver
.RenderMode
)
489 ctx
->Driver
.RenderMode( ctx
, mode
);
497 /**********************************************************************/
498 /** \name Initialization */
502 * Initialize context feedback data.
504 void _mesa_init_feedback( struct gl_context
* ctx
)
507 ctx
->Feedback
.Type
= GL_2D
; /* TODO: verify */
508 ctx
->Feedback
.Buffer
= NULL
;
509 ctx
->Feedback
.BufferSize
= 0;
510 ctx
->Feedback
.Count
= 0;
512 /* Selection/picking */
513 ctx
->Select
.Buffer
= NULL
;
514 ctx
->Select
.BufferSize
= 0;
515 ctx
->Select
.BufferCount
= 0;
516 ctx
->Select
.Hits
= 0;
517 ctx
->Select
.NameStackDepth
= 0;
520 ctx
->RenderMode
= GL_RENDER
;