1/*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
5 * Copyright (C) 2009  VMware, Inc.  All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26/**
27 * \file feedback.c
28 * Selection and feedback modes functions.
29 */
30
31
32#include "glheader.h"
33#include "context.h"
34#include "enums.h"
35#include "feedback.h"
36#include "macros.h"
37#include "mtypes.h"
38
39
40#define FB_3D		0x01
41#define FB_4D		0x02
42#define FB_COLOR	0x04
43#define FB_TEXTURE	0X08
44
45
46
47void GLAPIENTRY
48_mesa_FeedbackBuffer( GLsizei size, GLenum type, GLfloat *buffer )
49{
50   GET_CURRENT_CONTEXT(ctx);
51
52   if (ctx->RenderMode==GL_FEEDBACK) {
53      _mesa_error( ctx, GL_INVALID_OPERATION, "glFeedbackBuffer" );
54      return;
55   }
56   if (size<0) {
57      _mesa_error( ctx, GL_INVALID_VALUE, "glFeedbackBuffer(size<0)" );
58      return;
59   }
60   if (!buffer && size > 0) {
61      _mesa_error( ctx, GL_INVALID_VALUE, "glFeedbackBuffer(buffer==NULL)" );
62      ctx->Feedback.BufferSize = 0;
63      return;
64   }
65
66   switch (type) {
67      case GL_2D:
68	 ctx->Feedback._Mask = 0;
69	 break;
70      case GL_3D:
71	 ctx->Feedback._Mask = FB_3D;
72	 break;
73      case GL_3D_COLOR:
74	 ctx->Feedback._Mask = (FB_3D | FB_COLOR);
75	 break;
76      case GL_3D_COLOR_TEXTURE:
77	 ctx->Feedback._Mask = (FB_3D | FB_COLOR | FB_TEXTURE);
78	 break;
79      case GL_4D_COLOR_TEXTURE:
80	 ctx->Feedback._Mask = (FB_3D | FB_4D | FB_COLOR | FB_TEXTURE);
81	 break;
82      default:
83         _mesa_error( ctx, GL_INVALID_ENUM, "glFeedbackBuffer" );
84	 return;
85   }
86
87   FLUSH_VERTICES(ctx, _NEW_RENDERMODE, 0); /* Always flush */
88   ctx->Feedback.Type = type;
89   ctx->Feedback.BufferSize = size;
90   ctx->Feedback.Buffer = buffer;
91   ctx->Feedback.Count = 0;	              /* Because of this. */
92}
93
94
95void GLAPIENTRY
96_mesa_PassThrough( GLfloat token )
97{
98   GET_CURRENT_CONTEXT(ctx);
99
100   if (ctx->RenderMode==GL_FEEDBACK) {
101      FLUSH_VERTICES(ctx, 0, 0);
102      _mesa_feedback_token( ctx, (GLfloat) (GLint) GL_PASS_THROUGH_TOKEN );
103      _mesa_feedback_token( ctx, token );
104   }
105}
106
107
108/**
109 * Put a vertex into the feedback buffer.
110 */
111void
112_mesa_feedback_vertex(struct gl_context *ctx,
113                      const GLfloat win[4],
114                      const GLfloat color[4],
115                      const GLfloat texcoord[4])
116{
117   _mesa_feedback_token( ctx, win[0] );
118   _mesa_feedback_token( ctx, win[1] );
119   if (ctx->Feedback._Mask & FB_3D) {
120      _mesa_feedback_token( ctx, win[2] );
121   }
122   if (ctx->Feedback._Mask & FB_4D) {
123      _mesa_feedback_token( ctx, win[3] );
124   }
125   if (ctx->Feedback._Mask & FB_COLOR) {
126      _mesa_feedback_token( ctx, color[0] );
127      _mesa_feedback_token( ctx, color[1] );
128      _mesa_feedback_token( ctx, color[2] );
129      _mesa_feedback_token( ctx, color[3] );
130   }
131   if (ctx->Feedback._Mask & FB_TEXTURE) {
132      _mesa_feedback_token( ctx, texcoord[0] );
133      _mesa_feedback_token( ctx, texcoord[1] );
134      _mesa_feedback_token( ctx, texcoord[2] );
135      _mesa_feedback_token( ctx, texcoord[3] );
136   }
137}
138
139
140/**********************************************************************/
141/** \name Selection */
142/*@{*/
143
144/**
145 * Establish a buffer for selection mode values.
146 *
147 * \param size buffer size.
148 * \param buffer buffer.
149 *
150 * \sa glSelectBuffer().
151 *
152 * \note this function can't be put in a display list.
153 *
154 * Verifies we're not in selection mode, flushes the vertices and initialize
155 * the fields in __struct gl_contextRec::Select with the given buffer.
156 */
157void GLAPIENTRY
158_mesa_SelectBuffer( GLsizei size, GLuint *buffer )
159{
160   GET_CURRENT_CONTEXT(ctx);
161
162   if (size < 0) {
163      _mesa_error(ctx, GL_INVALID_VALUE, "glSelectBuffer(size)");
164      return;
165   }
166
167   if (ctx->RenderMode==GL_SELECT) {
168      _mesa_error( ctx, GL_INVALID_OPERATION, "glSelectBuffer" );
169      return;			/* KW: added return */
170   }
171
172   FLUSH_VERTICES(ctx, _NEW_RENDERMODE, 0);
173   ctx->Select.Buffer = buffer;
174   ctx->Select.BufferSize = size;
175   ctx->Select.BufferCount = 0;
176   ctx->Select.HitFlag = GL_FALSE;
177   ctx->Select.HitMinZ = 1.0;
178   ctx->Select.HitMaxZ = 0.0;
179}
180
181
182/**
183 * Write a value of a record into the selection buffer.
184 *
185 * \param ctx GL context.
186 * \param value value.
187 *
188 * Verifies there is free space in the buffer to write the value and
189 * increments the pointer.
190 */
191static inline void
192write_record(struct gl_context *ctx, GLuint value)
193{
194   if (ctx->Select.BufferCount < ctx->Select.BufferSize) {
195      ctx->Select.Buffer[ctx->Select.BufferCount] = value;
196   }
197   ctx->Select.BufferCount++;
198}
199
200
201/**
202 * Update the hit flag and the maximum and minimum depth values.
203 *
204 * \param ctx GL context.
205 * \param z depth.
206 *
207 * Sets gl_selection::HitFlag and updates gl_selection::HitMinZ and
208 * gl_selection::HitMaxZ.
209 */
210void
211_mesa_update_hitflag(struct gl_context *ctx, GLfloat z)
212{
213   ctx->Select.HitFlag = GL_TRUE;
214   if (z < ctx->Select.HitMinZ) {
215      ctx->Select.HitMinZ = z;
216   }
217   if (z > ctx->Select.HitMaxZ) {
218      ctx->Select.HitMaxZ = z;
219   }
220}
221
222
223/**
224 * Write the hit record.
225 *
226 * \param ctx GL context.
227 *
228 * Write the hit record, i.e., the number of names in the stack, the minimum and
229 * maximum depth values and the number of names in the name stack at the time
230 * of the event. Resets the hit flag.
231 *
232 * \sa gl_selection.
233 */
234static void
235write_hit_record(struct gl_context *ctx)
236{
237   GLuint i;
238   GLuint zmin, zmax, zscale = (~0u);
239
240   /* HitMinZ and HitMaxZ are in [0,1].  Multiply these values by */
241   /* 2^32-1 and round to nearest unsigned integer. */
242
243   assert( ctx != NULL ); /* this line magically fixes a SunOS 5.x/gcc bug */
244   zmin = (GLuint) ((GLfloat) zscale * ctx->Select.HitMinZ);
245   zmax = (GLuint) ((GLfloat) zscale * ctx->Select.HitMaxZ);
246
247   write_record( ctx, ctx->Select.NameStackDepth );
248   write_record( ctx, zmin );
249   write_record( ctx, zmax );
250   for (i = 0; i < ctx->Select.NameStackDepth; i++) {
251      write_record( ctx, ctx->Select.NameStack[i] );
252   }
253
254   ctx->Select.Hits++;
255   ctx->Select.HitFlag = GL_FALSE;
256   ctx->Select.HitMinZ = 1.0;
257   ctx->Select.HitMaxZ = -1.0;
258}
259
260
261/**
262 * Initialize the name stack.
263 *
264 * Verifies we are in select mode and resets the name stack depth and resets
265 * the hit record data in gl_selection. Marks new render mode in
266 * __struct gl_contextRec::NewState.
267 */
268void GLAPIENTRY
269_mesa_InitNames( void )
270{
271   GET_CURRENT_CONTEXT(ctx);
272   FLUSH_VERTICES(ctx, 0, 0);
273
274   /* Record the hit before the HitFlag is wiped out again. */
275   if (ctx->RenderMode == GL_SELECT) {
276      if (ctx->Select.HitFlag) {
277         write_hit_record( ctx );
278      }
279   }
280   ctx->Select.NameStackDepth = 0;
281   ctx->Select.HitFlag = GL_FALSE;
282   ctx->Select.HitMinZ = 1.0;
283   ctx->Select.HitMaxZ = 0.0;
284   ctx->NewState |= _NEW_RENDERMODE;
285}
286
287
288/**
289 * Load the top-most name of the name stack.
290 *
291 * \param name name.
292 *
293 * Verifies we are in selection mode and that the name stack is not empty.
294 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
295 * and replace the top-most name in the stack.
296 *
297 * sa __struct gl_contextRec::Select.
298 */
299void GLAPIENTRY
300_mesa_LoadName( GLuint name )
301{
302   GET_CURRENT_CONTEXT(ctx);
303
304   if (ctx->RenderMode != GL_SELECT) {
305      return;
306   }
307   if (ctx->Select.NameStackDepth == 0) {
308      _mesa_error( ctx, GL_INVALID_OPERATION, "glLoadName" );
309      return;
310   }
311
312   FLUSH_VERTICES(ctx, _NEW_RENDERMODE, 0);
313
314   if (ctx->Select.HitFlag) {
315      write_hit_record( ctx );
316   }
317   if (ctx->Select.NameStackDepth < MAX_NAME_STACK_DEPTH) {
318      ctx->Select.NameStack[ctx->Select.NameStackDepth-1] = name;
319   }
320   else {
321      ctx->Select.NameStack[MAX_NAME_STACK_DEPTH-1] = name;
322   }
323}
324
325
326/**
327 * Push a name into the name stack.
328 *
329 * \param name name.
330 *
331 * Verifies we are in selection mode and that the name stack is not full.
332 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
333 * and adds the name to the top of the name stack.
334 *
335 * sa __struct gl_contextRec::Select.
336 */
337void GLAPIENTRY
338_mesa_PushName( GLuint name )
339{
340   GET_CURRENT_CONTEXT(ctx);
341
342   if (ctx->RenderMode != GL_SELECT) {
343      return;
344   }
345
346   FLUSH_VERTICES(ctx, _NEW_RENDERMODE, 0);
347   if (ctx->Select.HitFlag) {
348      write_hit_record( ctx );
349   }
350   if (ctx->Select.NameStackDepth >= MAX_NAME_STACK_DEPTH) {
351      _mesa_error( ctx, GL_STACK_OVERFLOW, "glPushName" );
352   }
353   else
354      ctx->Select.NameStack[ctx->Select.NameStackDepth++] = name;
355}
356
357
358/**
359 * Pop a name into the name stack.
360 *
361 * Verifies we are in selection mode and that the name stack is not empty.
362 * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
363 * and removes top-most name in the name stack.
364 *
365 * sa __struct gl_contextRec::Select.
366 */
367void GLAPIENTRY
368_mesa_PopName( void )
369{
370   GET_CURRENT_CONTEXT(ctx);
371
372   if (ctx->RenderMode != GL_SELECT) {
373      return;
374   }
375
376   FLUSH_VERTICES(ctx, _NEW_RENDERMODE, 0);
377   if (ctx->Select.HitFlag) {
378      write_hit_record( ctx );
379   }
380   if (ctx->Select.NameStackDepth == 0) {
381      _mesa_error( ctx, GL_STACK_UNDERFLOW, "glPopName" );
382   }
383   else
384      ctx->Select.NameStackDepth--;
385}
386
387/*@}*/
388
389
390/**********************************************************************/
391/** \name Render Mode */
392/*@{*/
393
394/**
395 * Set rasterization mode.
396 *
397 * \param mode rasterization mode.
398 *
399 * \note this function can't be put in a display list.
400 *
401 * \sa glRenderMode().
402 *
403 * Flushes the vertices and do the necessary cleanup according to the previous
404 * rasterization mode, such as writing the hit record or resent the select
405 * buffer index when exiting the select mode. Updates
406 * __struct gl_contextRec::RenderMode and notifies the driver via the
407 * dd_function_table::RenderMode callback.
408 */
409GLint GLAPIENTRY
410_mesa_RenderMode( GLenum mode )
411{
412   GET_CURRENT_CONTEXT(ctx);
413   GLint result;
414   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
415
416   if (MESA_VERBOSE & VERBOSE_API)
417      _mesa_debug(ctx, "glRenderMode %s\n", _mesa_enum_to_string(mode));
418
419   FLUSH_VERTICES(ctx, _NEW_RENDERMODE | _NEW_FF_VERT_PROGRAM |
420                  _NEW_FF_FRAG_PROGRAM, 0);
421
422   switch (ctx->RenderMode) {
423      case GL_RENDER:
424	 result = 0;
425	 break;
426      case GL_SELECT:
427	 if (ctx->Select.HitFlag) {
428	    write_hit_record( ctx );
429	 }
430	 if (ctx->Select.BufferCount > ctx->Select.BufferSize) {
431	    /* overflow */
432#ifndef NDEBUG
433            _mesa_warning(ctx, "Feedback buffer overflow");
434#endif
435	    result = -1;
436	 }
437	 else {
438	    result = ctx->Select.Hits;
439	 }
440	 ctx->Select.BufferCount = 0;
441	 ctx->Select.Hits = 0;
442	 ctx->Select.NameStackDepth = 0;
443	 break;
444      case GL_FEEDBACK:
445	 if (ctx->Feedback.Count > ctx->Feedback.BufferSize) {
446	    /* overflow */
447	    result = -1;
448	 }
449	 else {
450	    result = ctx->Feedback.Count;
451	 }
452	 ctx->Feedback.Count = 0;
453	 break;
454      default:
455	 _mesa_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
456	 return 0;
457   }
458
459   switch (mode) {
460      case GL_RENDER:
461         break;
462      case GL_SELECT:
463	 if (ctx->Select.BufferSize==0) {
464	    /* haven't called glSelectBuffer yet */
465	    _mesa_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
466	 }
467	 break;
468      case GL_FEEDBACK:
469	 if (ctx->Feedback.BufferSize==0) {
470	    /* haven't called glFeedbackBuffer yet */
471	    _mesa_error( ctx, GL_INVALID_OPERATION, "glRenderMode" );
472	 }
473	 break;
474      default:
475	 _mesa_error( ctx, GL_INVALID_ENUM, "glRenderMode" );
476	 return 0;
477   }
478
479   ctx->RenderMode = mode;
480   if (ctx->Driver.RenderMode)
481      ctx->Driver.RenderMode( ctx, mode );
482
483   return result;
484}
485
486/*@}*/
487
488
489/**********************************************************************/
490/** \name Initialization */
491/*@{*/
492
493/**
494 * Initialize context feedback data.
495 */
496void _mesa_init_feedback( struct gl_context * ctx )
497{
498   /* Feedback */
499   ctx->Feedback.Type = GL_2D;   /* TODO: verify */
500   ctx->Feedback.Buffer = NULL;
501   ctx->Feedback.BufferSize = 0;
502   ctx->Feedback.Count = 0;
503
504   /* Selection/picking */
505   ctx->Select.Buffer = NULL;
506   ctx->Select.BufferSize = 0;
507   ctx->Select.BufferCount = 0;
508   ctx->Select.Hits = 0;
509   ctx->Select.NameStackDepth = 0;
510
511   /* Miscellaneous */
512   ctx->RenderMode = GL_RENDER;
513}
514
515/*@}*/
516