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