433d0391e880b7bd58c507e98a9381abdc247ba2
[mesa.git] / src / mesa / main / feedback.c
1 /* $Id: feedback.c,v 1.2 1999/09/18 20:41:23 keithw Exp $ */
2
3 /*
4 * Mesa 3-D graphics library
5 * Version: 3.1
6 *
7 * Copyright (C) 1999 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
29
30
31 #ifdef PC_HEADER
32 #include "all.h"
33 #else
34 #include <assert.h>
35 #include <stdio.h>
36 #include "context.h"
37 #include "enums.h"
38 #include "feedback.h"
39 #include "macros.h"
40 #include "mmath.h"
41 #include "types.h"
42 #ifdef XFree86Server
43 #include "GL/xf86glx.h"
44 #endif
45 #endif
46
47
48
49 #define FB_3D 0x01
50 #define FB_4D 0x02
51 #define FB_INDEX 0x04
52 #define FB_COLOR 0x08
53 #define FB_TEXTURE 0X10
54
55
56
57 void
58 gl_FeedbackBuffer( GLcontext *ctx, GLsizei size, GLenum type, GLfloat *buffer )
59 {
60 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH( ctx, "glFeedbackBuffer" );
61
62 if (ctx->RenderMode==GL_FEEDBACK) {
63 gl_error( ctx, GL_INVALID_OPERATION, "glFeedbackBuffer" );
64 return;
65 }
66
67 if (size<0) {
68 gl_error( ctx, GL_INVALID_VALUE, "glFeedbackBuffer(size<0)" );
69 return;
70 }
71 if (!buffer) {
72 gl_error( ctx, GL_INVALID_VALUE, "glFeedbackBuffer(buffer==NULL)" );
73 ctx->Feedback.BufferSize = 0;
74 return;
75 }
76
77 switch (type) {
78 case GL_2D:
79 ctx->Feedback.Mask = 0;
80 ctx->Feedback.Type = type;
81 break;
82 case GL_3D:
83 ctx->Feedback.Mask = FB_3D;
84 ctx->Feedback.Type = type;
85 break;
86 case GL_3D_COLOR:
87 ctx->Feedback.Mask = FB_3D
88 | (ctx->Visual->RGBAflag ? FB_COLOR : FB_INDEX);
89 ctx->Feedback.Type = type;
90 break;
91 case GL_3D_COLOR_TEXTURE:
92 ctx->Feedback.Mask = FB_3D
93 | (ctx->Visual->RGBAflag ? FB_COLOR : FB_INDEX)
94 | FB_TEXTURE;
95 ctx->Feedback.Type = type;
96 break;
97 case GL_4D_COLOR_TEXTURE:
98 ctx->Feedback.Mask = FB_3D | FB_4D
99 | (ctx->Visual->RGBAflag ? FB_COLOR : FB_INDEX)
100 | FB_TEXTURE;
101 ctx->Feedback.Type = type;
102 break;
103 default:
104 ctx->Feedback.Mask = 0;
105 gl_error( ctx, GL_INVALID_ENUM, "glFeedbackBuffer" );
106 }
107
108 ctx->Feedback.BufferSize = size;
109 ctx->Feedback.Buffer = buffer;
110 ctx->Feedback.Count = 0;
111 }
112
113
114
115 void gl_PassThrough( GLcontext *ctx, GLfloat token )
116 {
117 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glPassThrough");
118
119 if (ctx->RenderMode==GL_FEEDBACK) {
120 FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_PASS_THROUGH_TOKEN );
121 FEEDBACK_TOKEN( ctx, token );
122 }
123 }
124
125
126
127 /*
128 * Put a vertex into the feedback buffer.
129 */
130 void gl_feedback_vertex( GLcontext *ctx,
131 const GLfloat win[4],
132 const GLfloat color[4],
133 GLuint index,
134 const GLfloat texcoord[4] )
135 {
136 FEEDBACK_TOKEN( ctx, win[0] );
137 FEEDBACK_TOKEN( ctx, win[1] );
138 if (ctx->Feedback.Mask & FB_3D) {
139 FEEDBACK_TOKEN( ctx, win[2] );
140 }
141 if (ctx->Feedback.Mask & FB_4D) {
142 FEEDBACK_TOKEN( ctx, win[3] );
143 }
144 if (ctx->Feedback.Mask & FB_INDEX) {
145 FEEDBACK_TOKEN( ctx, (GLfloat) index );
146 }
147 if (ctx->Feedback.Mask & FB_COLOR) {
148 FEEDBACK_TOKEN( ctx, color[0] );
149 FEEDBACK_TOKEN( ctx, color[1] );
150 FEEDBACK_TOKEN( ctx, color[2] );
151 FEEDBACK_TOKEN( ctx, color[3] );
152 }
153 if (ctx->Feedback.Mask & FB_TEXTURE) {
154 FEEDBACK_TOKEN( ctx, texcoord[0] );
155 FEEDBACK_TOKEN( ctx, texcoord[1] );
156 FEEDBACK_TOKEN( ctx, texcoord[2] );
157 FEEDBACK_TOKEN( ctx, texcoord[3] );
158 }
159 }
160
161
162
163 static void gl_do_feedback_vertex( GLcontext *ctx, GLuint v, GLuint pv )
164 {
165 GLfloat win[4];
166 GLfloat color[4];
167 GLfloat tc[4];
168 GLuint texUnit = ctx->Texture.CurrentTransformUnit;
169 struct vertex_buffer *VB = ctx->VB;
170
171 win[0] = VB->Win.data[v][0];
172 win[1] = VB->Win.data[v][1];
173 win[2] = VB->Win.data[v][2] / DEPTH_SCALE;
174 win[3] = 1.0 / VB->Win.data[v][3];
175
176 if (ctx->Light.ShadeModel==GL_SMOOTH) 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 {
183 GLfloat invq = 1.0F / VB->TexCoordPtr[texUnit]->data[v][3];
184 tc[0] = VB->TexCoordPtr[texUnit]->data[v][0] * invq;
185 tc[1] = VB->TexCoordPtr[texUnit]->data[v][1] * invq;
186 tc[2] = VB->TexCoordPtr[texUnit]->data[v][2] * invq;
187 tc[3] = VB->TexCoordPtr[texUnit]->data[v][3];
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 gl_feedback_vertex( ctx, win, color, VB->IndexPtr->data[v], tc );
196 }
197
198
199
200 /*
201 * Put triangle in feedback buffer.
202 */
203 void gl_feedback_triangle( GLcontext *ctx,
204 GLuint v0, GLuint v1, GLuint v2, GLuint pv )
205 {
206 if (gl_cull_triangle( ctx, v0, v1, v2 )) {
207 FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_POLYGON_TOKEN );
208 FEEDBACK_TOKEN( ctx, (GLfloat) 3 ); /* three vertices */
209
210 gl_do_feedback_vertex( ctx, v0, pv );
211 gl_do_feedback_vertex( ctx, v1, pv );
212 gl_do_feedback_vertex( ctx, v2, pv );
213 }
214 }
215
216
217 void gl_feedback_line( GLcontext *ctx, GLuint v1, GLuint v2, GLuint pv )
218 {
219 GLenum token = GL_LINE_TOKEN;
220
221 if (ctx->StippleCounter==0)
222 token = GL_LINE_RESET_TOKEN;
223
224 FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) token );
225
226 gl_do_feedback_vertex( ctx, v1, pv );
227 gl_do_feedback_vertex( ctx, v2, pv );
228
229 ctx->StippleCounter++;
230 }
231
232
233 void gl_feedback_points( GLcontext *ctx, GLuint first, GLuint last )
234 {
235 struct vertex_buffer *VB = ctx->VB;
236 GLuint i;
237
238 for (i=first;i<=last;i++)
239 if (VB->ClipMask[i]==0) {
240 FEEDBACK_TOKEN( ctx, (GLfloat) (GLint) GL_POINT_TOKEN );
241 gl_do_feedback_vertex( ctx, i, i );
242 }
243 }
244
245
246
247
248
249 /**********************************************************************/
250 /* Selection */
251 /**********************************************************************/
252
253
254 /*
255 * NOTE: this function can't be put in a display list.
256 */
257 void gl_SelectBuffer( GLcontext *ctx, GLsizei size, GLuint *buffer )
258 {
259 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glSelectBuffer");
260 if (ctx->RenderMode==GL_SELECT) {
261 gl_error( ctx, GL_INVALID_OPERATION, "glSelectBuffer" );
262 }
263 ctx->Select.Buffer = buffer;
264 ctx->Select.BufferSize = size;
265 ctx->Select.BufferCount = 0;
266
267 ctx->Select.HitFlag = GL_FALSE;
268 ctx->Select.HitMinZ = 1.0;
269 ctx->Select.HitMaxZ = 0.0;
270 }
271
272
273 #define WRITE_RECORD( CTX, V ) \
274 if (CTX->Select.BufferCount < CTX->Select.BufferSize) { \
275 CTX->Select.Buffer[CTX->Select.BufferCount] = (V); \
276 } \
277 CTX->Select.BufferCount++;
278
279
280
281 void gl_update_hitflag( GLcontext *ctx, GLfloat z )
282 {
283 ctx->Select.HitFlag = GL_TRUE;
284 if (z < ctx->Select.HitMinZ) {
285 ctx->Select.HitMinZ = z;
286 }
287 if (z > ctx->Select.HitMaxZ) {
288 ctx->Select.HitMaxZ = z;
289 }
290 }
291
292 void gl_select_triangle( GLcontext *ctx,
293 GLuint v0, GLuint v1, GLuint v2, GLuint pv )
294 {
295 struct vertex_buffer *VB = ctx->VB;
296
297 if (gl_cull_triangle( ctx, v0, v1, v2 )) {
298 gl_update_hitflag( ctx, VB->Win.data[v0][3] / DEPTH_SCALE );
299 gl_update_hitflag( ctx, VB->Win.data[v1][3] / DEPTH_SCALE );
300 gl_update_hitflag( ctx, VB->Win.data[v2][3] / DEPTH_SCALE );
301 }
302 }
303
304
305 void gl_select_line( GLcontext *ctx,
306 GLuint v0, GLuint v1, GLuint pv )
307 {
308 struct vertex_buffer *VB = ctx->VB;
309
310 gl_update_hitflag( ctx, VB->Win.data[v0][3] / DEPTH_SCALE );
311 gl_update_hitflag( ctx, VB->Win.data[v1][3] / DEPTH_SCALE );
312 }
313
314 void gl_select_points( GLcontext *ctx, GLuint first, GLuint last )
315 {
316 struct vertex_buffer *VB = ctx->VB;
317 GLuint i;
318
319 for (i=first;i<=last;i++)
320 if (VB->ClipMask[i]==0)
321 gl_update_hitflag( ctx, VB->Win.data[i][3] / DEPTH_SCALE);
322 }
323
324
325 static void write_hit_record( GLcontext *ctx )
326 {
327 GLuint i;
328 GLuint zmin, zmax, zscale = (~0u);
329
330 /* HitMinZ and HitMaxZ are in [0,1]. Multiply these values by */
331 /* 2^32-1 and round to nearest unsigned integer. */
332
333 assert( ctx != NULL ); /* this line magically fixes a SunOS 5.x/gcc bug */
334 zmin = (GLuint) ((GLfloat) zscale * ctx->Select.HitMinZ);
335 zmax = (GLuint) ((GLfloat) zscale * ctx->Select.HitMaxZ);
336
337 WRITE_RECORD( ctx, ctx->Select.NameStackDepth );
338 WRITE_RECORD( ctx, zmin );
339 WRITE_RECORD( ctx, zmax );
340 for (i=0;i<ctx->Select.NameStackDepth;i++) {
341 WRITE_RECORD( ctx, ctx->Select.NameStack[i] );
342 }
343
344 ctx->Select.Hits++;
345 ctx->Select.HitFlag = GL_FALSE;
346 ctx->Select.HitMinZ = 1.0;
347 ctx->Select.HitMaxZ = -1.0;
348 }
349
350
351
352 void gl_InitNames( GLcontext *ctx )
353 {
354 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glInitNames");
355 /* Record the hit before the HitFlag is wiped out again. */
356 if (ctx->RenderMode==GL_SELECT) {
357 if (ctx->Select.HitFlag) {
358 write_hit_record( ctx );
359 }
360 }
361 ctx->Select.NameStackDepth = 0;
362 ctx->Select.HitFlag = GL_FALSE;
363 ctx->Select.HitMinZ = 1.0;
364 ctx->Select.HitMaxZ = 0.0;
365 }
366
367
368
369 void gl_LoadName( GLcontext *ctx, GLuint name )
370 {
371 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glLoadName");
372 if (ctx->RenderMode!=GL_SELECT) {
373 return;
374 }
375 if (ctx->Select.NameStackDepth==0) {
376 gl_error( ctx, GL_INVALID_OPERATION, "glLoadName" );
377 return;
378 }
379 if (ctx->Select.HitFlag) {
380 write_hit_record( ctx );
381 }
382 if (ctx->Select.NameStackDepth<MAX_NAME_STACK_DEPTH) {
383 ctx->Select.NameStack[ctx->Select.NameStackDepth-1] = name;
384 }
385 else {
386 ctx->Select.NameStack[MAX_NAME_STACK_DEPTH-1] = name;
387 }
388 }
389
390
391 void gl_PushName( GLcontext *ctx, GLuint name )
392 {
393 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glPushName");
394 if (ctx->RenderMode!=GL_SELECT) {
395 return;
396 }
397 if (ctx->Select.HitFlag) {
398 write_hit_record( ctx );
399 }
400 if (ctx->Select.NameStackDepth<MAX_NAME_STACK_DEPTH) {
401 ctx->Select.NameStack[ctx->Select.NameStackDepth++] = name;
402 }
403 else {
404 gl_error( ctx, GL_STACK_OVERFLOW, "glPushName" );
405 }
406 }
407
408
409
410 void gl_PopName( GLcontext *ctx )
411 {
412 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx, "glPopName");
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>0) {
420 ctx->Select.NameStackDepth--;
421 }
422 else {
423 gl_error( ctx, GL_STACK_UNDERFLOW, "glPopName" );
424 }
425 }
426
427
428
429 /**********************************************************************/
430 /* Render Mode */
431 /**********************************************************************/
432
433
434
435 /*
436 * NOTE: this function can't be put in a display list.
437 */
438 GLint gl_RenderMode( GLcontext *ctx, GLenum mode )
439 {
440 GLint result;
441
442 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx, "glRenderMode", 0);
443
444 if (MESA_VERBOSE & VERBOSE_API)
445 fprintf(stderr, "glRenderMode %s\n", gl_lookup_enum_by_nr(mode));
446
447 ctx->TriangleCaps &= ~(DD_FEEDBACK|DD_SELECT);
448
449 switch (ctx->RenderMode) {
450 case GL_RENDER:
451 result = 0;
452 break;
453 case GL_SELECT:
454 if (ctx->Select.HitFlag) {
455 write_hit_record( ctx );
456 }
457 if (ctx->Select.BufferCount > ctx->Select.BufferSize) {
458 /* overflow */
459 #ifdef DEBUG
460 gl_warning(ctx, "Feedback buffer overflow");
461 #endif
462 result = -1;
463 }
464 else {
465 result = ctx->Select.Hits;
466 }
467 ctx->Select.BufferCount = 0;
468 ctx->Select.Hits = 0;
469 ctx->Select.NameStackDepth = 0;
470 break;
471 case GL_FEEDBACK:
472 if (ctx->Feedback.Count > ctx->Feedback.BufferSize) {
473 /* overflow */
474 result = -1;
475 }
476 else {
477 result = ctx->Feedback.Count;
478 }
479 ctx->Feedback.Count = 0;
480 break;
481 default:
482 gl_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
483 return 0;
484 }
485
486 switch (mode) {
487 case GL_RENDER:
488 break;
489 case GL_SELECT:
490 ctx->TriangleCaps |= DD_SELECT;
491 if (ctx->Select.BufferSize==0) {
492 /* haven't called glSelectBuffer yet */
493 gl_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
494 }
495 break;
496 case GL_FEEDBACK:
497 ctx->TriangleCaps |= DD_FEEDBACK;
498 if (ctx->Feedback.BufferSize==0) {
499 /* haven't called glFeedbackBuffer yet */
500 gl_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
501 }
502 break;
503 default:
504 gl_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
505 return 0;
506 }
507
508
509 ctx->RenderMode = mode;
510 ctx->NewState |= NEW_ALL;
511
512 return result;
513 }
514