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