mesa: update/fix doxygen comments
[mesa.git] / src / mesa / main / feedback.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 7.5
4 *
5 * Copyright (C) 1999-2008 Brian Paul All Rights Reserved.
6 * Copyright (C) 2009 VMware, Inc. All Rights Reserved.
7 *
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:
14 *
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
17 *
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.
24 */
25
26 /**
27 * \file feedback.c
28 * Selection and feedback modes functions.
29 */
30
31
32 #include "glheader.h"
33 #include "colormac.h"
34 #include "context.h"
35 #include "enums.h"
36 #include "feedback.h"
37 #include "macros.h"
38 #include "mtypes.h"
39
40
41 #if _HAVE_FULL_GL
42
43
44 #define FB_3D 0x01
45 #define FB_4D 0x02
46 #define FB_INDEX 0x04
47 #define FB_COLOR 0x08
48 #define FB_TEXTURE 0X10
49
50
51
52 void GLAPIENTRY
53 _mesa_FeedbackBuffer( GLsizei size, GLenum type, GLfloat *buffer )
54 {
55 GET_CURRENT_CONTEXT(ctx);
56 ASSERT_OUTSIDE_BEGIN_END(ctx);
57
58 if (ctx->RenderMode==GL_FEEDBACK) {
59 _mesa_error( ctx, GL_INVALID_OPERATION, "glFeedbackBuffer" );
60 return;
61 }
62 if (size<0) {
63 _mesa_error( ctx, GL_INVALID_VALUE, "glFeedbackBuffer(size<0)" );
64 return;
65 }
66 if (!buffer) {
67 _mesa_error( ctx, GL_INVALID_VALUE, "glFeedbackBuffer(buffer==NULL)" );
68 ctx->Feedback.BufferSize = 0;
69 return;
70 }
71
72 switch (type) {
73 case GL_2D:
74 ctx->Feedback._Mask = 0;
75 break;
76 case GL_3D:
77 ctx->Feedback._Mask = FB_3D;
78 break;
79 case GL_3D_COLOR:
80 ctx->Feedback._Mask = (FB_3D |
81 (ctx->Visual.rgbMode ? FB_COLOR : FB_INDEX));
82 break;
83 case GL_3D_COLOR_TEXTURE:
84 ctx->Feedback._Mask = (FB_3D |
85 (ctx->Visual.rgbMode ? FB_COLOR : FB_INDEX) |
86 FB_TEXTURE);
87 break;
88 case GL_4D_COLOR_TEXTURE:
89 ctx->Feedback._Mask = (FB_3D | FB_4D |
90 (ctx->Visual.rgbMode ? FB_COLOR : FB_INDEX) |
91 FB_TEXTURE);
92 break;
93 default:
94 _mesa_error( ctx, GL_INVALID_ENUM, "glFeedbackBuffer" );
95 return;
96 }
97
98 FLUSH_VERTICES(ctx, _NEW_RENDERMODE); /* Always flush */
99 ctx->Feedback.Type = type;
100 ctx->Feedback.BufferSize = size;
101 ctx->Feedback.Buffer = buffer;
102 ctx->Feedback.Count = 0; /* Becaues of this. */
103 }
104
105
106 void GLAPIENTRY
107 _mesa_PassThrough( GLfloat token )
108 {
109 GET_CURRENT_CONTEXT(ctx);
110 ASSERT_OUTSIDE_BEGIN_END(ctx);
111
112 if (ctx->RenderMode==GL_FEEDBACK) {
113 FLUSH_VERTICES(ctx, 0);
114 _mesa_feedback_token( ctx, (GLfloat) (GLint) GL_PASS_THROUGH_TOKEN );
115 _mesa_feedback_token( ctx, token );
116 }
117 }
118
119
120 /**
121 * Put a vertex into the feedback buffer.
122 */
123 void
124 _mesa_feedback_vertex(GLcontext *ctx,
125 const GLfloat win[4],
126 const GLfloat color[4],
127 GLfloat index,
128 const GLfloat texcoord[4])
129 {
130 _mesa_feedback_token( ctx, win[0] );
131 _mesa_feedback_token( ctx, win[1] );
132 if (ctx->Feedback._Mask & FB_3D) {
133 _mesa_feedback_token( ctx, win[2] );
134 }
135 if (ctx->Feedback._Mask & FB_4D) {
136 _mesa_feedback_token( ctx, win[3] );
137 }
138 if (ctx->Feedback._Mask & FB_INDEX) {
139 _mesa_feedback_token( ctx, (GLfloat) index );
140 }
141 if (ctx->Feedback._Mask & FB_COLOR) {
142 _mesa_feedback_token( ctx, color[0] );
143 _mesa_feedback_token( ctx, color[1] );
144 _mesa_feedback_token( ctx, color[2] );
145 _mesa_feedback_token( ctx, color[3] );
146 }
147 if (ctx->Feedback._Mask & FB_TEXTURE) {
148 _mesa_feedback_token( ctx, texcoord[0] );
149 _mesa_feedback_token( ctx, texcoord[1] );
150 _mesa_feedback_token( ctx, texcoord[2] );
151 _mesa_feedback_token( ctx, texcoord[3] );
152 }
153 }
154
155
156 #endif /* _HAVE_FULL_GL */
157
158
159 /**********************************************************************/
160 /** \name Selection */
161 /*@{*/
162
163 /**
164 * Establish a buffer for selection mode values.
165 *
166 * \param size buffer size.
167 * \param buffer buffer.
168 *
169 * \sa glSelectBuffer().
170 *
171 * \note this function can't be put in a display list.
172 *
173 * Verifies we're not in selection mode, flushes the vertices and initialize
174 * the fields in __GLcontextRec::Select with the given buffer.
175 */
176 void GLAPIENTRY
177 _mesa_SelectBuffer( GLsizei size, GLuint *buffer )
178 {
179 GET_CURRENT_CONTEXT(ctx);
180 ASSERT_OUTSIDE_BEGIN_END(ctx);
181
182 if (ctx->RenderMode==GL_SELECT) {
183 _mesa_error( ctx, GL_INVALID_OPERATION, "glSelectBuffer" );
184 return; /* KW: added return */
185 }
186
187 FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
188 ctx->Select.Buffer = buffer;
189 ctx->Select.BufferSize = size;
190 ctx->Select.BufferCount = 0;
191 ctx->Select.HitFlag = GL_FALSE;
192 ctx->Select.HitMinZ = 1.0;
193 ctx->Select.HitMaxZ = 0.0;
194 }
195
196
197 /**
198 * Write a value of a record into the selection buffer.
199 *
200 * \param ctx GL context.
201 * \param value value.
202 *
203 * Verifies there is free space in the buffer to write the value and
204 * increments the pointer.
205 */
206 static INLINE void
207 write_record(GLcontext *ctx, GLuint value)
208 {
209 if (ctx->Select.BufferCount < ctx->Select.BufferSize) {
210 ctx->Select.Buffer[ctx->Select.BufferCount] = value;
211 }
212 ctx->Select.BufferCount++;
213 }
214
215
216 /**
217 * Update the hit flag and the maximum and minimum depth values.
218 *
219 * \param ctx GL context.
220 * \param z depth.
221 *
222 * Sets gl_selection::HitFlag and updates gl_selection::HitMinZ and
223 * gl_selection::HitMaxZ.
224 */
225 void
226 _mesa_update_hitflag(GLcontext *ctx, GLfloat z)
227 {
228 ctx->Select.HitFlag = GL_TRUE;
229 if (z < ctx->Select.HitMinZ) {
230 ctx->Select.HitMinZ = z;
231 }
232 if (z > ctx->Select.HitMaxZ) {
233 ctx->Select.HitMaxZ = z;
234 }
235 }
236
237
238 /**
239 * Write the hit record.
240 *
241 * \param ctx GL context.
242 *
243 * Write the hit record, i.e., the number of names in the stack, the minimum and
244 * maximum depth values and the number of names in the name stack at the time
245 * of the event. Resets the hit flag.
246 *
247 * \sa gl_selection.
248 */
249 static void
250 write_hit_record(GLcontext *ctx)
251 {
252 GLuint i;
253 GLuint zmin, zmax, zscale = (~0u);
254
255 /* HitMinZ and HitMaxZ are in [0,1]. Multiply these values by */
256 /* 2^32-1 and round to nearest unsigned integer. */
257
258 assert( ctx != NULL ); /* this line magically fixes a SunOS 5.x/gcc bug */
259 zmin = (GLuint) ((GLfloat) zscale * ctx->Select.HitMinZ);
260 zmax = (GLuint) ((GLfloat) zscale * ctx->Select.HitMaxZ);
261
262 write_record( ctx, ctx->Select.NameStackDepth );
263 write_record( ctx, zmin );
264 write_record( ctx, zmax );
265 for (i = 0; i < ctx->Select.NameStackDepth; i++) {
266 write_record( ctx, ctx->Select.NameStack[i] );
267 }
268
269 ctx->Select.Hits++;
270 ctx->Select.HitFlag = GL_FALSE;
271 ctx->Select.HitMinZ = 1.0;
272 ctx->Select.HitMaxZ = -1.0;
273 }
274
275
276 /**
277 * Initialize the name stack.
278 *
279 * Verifies we are in select mode and resets the name stack depth and resets
280 * the hit record data in gl_selection. Marks new render mode in
281 * __GLcontextRec::NewState.
282 */
283 void GLAPIENTRY
284 _mesa_InitNames( void )
285 {
286 GET_CURRENT_CONTEXT(ctx);
287 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
288
289 /* Record the hit before the HitFlag is wiped out again. */
290 if (ctx->RenderMode == GL_SELECT) {
291 if (ctx->Select.HitFlag) {
292 write_hit_record( ctx );
293 }
294 }
295 ctx->Select.NameStackDepth = 0;
296 ctx->Select.HitFlag = GL_FALSE;
297 ctx->Select.HitMinZ = 1.0;
298 ctx->Select.HitMaxZ = 0.0;
299 ctx->NewState |= _NEW_RENDERMODE;
300 }
301
302
303 /**
304 * Load the top-most name of the name stack.
305 *
306 * \param name name.
307 *
308 * Verifies we are in selection mode and that the name stack is not empty.
309 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
310 * and replace the top-most name in the stack.
311 *
312 * sa __GLcontextRec::Select.
313 */
314 void GLAPIENTRY
315 _mesa_LoadName( GLuint name )
316 {
317 GET_CURRENT_CONTEXT(ctx);
318 ASSERT_OUTSIDE_BEGIN_END(ctx);
319
320 if (ctx->RenderMode != GL_SELECT) {
321 return;
322 }
323 if (ctx->Select.NameStackDepth == 0) {
324 _mesa_error( ctx, GL_INVALID_OPERATION, "glLoadName" );
325 return;
326 }
327
328 FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
329
330 if (ctx->Select.HitFlag) {
331 write_hit_record( ctx );
332 }
333 if (ctx->Select.NameStackDepth < MAX_NAME_STACK_DEPTH) {
334 ctx->Select.NameStack[ctx->Select.NameStackDepth-1] = name;
335 }
336 else {
337 ctx->Select.NameStack[MAX_NAME_STACK_DEPTH-1] = name;
338 }
339 }
340
341
342 /**
343 * Push a name into the name stack.
344 *
345 * \param name name.
346 *
347 * Verifies we are in selection mode and that the name stack is not full.
348 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
349 * and adds the name to the top of the name stack.
350 *
351 * sa __GLcontextRec::Select.
352 */
353 void GLAPIENTRY
354 _mesa_PushName( GLuint name )
355 {
356 GET_CURRENT_CONTEXT(ctx);
357 ASSERT_OUTSIDE_BEGIN_END(ctx);
358
359 if (ctx->RenderMode != GL_SELECT) {
360 return;
361 }
362
363 FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
364 if (ctx->Select.HitFlag) {
365 write_hit_record( ctx );
366 }
367 if (ctx->Select.NameStackDepth >= MAX_NAME_STACK_DEPTH) {
368 _mesa_error( ctx, GL_STACK_OVERFLOW, "glPushName" );
369 }
370 else
371 ctx->Select.NameStack[ctx->Select.NameStackDepth++] = name;
372 }
373
374
375 /**
376 * Pop a name into the name stack.
377 *
378 * Verifies we are in selection mode and that the name stack is not empty.
379 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
380 * and removes top-most name in the name stack.
381 *
382 * sa __GLcontextRec::Select.
383 */
384 void GLAPIENTRY
385 _mesa_PopName( void )
386 {
387 GET_CURRENT_CONTEXT(ctx);
388 ASSERT_OUTSIDE_BEGIN_END(ctx);
389
390 if (ctx->RenderMode != GL_SELECT) {
391 return;
392 }
393
394 FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
395 if (ctx->Select.HitFlag) {
396 write_hit_record( ctx );
397 }
398 if (ctx->Select.NameStackDepth == 0) {
399 _mesa_error( ctx, GL_STACK_UNDERFLOW, "glPopName" );
400 }
401 else
402 ctx->Select.NameStackDepth--;
403 }
404
405 /*@}*/
406
407
408 /**********************************************************************/
409 /** \name Render Mode */
410 /*@{*/
411
412 /**
413 * Set rasterization mode.
414 *
415 * \param mode rasterization mode.
416 *
417 * \note this function can't be put in a display list.
418 *
419 * \sa glRenderMode().
420 *
421 * Flushes the vertices and do the necessary cleanup according to the previous
422 * rasterization mode, such as writing the hit record or resent the select
423 * buffer index when exiting the select mode. Updates
424 * __GLcontextRec::RenderMode and notifies the driver via the
425 * dd_function_table::RenderMode callback.
426 */
427 GLint GLAPIENTRY
428 _mesa_RenderMode( GLenum mode )
429 {
430 GET_CURRENT_CONTEXT(ctx);
431 GLint result;
432 ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
433
434 if (MESA_VERBOSE & VERBOSE_API)
435 _mesa_debug(ctx, "glRenderMode %s\n", _mesa_lookup_enum_by_nr(mode));
436
437 FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
438
439 switch (ctx->RenderMode) {
440 case GL_RENDER:
441 result = 0;
442 break;
443 case GL_SELECT:
444 if (ctx->Select.HitFlag) {
445 write_hit_record( ctx );
446 }
447 if (ctx->Select.BufferCount > ctx->Select.BufferSize) {
448 /* overflow */
449 #ifdef DEBUG
450 _mesa_warning(ctx, "Feedback buffer overflow");
451 #endif
452 result = -1;
453 }
454 else {
455 result = ctx->Select.Hits;
456 }
457 ctx->Select.BufferCount = 0;
458 ctx->Select.Hits = 0;
459 ctx->Select.NameStackDepth = 0;
460 break;
461 #if _HAVE_FULL_GL
462 case GL_FEEDBACK:
463 if (ctx->Feedback.Count > ctx->Feedback.BufferSize) {
464 /* overflow */
465 result = -1;
466 }
467 else {
468 result = ctx->Feedback.Count;
469 }
470 ctx->Feedback.Count = 0;
471 break;
472 #endif
473 default:
474 _mesa_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
475 return 0;
476 }
477
478 switch (mode) {
479 case GL_RENDER:
480 break;
481 case GL_SELECT:
482 if (ctx->Select.BufferSize==0) {
483 /* haven't called glSelectBuffer yet */
484 _mesa_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
485 }
486 break;
487 #if _HAVE_FULL_GL
488 case GL_FEEDBACK:
489 if (ctx->Feedback.BufferSize==0) {
490 /* haven't called glFeedbackBuffer yet */
491 _mesa_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
492 }
493 break;
494 #endif
495 default:
496 _mesa_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
497 return 0;
498 }
499
500 ctx->RenderMode = mode;
501 if (ctx->Driver.RenderMode)
502 ctx->Driver.RenderMode( ctx, mode );
503
504 return result;
505 }
506
507 /*@}*/
508
509
510 /**********************************************************************/
511 /** \name Initialization */
512 /*@{*/
513
514 /**
515 * Initialize context feedback data.
516 */
517 void _mesa_init_feedback( GLcontext * ctx )
518 {
519 /* Feedback */
520 ctx->Feedback.Type = GL_2D; /* TODO: verify */
521 ctx->Feedback.Buffer = NULL;
522 ctx->Feedback.BufferSize = 0;
523 ctx->Feedback.Count = 0;
524
525 /* Selection/picking */
526 ctx->Select.Buffer = NULL;
527 ctx->Select.BufferSize = 0;
528 ctx->Select.BufferCount = 0;
529 ctx->Select.Hits = 0;
530 ctx->Select.NameStackDepth = 0;
531
532 /* Miscellaneous */
533 ctx->RenderMode = GL_RENDER;
534 }
535
536 /*@}*/