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