1 /* $Id: feedback.c,v 1.14 2000/10/30 13:32:00 keithw Exp $ */
4 * Mesa 3-D graphics library
7 * Copyright (C) 1999-2000 Brian Paul All Rights Reserved.
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:
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
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.
48 #define FB_TEXTURE 0X10
53 _mesa_FeedbackBuffer( GLsizei size
, GLenum type
, GLfloat
*buffer
)
55 GET_CURRENT_CONTEXT(ctx
);
56 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH( ctx
, "glFeedbackBuffer" );
58 if (ctx
->RenderMode
==GL_FEEDBACK
) {
59 gl_error( ctx
, GL_INVALID_OPERATION
, "glFeedbackBuffer" );
64 gl_error( ctx
, GL_INVALID_VALUE
, "glFeedbackBuffer(size<0)" );
68 gl_error( ctx
, GL_INVALID_VALUE
, "glFeedbackBuffer(buffer==NULL)" );
69 ctx
->Feedback
.BufferSize
= 0;
75 ctx
->Feedback
.Mask
= 0;
76 ctx
->Feedback
.Type
= type
;
79 ctx
->Feedback
.Mask
= FB_3D
;
80 ctx
->Feedback
.Type
= type
;
83 ctx
->Feedback
.Mask
= FB_3D
84 | (ctx
->Visual
.RGBAflag
? FB_COLOR
: FB_INDEX
);
85 ctx
->Feedback
.Type
= type
;
87 case GL_3D_COLOR_TEXTURE
:
88 ctx
->Feedback
.Mask
= FB_3D
89 | (ctx
->Visual
.RGBAflag
? FB_COLOR
: FB_INDEX
)
91 ctx
->Feedback
.Type
= type
;
93 case GL_4D_COLOR_TEXTURE
:
94 ctx
->Feedback
.Mask
= FB_3D
| FB_4D
95 | (ctx
->Visual
.RGBAflag
? FB_COLOR
: FB_INDEX
)
97 ctx
->Feedback
.Type
= type
;
100 ctx
->Feedback
.Mask
= 0;
101 gl_error( ctx
, GL_INVALID_ENUM
, "glFeedbackBuffer" );
104 ctx
->Feedback
.BufferSize
= size
;
105 ctx
->Feedback
.Buffer
= buffer
;
106 ctx
->Feedback
.Count
= 0;
107 ctx
->NewState
|= _NEW_FEEDBACK_SELECT
;
113 _mesa_PassThrough( GLfloat token
)
115 GET_CURRENT_CONTEXT(ctx
);
116 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glPassThrough");
118 if (ctx
->RenderMode
==GL_FEEDBACK
) {
119 FEEDBACK_TOKEN( ctx
, (GLfloat
) (GLint
) GL_PASS_THROUGH_TOKEN
);
120 FEEDBACK_TOKEN( ctx
, token
);
127 * Put a vertex into the feedback buffer.
129 void gl_feedback_vertex( GLcontext
*ctx
,
130 const GLfloat win
[4],
131 const GLfloat color
[4],
133 const GLfloat texcoord
[4] )
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] );
140 if (ctx
->Feedback
.Mask
& FB_4D
) {
141 FEEDBACK_TOKEN( ctx
, win
[3] );
143 if (ctx
->Feedback
.Mask
& FB_INDEX
) {
144 FEEDBACK_TOKEN( ctx
, (GLfloat
) index
);
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] );
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] );
162 static void feedback_vertex( GLcontext
*ctx
, GLuint v
, GLuint pv
)
167 GLuint texUnit
= ctx
->Texture
.CurrentTransformUnit
;
168 const struct vertex_buffer
*VB
= ctx
->VB
;
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];
176 if (ctx
->Light
.ShadeModel
== GL_SMOOTH
)
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]);
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];
193 ASSIGN_4V(tc
, 0,0,0,1);
195 VB
->TexCoordPtr
[texUnit
]->size
,
196 VB
->TexCoordPtr
[texUnit
]->data
[v
]);
200 index
= VB
->IndexPtr
->data
[v
];
204 gl_feedback_vertex( ctx
, win
, color
, index
, tc
);
210 * Put triangle in feedback buffer.
212 void gl_feedback_triangle( GLcontext
*ctx
,
213 GLuint v0
, GLuint v1
, GLuint v2
, GLuint pv
)
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 */
219 feedback_vertex( ctx
, v0
, pv
);
220 feedback_vertex( ctx
, v1
, pv
);
221 feedback_vertex( ctx
, v2
, pv
);
226 void gl_feedback_line( GLcontext
*ctx
, GLuint v1
, GLuint v2
, GLuint pv
)
228 GLenum token
= GL_LINE_TOKEN
;
230 if (ctx
->StippleCounter
==0)
231 token
= GL_LINE_RESET_TOKEN
;
233 FEEDBACK_TOKEN( ctx
, (GLfloat
) (GLint
) token
);
235 feedback_vertex( ctx
, v1
, pv
);
236 feedback_vertex( ctx
, v2
, pv
);
238 ctx
->StippleCounter
++;
242 void gl_feedback_points( GLcontext
*ctx
, GLuint first
, GLuint last
)
244 const struct vertex_buffer
*VB
= ctx
->VB
;
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
);
259 /**********************************************************************/
261 /**********************************************************************/
265 * NOTE: this function can't be put in a display list.
268 _mesa_SelectBuffer( GLsizei size
, GLuint
*buffer
)
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" );
275 ctx
->Select
.Buffer
= buffer
;
276 ctx
->Select
.BufferSize
= size
;
277 ctx
->Select
.BufferCount
= 0;
279 ctx
->Select
.HitFlag
= GL_FALSE
;
280 ctx
->Select
.HitMinZ
= 1.0;
281 ctx
->Select
.HitMaxZ
= 0.0;
283 ctx
->NewState
|= _NEW_FEEDBACK_SELECT
;
287 #define WRITE_RECORD( CTX, V ) \
288 if (CTX->Select.BufferCount < CTX->Select.BufferSize) { \
289 CTX->Select.Buffer[CTX->Select.BufferCount] = (V); \
291 CTX->Select.BufferCount++;
295 void gl_update_hitflag( GLcontext
*ctx
, GLfloat z
)
297 ctx
->Select
.HitFlag
= GL_TRUE
;
298 if (z
< ctx
->Select
.HitMinZ
) {
299 ctx
->Select
.HitMinZ
= z
;
301 if (z
> ctx
->Select
.HitMaxZ
) {
302 ctx
->Select
.HitMaxZ
= z
;
306 void gl_select_triangle( GLcontext
*ctx
,
307 GLuint v0
, GLuint v1
, GLuint v2
, GLuint pv
)
309 const struct vertex_buffer
*VB
= ctx
->VB
;
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
);
320 void gl_select_line( GLcontext
*ctx
,
321 GLuint v0
, GLuint v1
, GLuint pv
)
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
);
330 void gl_select_points( GLcontext
*ctx
, GLuint first
, GLuint last
)
332 struct vertex_buffer
*VB
= ctx
->VB
;
333 const GLfloat zs
= 1.0F
/ ctx
->Visual
.DepthMaxF
;
336 for (i
=first
;i
<=last
;i
++) {
337 if (VB
->ClipMask
[i
]==0) {
338 gl_update_hitflag( ctx
, VB
->Win
.data
[i
][2] * zs
);
344 static void write_hit_record( GLcontext
*ctx
)
347 GLuint zmin
, zmax
, zscale
= (~0u);
349 /* HitMinZ and HitMaxZ are in [0,1]. Multiply these values by */
350 /* 2^32-1 and round to nearest unsigned integer. */
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
);
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
] );
364 ctx
->Select
.HitFlag
= GL_FALSE
;
365 ctx
->Select
.HitMinZ
= 1.0;
366 ctx
->Select
.HitMaxZ
= -1.0;
372 _mesa_InitNames( void )
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
);
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
;
392 _mesa_LoadName( GLuint name
)
394 GET_CURRENT_CONTEXT(ctx
);
395 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glLoadName");
396 if (ctx
->RenderMode
!= GL_SELECT
) {
399 if (ctx
->Select
.NameStackDepth
== 0) {
400 gl_error( ctx
, GL_INVALID_OPERATION
, "glLoadName" );
403 if (ctx
->Select
.HitFlag
) {
404 write_hit_record( ctx
);
406 if (ctx
->Select
.NameStackDepth
< MAX_NAME_STACK_DEPTH
) {
407 ctx
->Select
.NameStack
[ctx
->Select
.NameStackDepth
-1] = name
;
410 ctx
->Select
.NameStack
[MAX_NAME_STACK_DEPTH
-1] = name
;
412 ctx
->NewState
|= _NEW_FEEDBACK_SELECT
;
417 _mesa_PushName( GLuint name
)
419 GET_CURRENT_CONTEXT(ctx
);
420 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glPushName");
421 if (ctx
->RenderMode
!= GL_SELECT
) {
424 if (ctx
->Select
.HitFlag
) {
425 write_hit_record( ctx
);
427 if (ctx
->Select
.NameStackDepth
< MAX_NAME_STACK_DEPTH
) {
428 ctx
->Select
.NameStack
[ctx
->Select
.NameStackDepth
++] = name
;
431 gl_error( ctx
, GL_STACK_OVERFLOW
, "glPushName" );
433 ctx
->NewState
|= _NEW_FEEDBACK_SELECT
;
439 _mesa_PopName( void )
441 GET_CURRENT_CONTEXT(ctx
);
442 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
, "glPopName");
443 if (ctx
->RenderMode
!= GL_SELECT
) {
446 if (ctx
->Select
.HitFlag
) {
447 write_hit_record( ctx
);
449 if (ctx
->Select
.NameStackDepth
> 0) {
450 ctx
->Select
.NameStackDepth
--;
453 gl_error( ctx
, GL_STACK_UNDERFLOW
, "glPopName" );
455 ctx
->NewState
|= _NEW_FEEDBACK_SELECT
;
460 /**********************************************************************/
462 /**********************************************************************/
467 * NOTE: this function can't be put in a display list.
470 _mesa_RenderMode( GLenum mode
)
472 GET_CURRENT_CONTEXT(ctx
);
475 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH_WITH_RETVAL(ctx
, "glRenderMode", 0);
477 if (MESA_VERBOSE
& VERBOSE_API
)
478 fprintf(stderr
, "glRenderMode %s\n", gl_lookup_enum_by_nr(mode
));
480 ctx
->TriangleCaps
&= ~(DD_FEEDBACK
|DD_SELECT
);
482 switch (ctx
->RenderMode
) {
487 if (ctx
->Select
.HitFlag
) {
488 write_hit_record( ctx
);
490 if (ctx
->Select
.BufferCount
> ctx
->Select
.BufferSize
) {
493 _mesa_warning(ctx
, "Feedback buffer overflow");
498 result
= ctx
->Select
.Hits
;
500 ctx
->Select
.BufferCount
= 0;
501 ctx
->Select
.Hits
= 0;
502 ctx
->Select
.NameStackDepth
= 0;
503 ctx
->NewState
|= _NEW_FEEDBACK_SELECT
;
506 if (ctx
->Feedback
.Count
> ctx
->Feedback
.BufferSize
) {
511 result
= ctx
->Feedback
.Count
;
513 ctx
->Feedback
.Count
= 0;
514 ctx
->NewState
|= _NEW_FEEDBACK_SELECT
;
517 gl_error( ctx
, GL_INVALID_ENUM
, "glRenderMode" );
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" );
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" );
539 gl_error( ctx
, GL_INVALID_ENUM
, "glRenderMode" );
543 ctx
->RenderMode
= mode
;
544 ctx
->NewState
|= _NEW_RENDERMODE
;