2 * Mesa 3-D graphics library
4 * Copyright (C) 1999-2008 Brian Paul All Rights Reserved.
5 * Copyright (C) 2009 VMware, Inc. All Rights Reserved.
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
28 * Selection and feedback modes functions.
38 #include "main/dispatch.h"
44 #define FB_TEXTURE 0X08
49 _mesa_FeedbackBuffer( GLsizei size
, GLenum type
, GLfloat
*buffer
)
51 GET_CURRENT_CONTEXT(ctx
);
53 if (ctx
->RenderMode
==GL_FEEDBACK
) {
54 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glFeedbackBuffer" );
58 _mesa_error( ctx
, GL_INVALID_VALUE
, "glFeedbackBuffer(size<0)" );
61 if (!buffer
&& size
> 0) {
62 _mesa_error( ctx
, GL_INVALID_VALUE
, "glFeedbackBuffer(buffer==NULL)" );
63 ctx
->Feedback
.BufferSize
= 0;
69 ctx
->Feedback
._Mask
= 0;
72 ctx
->Feedback
._Mask
= FB_3D
;
75 ctx
->Feedback
._Mask
= (FB_3D
| FB_COLOR
);
77 case GL_3D_COLOR_TEXTURE
:
78 ctx
->Feedback
._Mask
= (FB_3D
| FB_COLOR
| FB_TEXTURE
);
80 case GL_4D_COLOR_TEXTURE
:
81 ctx
->Feedback
._Mask
= (FB_3D
| FB_4D
| FB_COLOR
| FB_TEXTURE
);
84 _mesa_error( ctx
, GL_INVALID_ENUM
, "glFeedbackBuffer" );
88 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
); /* Always flush */
89 ctx
->Feedback
.Type
= type
;
90 ctx
->Feedback
.BufferSize
= size
;
91 ctx
->Feedback
.Buffer
= buffer
;
92 ctx
->Feedback
.Count
= 0; /* Becaues of this. */
97 _mesa_PassThrough( GLfloat token
)
99 GET_CURRENT_CONTEXT(ctx
);
101 if (ctx
->RenderMode
==GL_FEEDBACK
) {
102 FLUSH_VERTICES(ctx
, 0);
103 _mesa_feedback_token( ctx
, (GLfloat
) (GLint
) GL_PASS_THROUGH_TOKEN
);
104 _mesa_feedback_token( ctx
, token
);
110 * Put a vertex into the feedback buffer.
113 _mesa_feedback_vertex(struct gl_context
*ctx
,
114 const GLfloat win
[4],
115 const GLfloat color
[4],
116 const GLfloat texcoord
[4])
118 _mesa_feedback_token( ctx
, win
[0] );
119 _mesa_feedback_token( ctx
, win
[1] );
120 if (ctx
->Feedback
._Mask
& FB_3D
) {
121 _mesa_feedback_token( ctx
, win
[2] );
123 if (ctx
->Feedback
._Mask
& FB_4D
) {
124 _mesa_feedback_token( ctx
, win
[3] );
126 if (ctx
->Feedback
._Mask
& FB_COLOR
) {
127 _mesa_feedback_token( ctx
, color
[0] );
128 _mesa_feedback_token( ctx
, color
[1] );
129 _mesa_feedback_token( ctx
, color
[2] );
130 _mesa_feedback_token( ctx
, color
[3] );
132 if (ctx
->Feedback
._Mask
& FB_TEXTURE
) {
133 _mesa_feedback_token( ctx
, texcoord
[0] );
134 _mesa_feedback_token( ctx
, texcoord
[1] );
135 _mesa_feedback_token( ctx
, texcoord
[2] );
136 _mesa_feedback_token( ctx
, texcoord
[3] );
141 /**********************************************************************/
142 /** \name Selection */
146 * Establish a buffer for selection mode values.
148 * \param size buffer size.
149 * \param buffer buffer.
151 * \sa glSelectBuffer().
153 * \note this function can't be put in a display list.
155 * Verifies we're not in selection mode, flushes the vertices and initialize
156 * the fields in __struct gl_contextRec::Select with the given buffer.
159 _mesa_SelectBuffer( GLsizei size
, GLuint
*buffer
)
161 GET_CURRENT_CONTEXT(ctx
);
164 _mesa_error(ctx
, GL_INVALID_VALUE
, "glSelectBuffer(size)");
168 if (ctx
->RenderMode
==GL_SELECT
) {
169 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glSelectBuffer" );
170 return; /* KW: added return */
173 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
174 ctx
->Select
.Buffer
= buffer
;
175 ctx
->Select
.BufferSize
= size
;
176 ctx
->Select
.BufferCount
= 0;
177 ctx
->Select
.HitFlag
= GL_FALSE
;
178 ctx
->Select
.HitMinZ
= 1.0;
179 ctx
->Select
.HitMaxZ
= 0.0;
184 * Write a value of a record into the selection buffer.
186 * \param ctx GL context.
187 * \param value value.
189 * Verifies there is free space in the buffer to write the value and
190 * increments the pointer.
193 write_record(struct gl_context
*ctx
, GLuint value
)
195 if (ctx
->Select
.BufferCount
< ctx
->Select
.BufferSize
) {
196 ctx
->Select
.Buffer
[ctx
->Select
.BufferCount
] = value
;
198 ctx
->Select
.BufferCount
++;
203 * Update the hit flag and the maximum and minimum depth values.
205 * \param ctx GL context.
208 * Sets gl_selection::HitFlag and updates gl_selection::HitMinZ and
209 * gl_selection::HitMaxZ.
212 _mesa_update_hitflag(struct gl_context
*ctx
, GLfloat z
)
214 ctx
->Select
.HitFlag
= GL_TRUE
;
215 if (z
< ctx
->Select
.HitMinZ
) {
216 ctx
->Select
.HitMinZ
= z
;
218 if (z
> ctx
->Select
.HitMaxZ
) {
219 ctx
->Select
.HitMaxZ
= z
;
225 * Write the hit record.
227 * \param ctx GL context.
229 * Write the hit record, i.e., the number of names in the stack, the minimum and
230 * maximum depth values and the number of names in the name stack at the time
231 * of the event. Resets the hit flag.
236 write_hit_record(struct gl_context
*ctx
)
239 GLuint zmin
, zmax
, zscale
= (~0u);
241 /* HitMinZ and HitMaxZ are in [0,1]. Multiply these values by */
242 /* 2^32-1 and round to nearest unsigned integer. */
244 assert( ctx
!= NULL
); /* this line magically fixes a SunOS 5.x/gcc bug */
245 zmin
= (GLuint
) ((GLfloat
) zscale
* ctx
->Select
.HitMinZ
);
246 zmax
= (GLuint
) ((GLfloat
) zscale
* ctx
->Select
.HitMaxZ
);
248 write_record( ctx
, ctx
->Select
.NameStackDepth
);
249 write_record( ctx
, zmin
);
250 write_record( ctx
, zmax
);
251 for (i
= 0; i
< ctx
->Select
.NameStackDepth
; i
++) {
252 write_record( ctx
, ctx
->Select
.NameStack
[i
] );
256 ctx
->Select
.HitFlag
= GL_FALSE
;
257 ctx
->Select
.HitMinZ
= 1.0;
258 ctx
->Select
.HitMaxZ
= -1.0;
263 * Initialize the name stack.
265 * Verifies we are in select mode and resets the name stack depth and resets
266 * the hit record data in gl_selection. Marks new render mode in
267 * __struct gl_contextRec::NewState.
270 _mesa_InitNames( void )
272 GET_CURRENT_CONTEXT(ctx
);
273 FLUSH_VERTICES(ctx
, 0);
275 /* Record the hit before the HitFlag is wiped out again. */
276 if (ctx
->RenderMode
== GL_SELECT
) {
277 if (ctx
->Select
.HitFlag
) {
278 write_hit_record( ctx
);
281 ctx
->Select
.NameStackDepth
= 0;
282 ctx
->Select
.HitFlag
= GL_FALSE
;
283 ctx
->Select
.HitMinZ
= 1.0;
284 ctx
->Select
.HitMaxZ
= 0.0;
285 ctx
->NewState
|= _NEW_RENDERMODE
;
290 * Load the top-most name of the name stack.
294 * Verifies we are in selection mode and that the name stack is not empty.
295 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
296 * and replace the top-most name in the stack.
298 * sa __struct gl_contextRec::Select.
301 _mesa_LoadName( GLuint name
)
303 GET_CURRENT_CONTEXT(ctx
);
305 if (ctx
->RenderMode
!= GL_SELECT
) {
308 if (ctx
->Select
.NameStackDepth
== 0) {
309 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glLoadName" );
313 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
315 if (ctx
->Select
.HitFlag
) {
316 write_hit_record( ctx
);
318 if (ctx
->Select
.NameStackDepth
< MAX_NAME_STACK_DEPTH
) {
319 ctx
->Select
.NameStack
[ctx
->Select
.NameStackDepth
-1] = name
;
322 ctx
->Select
.NameStack
[MAX_NAME_STACK_DEPTH
-1] = name
;
328 * Push a name into the name stack.
332 * Verifies we are in selection mode and that the name stack is not full.
333 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
334 * and adds the name to the top of the name stack.
336 * sa __struct gl_contextRec::Select.
339 _mesa_PushName( GLuint name
)
341 GET_CURRENT_CONTEXT(ctx
);
343 if (ctx
->RenderMode
!= GL_SELECT
) {
347 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
348 if (ctx
->Select
.HitFlag
) {
349 write_hit_record( ctx
);
351 if (ctx
->Select
.NameStackDepth
>= MAX_NAME_STACK_DEPTH
) {
352 _mesa_error( ctx
, GL_STACK_OVERFLOW
, "glPushName" );
355 ctx
->Select
.NameStack
[ctx
->Select
.NameStackDepth
++] = name
;
360 * Pop a name into the name stack.
362 * Verifies we are in selection mode and that the name stack is not empty.
363 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
364 * and removes top-most name in the name stack.
366 * sa __struct gl_contextRec::Select.
369 _mesa_PopName( void )
371 GET_CURRENT_CONTEXT(ctx
);
373 if (ctx
->RenderMode
!= GL_SELECT
) {
377 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
378 if (ctx
->Select
.HitFlag
) {
379 write_hit_record( ctx
);
381 if (ctx
->Select
.NameStackDepth
== 0) {
382 _mesa_error( ctx
, GL_STACK_UNDERFLOW
, "glPopName" );
385 ctx
->Select
.NameStackDepth
--;
391 /**********************************************************************/
392 /** \name Render Mode */
396 * Set rasterization mode.
398 * \param mode rasterization mode.
400 * \note this function can't be put in a display list.
402 * \sa glRenderMode().
404 * Flushes the vertices and do the necessary cleanup according to the previous
405 * rasterization mode, such as writing the hit record or resent the select
406 * buffer index when exiting the select mode. Updates
407 * __struct gl_contextRec::RenderMode and notifies the driver via the
408 * dd_function_table::RenderMode callback.
411 _mesa_RenderMode( GLenum mode
)
413 GET_CURRENT_CONTEXT(ctx
);
415 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx
, 0);
417 if (MESA_VERBOSE
& VERBOSE_API
)
418 _mesa_debug(ctx
, "glRenderMode %s\n", _mesa_lookup_enum_by_nr(mode
));
420 FLUSH_VERTICES(ctx
, _NEW_RENDERMODE
);
422 switch (ctx
->RenderMode
) {
427 if (ctx
->Select
.HitFlag
) {
428 write_hit_record( ctx
);
430 if (ctx
->Select
.BufferCount
> ctx
->Select
.BufferSize
) {
433 _mesa_warning(ctx
, "Feedback buffer overflow");
438 result
= ctx
->Select
.Hits
;
440 ctx
->Select
.BufferCount
= 0;
441 ctx
->Select
.Hits
= 0;
442 ctx
->Select
.NameStackDepth
= 0;
445 if (ctx
->Feedback
.Count
> ctx
->Feedback
.BufferSize
) {
450 result
= ctx
->Feedback
.Count
;
452 ctx
->Feedback
.Count
= 0;
455 _mesa_error( ctx
, GL_INVALID_ENUM
, "glRenderMode" );
463 if (ctx
->Select
.BufferSize
==0) {
464 /* haven't called glSelectBuffer yet */
465 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glRenderMode" );
469 if (ctx
->Feedback
.BufferSize
==0) {
470 /* haven't called glFeedbackBuffer yet */
471 _mesa_error( ctx
, GL_INVALID_OPERATION
, "glRenderMode" );
475 _mesa_error( ctx
, GL_INVALID_ENUM
, "glRenderMode" );
479 ctx
->RenderMode
= mode
;
480 if (ctx
->Driver
.RenderMode
)
481 ctx
->Driver
.RenderMode( ctx
, mode
);
489 /**********************************************************************/
490 /** \name Initialization */
494 * Initialize context feedback data.
496 void _mesa_init_feedback( struct gl_context
* ctx
)
499 ctx
->Feedback
.Type
= GL_2D
; /* TODO: verify */
500 ctx
->Feedback
.Buffer
= NULL
;
501 ctx
->Feedback
.BufferSize
= 0;
502 ctx
->Feedback
.Count
= 0;
504 /* Selection/picking */
505 ctx
->Select
.Buffer
= NULL
;
506 ctx
->Select
.BufferSize
= 0;
507 ctx
->Select
.BufferCount
= 0;
508 ctx
->Select
.Hits
= 0;
509 ctx
->Select
.NameStackDepth
= 0;
512 ctx
->RenderMode
= GL_RENDER
;