3a38db1d62ca727f205eb7eedf9cccfa7be258fa
[mesa.git] / src / mesa / main / feedback.c
1 /* $Id: feedback.c,v 1.9 2000/03/03 17:47:39 brianp 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 "context.h"
33 #include "enums.h"
34 #include "feedback.h"
35 #include "macros.h"
36 #include "mmath.h"
37 #include "types.h"
38 #include "triangle.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 }
107
108
109
110 void
111 _mesa_PassThrough( GLfloat token )
112 {
113 GET_CURRENT_CONTEXT(ctx);
114 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glPassThrough");
115
116 if (ctx->RenderMode==GL_FEEDBACK) {
117 FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_PASS_THROUGH_TOKEN );
118 FEEDBACK_TOKEN( ctx, token );
119 }
120 }
121
122
123
124 /*
125 * Put a vertex into the feedback buffer.
126 */
127 void gl_feedback_vertex( GLcontext *ctx,
128 const GLfloat win[4],
129 const GLfloat color[4],
130 GLuint index,
131 const GLfloat texcoord[4] )
132 {
133 FEEDBACK_TOKEN( ctx, win[0] );
134 FEEDBACK_TOKEN( ctx, win[1] );
135 if (ctx->Feedback.Mask & FB_3D) {
136 printf("FB %g\n", win[2]);
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 UBYTE_RGBA_TO_FLOAT_RGBA( color, VB->ColorPtr->data[pv] );
179
180 if (VB->TexCoordPtr[texUnit]->size == 4 &&
181 VB->TexCoordPtr[texUnit]->data[v][3] != 0.0) {
182 GLfloat invq = 1.0F / VB->TexCoordPtr[texUnit]->data[v][3];
183 tc[0] = VB->TexCoordPtr[texUnit]->data[v][0] * invq;
184 tc[1] = VB->TexCoordPtr[texUnit]->data[v][1] * invq;
185 tc[2] = VB->TexCoordPtr[texUnit]->data[v][2] * invq;
186 tc[3] = VB->TexCoordPtr[texUnit]->data[v][3];
187 }
188 else {
189 ASSIGN_4V(tc, 0,0,0,1);
190 COPY_SZ_4V(tc,
191 VB->TexCoordPtr[texUnit]->size,
192 VB->TexCoordPtr[texUnit]->data[v]);
193 }
194
195 if (VB->IndexPtr)
196 index = VB->IndexPtr->data[v];
197 else
198 index = 0;
199
200 gl_feedback_vertex( ctx, win, color, index, tc );
201 }
202
203
204
205 /*
206 * Put triangle in feedback buffer.
207 */
208 void gl_feedback_triangle( GLcontext *ctx,
209 GLuint v0, GLuint v1, GLuint v2, GLuint pv )
210 {
211 if (gl_cull_triangle( ctx, v0, v1, v2, 0 )) {
212 FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_POLYGON_TOKEN );
213 FEEDBACK_TOKEN( ctx, (GLfloat) 3 ); /* three vertices */
214
215 feedback_vertex( ctx, v0, pv );
216 feedback_vertex( ctx, v1, pv );
217 feedback_vertex( ctx, v2, pv );
218 }
219 }
220
221
222 void gl_feedback_line( GLcontext *ctx, GLuint v1, GLuint v2, GLuint pv )
223 {
224 GLenum token = GL_LINE_TOKEN;
225
226 if (ctx->StippleCounter==0)
227 token = GL_LINE_RESET_TOKEN;
228
229 FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) token );
230
231 feedback_vertex( ctx, v1, pv );
232 feedback_vertex( ctx, v2, pv );
233
234 ctx->StippleCounter++;
235 }
236
237
238 void gl_feedback_points( GLcontext *ctx, GLuint first, GLuint last )
239 {
240 const struct vertex_buffer *VB = ctx->VB;
241 GLuint i;
242
243 for (i=first;i<=last;i++) {
244 if (VB->ClipMask[i]==0) {
245 FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_POINT_TOKEN );
246 feedback_vertex( ctx, i, i );
247 }
248 }
249 }
250
251
252
253
254
255 /**********************************************************************/
256 /* Selection */
257 /**********************************************************************/
258
259
260 /*
261 * NOTE: this function can't be put in a display list.
262 */
263 void
264 _mesa_SelectBuffer( GLsizei size, GLuint *buffer )
265 {
266 GET_CURRENT_CONTEXT(ctx);
267 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glSelectBuffer");
268 if (ctx->RenderMode==GL_SELECT) {
269 gl_error( ctx, GL_INVALID_OPERATION, "glSelectBuffer" );
270 }
271 ctx->Select.Buffer = buffer;
272 ctx->Select.BufferSize = size;
273 ctx->Select.BufferCount = 0;
274
275 ctx->Select.HitFlag = GL_FALSE;
276 ctx->Select.HitMinZ = 1.0;
277 ctx->Select.HitMaxZ = 0.0;
278 }
279
280
281 #define WRITE_RECORD( CTX, V ) \
282 if (CTX->Select.BufferCount < CTX->Select.BufferSize) { \
283 CTX->Select.Buffer[CTX->Select.BufferCount] = (V); \
284 } \
285 CTX->Select.BufferCount++;
286
287
288
289 void gl_update_hitflag( GLcontext *ctx, GLfloat z )
290 {
291 ctx->Select.HitFlag = GL_TRUE;
292 if (z < ctx->Select.HitMinZ) {
293 ctx->Select.HitMinZ = z;
294 }
295 if (z > ctx->Select.HitMaxZ) {
296 ctx->Select.HitMaxZ = z;
297 }
298 }
299
300 void gl_select_triangle( GLcontext *ctx,
301 GLuint v0, GLuint v1, GLuint v2, GLuint pv )
302 {
303 const struct vertex_buffer *VB = ctx->VB;
304
305 if (gl_cull_triangle( ctx, v0, v1, v2, 0 )) {
306 const GLfloat zs = 1.0F / ctx->Visual->DepthMaxF;
307 gl_update_hitflag( ctx, VB->Win.data[v0][2] * zs );
308 gl_update_hitflag( ctx, VB->Win.data[v1][2] * zs );
309 gl_update_hitflag( ctx, VB->Win.data[v2][2] * zs );
310 }
311 }
312
313
314 void gl_select_line( GLcontext *ctx,
315 GLuint v0, GLuint v1, GLuint pv )
316 {
317 const struct vertex_buffer *VB = ctx->VB;
318 const GLfloat zs = 1.0F / ctx->Visual->DepthMaxF;
319 gl_update_hitflag( ctx, VB->Win.data[v0][2] * zs );
320 gl_update_hitflag( ctx, VB->Win.data[v1][2] * zs );
321 }
322
323
324 void gl_select_points( GLcontext *ctx, GLuint first, GLuint last )
325 {
326 struct vertex_buffer *VB = ctx->VB;
327 const GLfloat zs = 1.0F / ctx->Visual->DepthMaxF;
328 GLuint i;
329
330 for (i=first;i<=last;i++) {
331 if (VB->ClipMask[i]==0) {
332 gl_update_hitflag( ctx, VB->Win.data[i][2] * zs );
333 }
334 }
335 }
336
337
338 static void write_hit_record( GLcontext *ctx )
339 {
340 GLuint i;
341 GLuint zmin, zmax, zscale = (~0u);
342
343 /* HitMinZ and HitMaxZ are in [0,1]. Multiply these values by */
344 /* 2^32-1 and round to nearest unsigned integer. */
345
346 assert( ctx != NULL ); /* this line magically fixes a SunOS 5.x/gcc bug */
347 zmin = (GLuint) ((GLfloat) zscale * ctx->Select.HitMinZ);
348 zmax = (GLuint) ((GLfloat) zscale * ctx->Select.HitMaxZ);
349
350 WRITE_RECORD( ctx, ctx->Select.NameStackDepth );
351 WRITE_RECORD( ctx, zmin );
352 WRITE_RECORD( ctx, zmax );
353 for (i = 0; i < ctx->Select.NameStackDepth; i++) {
354 WRITE_RECORD( ctx, ctx->Select.NameStack[i] );
355 }
356
357 ctx->Select.Hits++;
358 ctx->Select.HitFlag = GL_FALSE;
359 ctx->Select.HitMinZ = 1.0;
360 ctx->Select.HitMaxZ = -1.0;
361 }
362
363
364
365 void
366 _mesa_InitNames( void )
367 {
368 GET_CURRENT_CONTEXT(ctx);
369 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glInitNames");
370 /* Record the hit before the HitFlag is wiped out again. */
371 if (ctx->RenderMode == GL_SELECT) {
372 if (ctx->Select.HitFlag) {
373 write_hit_record( ctx );
374 }
375 }
376 ctx->Select.NameStackDepth = 0;
377 ctx->Select.HitFlag = GL_FALSE;
378 ctx->Select.HitMinZ = 1.0;
379 ctx->Select.HitMaxZ = 0.0;
380 }
381
382
383
384 void
385 _mesa_LoadName( GLuint name )
386 {
387 GET_CURRENT_CONTEXT(ctx);
388 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glLoadName");
389 if (ctx->RenderMode != GL_SELECT) {
390 return;
391 }
392 if (ctx->Select.NameStackDepth == 0) {
393 gl_error( ctx, GL_INVALID_OPERATION, "glLoadName" );
394 return;
395 }
396 if (ctx->Select.HitFlag) {
397 write_hit_record( ctx );
398 }
399 if (ctx->Select.NameStackDepth < MAX_NAME_STACK_DEPTH) {
400 ctx->Select.NameStack[ctx->Select.NameStackDepth-1] = name;
401 }
402 else {
403 ctx->Select.NameStack[MAX_NAME_STACK_DEPTH-1] = name;
404 }
405 }
406
407
408 void
409 _mesa_PushName( GLuint name )
410 {
411 GET_CURRENT_CONTEXT(ctx);
412 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glPushName");
413 if (ctx->RenderMode != GL_SELECT) {
414 return;
415 }
416 if (ctx->Select.HitFlag) {
417 write_hit_record( ctx );
418 }
419 if (ctx->Select.NameStackDepth < MAX_NAME_STACK_DEPTH) {
420 ctx->Select.NameStack[ctx->Select.NameStackDepth++] = name;
421 }
422 else {
423 gl_error( ctx, GL_STACK_OVERFLOW, "glPushName" );
424 }
425 }
426
427
428
429 void
430 _mesa_PopName( void )
431 {
432 GET_CURRENT_CONTEXT(ctx);
433 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glPopName");
434 if (ctx->RenderMode != GL_SELECT) {
435 return;
436 }
437 if (ctx->Select.HitFlag) {
438 write_hit_record( ctx );
439 }
440 if (ctx->Select.NameStackDepth > 0) {
441 ctx->Select.NameStackDepth--;
442 }
443 else {
444 gl_error( ctx, GL_STACK_UNDERFLOW, "glPopName" );
445 }
446 }
447
448
449
450 /**********************************************************************/
451 /* Render Mode */
452 /**********************************************************************/
453
454
455
456 /*
457 * NOTE: this function can't be put in a display list.
458 */
459 GLint
460 _mesa_RenderMode( GLenum mode )
461 {
462 GET_CURRENT_CONTEXT(ctx);
463 GLint result;
464
465 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, "glRenderMode", 0);
466
467 if (MESA_VERBOSE & VERBOSE_API)
468 fprintf(stderr, "glRenderMode %s\n", gl_lookup_enum_by_nr(mode));
469
470 ctx->TriangleCaps &= ~(DD_FEEDBACK|DD_SELECT);
471
472 switch (ctx->RenderMode) {
473 case GL_RENDER:
474 result = 0;
475 break;
476 case GL_SELECT:
477 if (ctx->Select.HitFlag) {
478 write_hit_record( ctx );
479 }
480 if (ctx->Select.BufferCount > ctx->Select.BufferSize) {
481 /* overflow */
482 #ifdef DEBUG
483 gl_warning(ctx, "Feedback buffer overflow");
484 #endif
485 result = -1;
486 }
487 else {
488 result = ctx->Select.Hits;
489 }
490 ctx->Select.BufferCount = 0;
491 ctx->Select.Hits = 0;
492 ctx->Select.NameStackDepth = 0;
493 break;
494 case GL_FEEDBACK:
495 if (ctx->Feedback.Count > ctx->Feedback.BufferSize) {
496 /* overflow */
497 result = -1;
498 }
499 else {
500 result = ctx->Feedback.Count;
501 }
502 ctx->Feedback.Count = 0;
503 break;
504 default:
505 gl_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
506 return 0;
507 }
508
509 switch (mode) {
510 case GL_RENDER:
511 break;
512 case GL_SELECT:
513 ctx->TriangleCaps |= DD_SELECT;
514 if (ctx->Select.BufferSize==0) {
515 /* haven't called glSelectBuffer yet */
516 gl_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
517 }
518 break;
519 case GL_FEEDBACK:
520 ctx->TriangleCaps |= DD_FEEDBACK;
521 if (ctx->Feedback.BufferSize==0) {
522 /* haven't called glFeedbackBuffer yet */
523 gl_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
524 }
525 break;
526 default:
527 gl_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
528 return 0;
529 }
530
531 ctx->RenderMode = mode;
532 ctx->NewState |= NEW_ALL;
533
534 return result;
535 }
536