438fbae46994aad5be3f4fd25743ca82ec75524b
[mesa.git] / src / mesa / main / feedback.c
1 /* $Id: feedback.c,v 1.15 2000/10/31 18:09:44 keithw Exp $ */
2
3 /*
4 * Mesa 3-D graphics library
5 * Version: 3.3
6 *
7 * Copyright (C) 1999-2000 Brian Paul All Rights Reserved.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
23 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27
28 #ifdef PC_HEADER
29 #include "all.h"
30 #else
31 #include "glheader.h"
32 #include "colormac.h"
33 #include "context.h"
34 #include "enums.h"
35 #include "feedback.h"
36 #include "macros.h"
37 #include "mmath.h"
38 #include "types.h"
39 #endif
40
41
42
43 #define FB_3D 0x01
44 #define FB_4D 0x02
45 #define FB_INDEX 0x04
46 #define FB_COLOR 0x08
47 #define FB_TEXTURE 0X10
48
49
50
51 void
52 _mesa_FeedbackBuffer( GLsizei size, GLenum type, GLfloat *buffer )
53 {
54 GET_CURRENT_CONTEXT(ctx);
55 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH( ctx, "glFeedbackBuffer" );
56
57 if (ctx->RenderMode==GL_FEEDBACK) {
58 gl_error( ctx, GL_INVALID_OPERATION, "glFeedbackBuffer" );
59 return;
60 }
61
62 if (size<0) {
63 gl_error( ctx, GL_INVALID_VALUE, "glFeedbackBuffer(size<0)" );
64 return;
65 }
66 if (!buffer) {
67 gl_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 ctx->Feedback.Type = type;
76 break;
77 case GL_3D:
78 ctx->Feedback.Mask = FB_3D;
79 ctx->Feedback.Type = type;
80 break;
81 case GL_3D_COLOR:
82 ctx->Feedback.Mask = FB_3D
83 | (ctx->Visual.RGBAflag ? FB_COLOR : FB_INDEX);
84 ctx->Feedback.Type = type;
85 break;
86 case GL_3D_COLOR_TEXTURE:
87 ctx->Feedback.Mask = FB_3D
88 | (ctx->Visual.RGBAflag ? FB_COLOR : FB_INDEX)
89 | FB_TEXTURE;
90 ctx->Feedback.Type = type;
91 break;
92 case GL_4D_COLOR_TEXTURE:
93 ctx->Feedback.Mask = FB_3D | FB_4D
94 | (ctx->Visual.RGBAflag ? FB_COLOR : FB_INDEX)
95 | FB_TEXTURE;
96 ctx->Feedback.Type = type;
97 break;
98 default:
99 ctx->Feedback.Mask = 0;
100 gl_error( ctx, GL_INVALID_ENUM, "glFeedbackBuffer" );
101 }
102
103 ctx->Feedback.BufferSize = size;
104 ctx->Feedback.Buffer = buffer;
105 ctx->Feedback.Count = 0;
106 ctx->NewState |= _NEW_FEEDBACK_SELECT;
107 }
108
109
110
111 void
112 _mesa_PassThrough( GLfloat token )
113 {
114 GET_CURRENT_CONTEXT(ctx);
115 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glPassThrough");
116
117 if (ctx->RenderMode==GL_FEEDBACK) {
118 FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_PASS_THROUGH_TOKEN );
119 FEEDBACK_TOKEN( ctx, token );
120 }
121 }
122
123
124
125 /*
126 * Put a vertex into the feedback buffer.
127 */
128 void gl_feedback_vertex( GLcontext *ctx,
129 const GLfloat win[4],
130 const GLfloat color[4],
131 GLuint index,
132 const GLfloat texcoord[4] )
133 {
134 FEEDBACK_TOKEN( ctx, win[0] );
135 FEEDBACK_TOKEN( ctx, win[1] );
136 if (ctx->Feedback.Mask & FB_3D) {
137 FEEDBACK_TOKEN( ctx, win[2] );
138 }
139 if (ctx->Feedback.Mask & FB_4D) {
140 FEEDBACK_TOKEN( ctx, win[3] );
141 }
142 if (ctx->Feedback.Mask & FB_INDEX) {
143 FEEDBACK_TOKEN( ctx, (GLfloat) index );
144 }
145 if (ctx->Feedback.Mask & FB_COLOR) {
146 FEEDBACK_TOKEN( ctx, color[0] );
147 FEEDBACK_TOKEN( ctx, color[1] );
148 FEEDBACK_TOKEN( ctx, color[2] );
149 FEEDBACK_TOKEN( ctx, color[3] );
150 }
151 if (ctx->Feedback.Mask & FB_TEXTURE) {
152 FEEDBACK_TOKEN( ctx, texcoord[0] );
153 FEEDBACK_TOKEN( ctx, texcoord[1] );
154 FEEDBACK_TOKEN( ctx, texcoord[2] );
155 FEEDBACK_TOKEN( ctx, texcoord[3] );
156 }
157 }
158
159
160
161 static void feedback_vertex( GLcontext *ctx, GLuint v, GLuint pv )
162 {
163 GLfloat win[4];
164 GLfloat color[4];
165 GLfloat tc[4];
166 GLuint texUnit = ctx->Texture.CurrentTransformUnit;
167 const struct vertex_buffer *VB = ctx->VB;
168 GLuint index;
169
170 win[0] = VB->Win.data[v][0];
171 win[1] = VB->Win.data[v][1];
172 win[2] = VB->Win.data[v][2] / ctx->Visual.DepthMaxF;
173 win[3] = 1.0 / VB->Win.data[v][3];
174
175 if (ctx->Light.ShadeModel == GL_SMOOTH)
176 pv = v;
177
178 color[0] = CHAN_TO_FLOAT(VB->ColorPtr->data[pv][0]);
179 color[1] = CHAN_TO_FLOAT(VB->ColorPtr->data[pv][1]);
180 color[2] = CHAN_TO_FLOAT(VB->ColorPtr->data[pv][2]);
181 color[3] = CHAN_TO_FLOAT(VB->ColorPtr->data[pv][3]);
182
183 if (VB->TexCoordPtr[texUnit]->size == 4 &&
184 VB->TexCoordPtr[texUnit]->data[v][3] != 0.0) {
185 GLfloat invq = 1.0F / VB->TexCoordPtr[texUnit]->data[v][3];
186 tc[0] = VB->TexCoordPtr[texUnit]->data[v][0] * invq;
187 tc[1] = VB->TexCoordPtr[texUnit]->data[v][1] * invq;
188 tc[2] = VB->TexCoordPtr[texUnit]->data[v][2] * invq;
189 tc[3] = VB->TexCoordPtr[texUnit]->data[v][3];
190 }
191 else {
192 ASSIGN_4V(tc, 0,0,0,1);
193 COPY_SZ_4V(tc,
194 VB->TexCoordPtr[texUnit]->size,
195 VB->TexCoordPtr[texUnit]->data[v]);
196 }
197
198 if (VB->IndexPtr)
199 index = VB->IndexPtr->data[v];
200 else
201 index = 0;
202
203 gl_feedback_vertex( ctx, win, color, index, tc );
204 }
205
206
207 static GLboolean cull_triangle( GLcontext *ctx,
208 GLuint v0, GLuint v1, GLuint v2, GLuint pv )
209 {
210 struct vertex_buffer *VB = ctx->VB;
211 GLfloat (*win)[4] = VB->Win.data;
212 GLfloat ex = win[v1][0] - win[v0][0];
213 GLfloat ey = win[v1][1] - win[v0][1];
214 GLfloat fx = win[v2][0] - win[v0][0];
215 GLfloat fy = win[v2][1] - win[v0][1];
216 GLfloat c = ex*fy-ey*fx;
217
218 if (c * ctx->backface_sign > 0)
219 return 0;
220
221 return 1;
222 }
223
224
225
226 /*
227 * Put triangle in feedback buffer.
228 */
229 void gl_feedback_triangle( GLcontext *ctx,
230 GLuint v0, GLuint v1, GLuint v2, GLuint pv )
231 {
232 if (cull_triangle( ctx, v0, v1, v2, 0 )) {
233 FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_POLYGON_TOKEN );
234 FEEDBACK_TOKEN( ctx, (GLfloat) 3 ); /* three vertices */
235
236 feedback_vertex( ctx, v0, pv );
237 feedback_vertex( ctx, v1, pv );
238 feedback_vertex( ctx, v2, pv );
239 }
240 }
241
242
243 void gl_feedback_line( GLcontext *ctx, GLuint v1, GLuint v2, GLuint pv )
244 {
245 GLenum token = GL_LINE_TOKEN;
246
247 if (ctx->StippleCounter==0)
248 token = GL_LINE_RESET_TOKEN;
249
250 FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) token );
251
252 feedback_vertex( ctx, v1, pv );
253 feedback_vertex( ctx, v2, pv );
254
255 ctx->StippleCounter++;
256 }
257
258
259 void gl_feedback_points( GLcontext *ctx, GLuint first, GLuint last )
260 {
261 const struct vertex_buffer *VB = ctx->VB;
262 GLuint i;
263
264 for (i=first;i<=last;i++) {
265 if (VB->ClipMask[i]==0) {
266 FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_POINT_TOKEN );
267 feedback_vertex( ctx, i, i );
268 }
269 }
270 }
271
272
273
274
275
276 /**********************************************************************/
277 /* Selection */
278 /**********************************************************************/
279
280
281 /*
282 * NOTE: this function can't be put in a display list.
283 */
284 void
285 _mesa_SelectBuffer( GLsizei size, GLuint *buffer )
286 {
287 GET_CURRENT_CONTEXT(ctx);
288 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glSelectBuffer");
289 if (ctx->RenderMode==GL_SELECT) {
290 gl_error( ctx, GL_INVALID_OPERATION, "glSelectBuffer" );
291 }
292 ctx->Select.Buffer = buffer;
293 ctx->Select.BufferSize = size;
294 ctx->Select.BufferCount = 0;
295
296 ctx->Select.HitFlag = GL_FALSE;
297 ctx->Select.HitMinZ = 1.0;
298 ctx->Select.HitMaxZ = 0.0;
299
300 ctx->NewState |= _NEW_FEEDBACK_SELECT;
301 }
302
303
304 #define WRITE_RECORD( CTX, V ) \
305 if (CTX->Select.BufferCount < CTX->Select.BufferSize) { \
306 CTX->Select.Buffer[CTX->Select.BufferCount] = (V); \
307 } \
308 CTX->Select.BufferCount++;
309
310
311
312 void gl_update_hitflag( GLcontext *ctx, GLfloat z )
313 {
314 ctx->Select.HitFlag = GL_TRUE;
315 if (z < ctx->Select.HitMinZ) {
316 ctx->Select.HitMinZ = z;
317 }
318 if (z > ctx->Select.HitMaxZ) {
319 ctx->Select.HitMaxZ = z;
320 }
321 }
322
323 void gl_select_triangle( GLcontext *ctx,
324 GLuint v0, GLuint v1, GLuint v2, GLuint pv )
325 {
326 const struct vertex_buffer *VB = ctx->VB;
327
328 if (cull_triangle( ctx, v0, v1, v2, 0 )) {
329 const GLfloat zs = 1.0F / ctx->Visual.DepthMaxF;
330 gl_update_hitflag( ctx, VB->Win.data[v0][2] * zs );
331 gl_update_hitflag( ctx, VB->Win.data[v1][2] * zs );
332 gl_update_hitflag( ctx, VB->Win.data[v2][2] * zs );
333 }
334 }
335
336
337 void gl_select_line( GLcontext *ctx,
338 GLuint v0, GLuint v1, GLuint pv )
339 {
340 const struct vertex_buffer *VB = ctx->VB;
341 const GLfloat zs = 1.0F / ctx->Visual.DepthMaxF;
342 gl_update_hitflag( ctx, VB->Win.data[v0][2] * zs );
343 gl_update_hitflag( ctx, VB->Win.data[v1][2] * zs );
344 }
345
346
347 void gl_select_points( GLcontext *ctx, GLuint first, GLuint last )
348 {
349 struct vertex_buffer *VB = ctx->VB;
350 const GLfloat zs = 1.0F / ctx->Visual.DepthMaxF;
351 GLuint i;
352
353 for (i=first;i<=last;i++) {
354 if (VB->ClipMask[i]==0) {
355 gl_update_hitflag( ctx, VB->Win.data[i][2] * zs );
356 }
357 }
358 }
359
360
361 static void write_hit_record( GLcontext *ctx )
362 {
363 GLuint i;
364 GLuint zmin, zmax, zscale = (~0u);
365
366 /* HitMinZ and HitMaxZ are in [0,1]. Multiply these values by */
367 /* 2^32-1 and round to nearest unsigned integer. */
368
369 assert( ctx != NULL ); /* this line magically fixes a SunOS 5.x/gcc bug */
370 zmin = (GLuint) ((GLfloat) zscale * ctx->Select.HitMinZ);
371 zmax = (GLuint) ((GLfloat) zscale * ctx->Select.HitMaxZ);
372
373 WRITE_RECORD( ctx, ctx->Select.NameStackDepth );
374 WRITE_RECORD( ctx, zmin );
375 WRITE_RECORD( ctx, zmax );
376 for (i = 0; i < ctx->Select.NameStackDepth; i++) {
377 WRITE_RECORD( ctx, ctx->Select.NameStack[i] );
378 }
379
380 ctx->Select.Hits++;
381 ctx->Select.HitFlag = GL_FALSE;
382 ctx->Select.HitMinZ = 1.0;
383 ctx->Select.HitMaxZ = -1.0;
384 }
385
386
387
388 void
389 _mesa_InitNames( void )
390 {
391 GET_CURRENT_CONTEXT(ctx);
392 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glInitNames");
393 /* Record the hit before the HitFlag is wiped out again. */
394 if (ctx->RenderMode == GL_SELECT) {
395 if (ctx->Select.HitFlag) {
396 write_hit_record( ctx );
397 }
398 }
399 ctx->Select.NameStackDepth = 0;
400 ctx->Select.HitFlag = GL_FALSE;
401 ctx->Select.HitMinZ = 1.0;
402 ctx->Select.HitMaxZ = 0.0;
403 ctx->NewState |= _NEW_FEEDBACK_SELECT;
404 }
405
406
407
408 void
409 _mesa_LoadName( GLuint name )
410 {
411 GET_CURRENT_CONTEXT(ctx);
412 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glLoadName");
413 if (ctx->RenderMode != GL_SELECT) {
414 return;
415 }
416 if (ctx->Select.NameStackDepth == 0) {
417 gl_error( ctx, GL_INVALID_OPERATION, "glLoadName" );
418 return;
419 }
420 if (ctx->Select.HitFlag) {
421 write_hit_record( ctx );
422 }
423 if (ctx->Select.NameStackDepth < MAX_NAME_STACK_DEPTH) {
424 ctx->Select.NameStack[ctx->Select.NameStackDepth-1] = name;
425 }
426 else {
427 ctx->Select.NameStack[MAX_NAME_STACK_DEPTH-1] = name;
428 }
429 ctx->NewState |= _NEW_FEEDBACK_SELECT;
430 }
431
432
433 void
434 _mesa_PushName( GLuint name )
435 {
436 GET_CURRENT_CONTEXT(ctx);
437 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glPushName");
438 if (ctx->RenderMode != GL_SELECT) {
439 return;
440 }
441 if (ctx->Select.HitFlag) {
442 write_hit_record( ctx );
443 }
444 if (ctx->Select.NameStackDepth < MAX_NAME_STACK_DEPTH) {
445 ctx->Select.NameStack[ctx->Select.NameStackDepth++] = name;
446 }
447 else {
448 gl_error( ctx, GL_STACK_OVERFLOW, "glPushName" );
449 }
450 ctx->NewState |= _NEW_FEEDBACK_SELECT;
451 }
452
453
454
455 void
456 _mesa_PopName( void )
457 {
458 GET_CURRENT_CONTEXT(ctx);
459 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glPopName");
460 if (ctx->RenderMode != GL_SELECT) {
461 return;
462 }
463 if (ctx->Select.HitFlag) {
464 write_hit_record( ctx );
465 }
466 if (ctx->Select.NameStackDepth > 0) {
467 ctx->Select.NameStackDepth--;
468 }
469 else {
470 gl_error( ctx, GL_STACK_UNDERFLOW, "glPopName" );
471 }
472 ctx->NewState |= _NEW_FEEDBACK_SELECT;
473 }
474
475
476
477 /**********************************************************************/
478 /* Render Mode */
479 /**********************************************************************/
480
481
482
483 /*
484 * NOTE: this function can't be put in a display list.
485 */
486 GLint
487 _mesa_RenderMode( GLenum mode )
488 {
489 GET_CURRENT_CONTEXT(ctx);
490 GLint result;
491
492 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, "glRenderMode", 0);
493
494 if (MESA_VERBOSE & VERBOSE_API)
495 fprintf(stderr, "glRenderMode %s\n", gl_lookup_enum_by_nr(mode));
496
497 ctx->TriangleCaps &= ~(DD_FEEDBACK|DD_SELECT);
498
499 switch (ctx->RenderMode) {
500 case GL_RENDER:
501 result = 0;
502 break;
503 case GL_SELECT:
504 if (ctx->Select.HitFlag) {
505 write_hit_record( ctx );
506 }
507 if (ctx->Select.BufferCount > ctx->Select.BufferSize) {
508 /* overflow */
509 #ifdef DEBUG
510 _mesa_warning(ctx, "Feedback buffer overflow");
511 #endif
512 result = -1;
513 }
514 else {
515 result = ctx->Select.Hits;
516 }
517 ctx->Select.BufferCount = 0;
518 ctx->Select.Hits = 0;
519 ctx->Select.NameStackDepth = 0;
520 ctx->NewState |= _NEW_FEEDBACK_SELECT;
521 break;
522 case GL_FEEDBACK:
523 if (ctx->Feedback.Count > ctx->Feedback.BufferSize) {
524 /* overflow */
525 result = -1;
526 }
527 else {
528 result = ctx->Feedback.Count;
529 }
530 ctx->Feedback.Count = 0;
531 ctx->NewState |= _NEW_FEEDBACK_SELECT;
532 break;
533 default:
534 gl_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
535 return 0;
536 }
537
538 switch (mode) {
539 case GL_RENDER:
540 break;
541 case GL_SELECT:
542 ctx->TriangleCaps |= DD_SELECT;
543 if (ctx->Select.BufferSize==0) {
544 /* haven't called glSelectBuffer yet */
545 gl_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
546 }
547 break;
548 case GL_FEEDBACK:
549 ctx->TriangleCaps |= DD_FEEDBACK;
550 if (ctx->Feedback.BufferSize==0) {
551 /* haven't called glFeedbackBuffer yet */
552 gl_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
553 }
554 break;
555 default:
556 gl_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
557 return 0;
558 }
559
560 ctx->RenderMode = mode;
561 ctx->NewState |= _NEW_RENDERMODE;
562
563 return result;
564 }
565