Home | History | Annotate | Download | only in main
      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 #include "main/dispatch.h"
     39 
     40 
     41 #define FB_3D		0x01
     42 #define FB_4D		0x02
     43 #define FB_COLOR	0x04
     44 #define FB_TEXTURE	0X08
     45 
     46 
     47 
     48 void GLAPIENTRY
     49 _mesa_FeedbackBuffer( GLsizei size, GLenum type, GLfloat *buffer )
     50 {
     51    GET_CURRENT_CONTEXT(ctx);
     52 
     53    if (ctx->RenderMode==GL_FEEDBACK) {
     54       _mesa_error( ctx, GL_INVALID_OPERATION, "glFeedbackBuffer" );
     55       return;
     56    }
     57    if (size<0) {
     58       _mesa_error( ctx, GL_INVALID_VALUE, "glFeedbackBuffer(size<0)" );
     59       return;
     60    }
     61    if (!buffer && size > 0) {
     62       _mesa_error( ctx, GL_INVALID_VALUE, "glFeedbackBuffer(buffer==NULL)" );
     63       ctx->Feedback.BufferSize = 0;
     64       return;
     65    }
     66 
     67    switch (type) {
     68       case GL_2D:
     69 	 ctx->Feedback._Mask = 0;
     70 	 break;
     71       case GL_3D:
     72 	 ctx->Feedback._Mask = FB_3D;
     73 	 break;
     74       case GL_3D_COLOR:
     75 	 ctx->Feedback._Mask = (FB_3D | FB_COLOR);
     76 	 break;
     77       case GL_3D_COLOR_TEXTURE:
     78 	 ctx->Feedback._Mask = (FB_3D | FB_COLOR | FB_TEXTURE);
     79 	 break;
     80       case GL_4D_COLOR_TEXTURE:
     81 	 ctx->Feedback._Mask = (FB_3D | FB_4D | FB_COLOR | FB_TEXTURE);
     82 	 break;
     83       default:
     84          _mesa_error( ctx, GL_INVALID_ENUM, "glFeedbackBuffer" );
     85 	 return;
     86    }
     87 
     88    FLUSH_VERTICES(ctx, _NEW_RENDERMODE); /* Always flush */
     89    ctx->Feedback.Type = type;
     90    ctx->Feedback.BufferSize = size;
     91    ctx->Feedback.Buffer = buffer;
     92    ctx->Feedback.Count = 0;	              /* Because of this. */
     93 }
     94 
     95 
     96 void GLAPIENTRY
     97 _mesa_PassThrough( GLfloat token )
     98 {
     99    GET_CURRENT_CONTEXT(ctx);
    100 
    101    if (ctx->RenderMode==GL_FEEDBACK) {
    102       FLUSH_VERTICES(ctx, 0);
    103       _mesa_feedback_token( ctx, (GLfloat) (GLint) GL_PASS_THROUGH_TOKEN );
    104       _mesa_feedback_token( ctx, token );
    105    }
    106 }
    107 
    108 
    109 /**
    110  * Put a vertex into the feedback buffer.
    111  */
    112 void
    113 _mesa_feedback_vertex(struct gl_context *ctx,
    114                       const GLfloat win[4],
    115                       const GLfloat color[4],
    116                       const GLfloat texcoord[4])
    117 {
    118    _mesa_feedback_token( ctx, win[0] );
    119    _mesa_feedback_token( ctx, win[1] );
    120    if (ctx->Feedback._Mask & FB_3D) {
    121       _mesa_feedback_token( ctx, win[2] );
    122    }
    123    if (ctx->Feedback._Mask & FB_4D) {
    124       _mesa_feedback_token( ctx, win[3] );
    125    }
    126    if (ctx->Feedback._Mask & FB_COLOR) {
    127       _mesa_feedback_token( ctx, color[0] );
    128       _mesa_feedback_token( ctx, color[1] );
    129       _mesa_feedback_token( ctx, color[2] );
    130       _mesa_feedback_token( ctx, color[3] );
    131    }
    132    if (ctx->Feedback._Mask & FB_TEXTURE) {
    133       _mesa_feedback_token( ctx, texcoord[0] );
    134       _mesa_feedback_token( ctx, texcoord[1] );
    135       _mesa_feedback_token( ctx, texcoord[2] );
    136       _mesa_feedback_token( ctx, texcoord[3] );
    137    }
    138 }
    139 
    140 
    141 /**********************************************************************/
    142 /** \name Selection */
    143 /*@{*/
    144 
    145 /**
    146  * Establish a buffer for selection mode values.
    147  *
    148  * \param size buffer size.
    149  * \param buffer buffer.
    150  *
    151  * \sa glSelectBuffer().
    152  *
    153  * \note this function can't be put in a display list.
    154  *
    155  * Verifies we're not in selection mode, flushes the vertices and initialize
    156  * the fields in __struct gl_contextRec::Select with the given buffer.
    157  */
    158 void GLAPIENTRY
    159 _mesa_SelectBuffer( GLsizei size, GLuint *buffer )
    160 {
    161    GET_CURRENT_CONTEXT(ctx);
    162 
    163    if (size < 0) {
    164       _mesa_error(ctx, GL_INVALID_VALUE, "glSelectBuffer(size)");
    165       return;
    166    }
    167 
    168    if (ctx->RenderMode==GL_SELECT) {
    169       _mesa_error( ctx, GL_INVALID_OPERATION, "glSelectBuffer" );
    170       return;			/* KW: added return */
    171    }
    172 
    173    FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
    174    ctx->Select.Buffer = buffer;
    175    ctx->Select.BufferSize = size;
    176    ctx->Select.BufferCount = 0;
    177    ctx->Select.HitFlag = GL_FALSE;
    178    ctx->Select.HitMinZ = 1.0;
    179    ctx->Select.HitMaxZ = 0.0;
    180 }
    181 
    182 
    183 /**
    184  * Write a value of a record into the selection buffer.
    185  *
    186  * \param ctx GL context.
    187  * \param value value.
    188  *
    189  * Verifies there is free space in the buffer to write the value and
    190  * increments the pointer.
    191  */
    192 static inline void
    193 write_record(struct gl_context *ctx, GLuint value)
    194 {
    195    if (ctx->Select.BufferCount < ctx->Select.BufferSize) {
    196       ctx->Select.Buffer[ctx->Select.BufferCount] = value;
    197    }
    198    ctx->Select.BufferCount++;
    199 }
    200 
    201 
    202 /**
    203  * Update the hit flag and the maximum and minimum depth values.
    204  *
    205  * \param ctx GL context.
    206  * \param z depth.
    207  *
    208  * Sets gl_selection::HitFlag and updates gl_selection::HitMinZ and
    209  * gl_selection::HitMaxZ.
    210  */
    211 void
    212 _mesa_update_hitflag(struct gl_context *ctx, GLfloat z)
    213 {
    214    ctx->Select.HitFlag = GL_TRUE;
    215    if (z < ctx->Select.HitMinZ) {
    216       ctx->Select.HitMinZ = z;
    217    }
    218    if (z > ctx->Select.HitMaxZ) {
    219       ctx->Select.HitMaxZ = z;
    220    }
    221 }
    222 
    223 
    224 /**
    225  * Write the hit record.
    226  *
    227  * \param ctx GL context.
    228  *
    229  * Write the hit record, i.e., the number of names in the stack, the minimum and
    230  * maximum depth values and the number of names in the name stack at the time
    231  * of the event. Resets the hit flag.
    232  *
    233  * \sa gl_selection.
    234  */
    235 static void
    236 write_hit_record(struct gl_context *ctx)
    237 {
    238    GLuint i;
    239    GLuint zmin, zmax, zscale = (~0u);
    240 
    241    /* HitMinZ and HitMaxZ are in [0,1].  Multiply these values by */
    242    /* 2^32-1 and round to nearest unsigned integer. */
    243 
    244    assert( ctx != NULL ); /* this line magically fixes a SunOS 5.x/gcc bug */
    245    zmin = (GLuint) ((GLfloat) zscale * ctx->Select.HitMinZ);
    246    zmax = (GLuint) ((GLfloat) zscale * ctx->Select.HitMaxZ);
    247 
    248    write_record( ctx, ctx->Select.NameStackDepth );
    249    write_record( ctx, zmin );
    250    write_record( ctx, zmax );
    251    for (i = 0; i < ctx->Select.NameStackDepth; i++) {
    252       write_record( ctx, ctx->Select.NameStack[i] );
    253    }
    254 
    255    ctx->Select.Hits++;
    256    ctx->Select.HitFlag = GL_FALSE;
    257    ctx->Select.HitMinZ = 1.0;
    258    ctx->Select.HitMaxZ = -1.0;
    259 }
    260 
    261 
    262 /**
    263  * Initialize the name stack.
    264  *
    265  * Verifies we are in select mode and resets the name stack depth and resets
    266  * the hit record data in gl_selection. Marks new render mode in
    267  * __struct gl_contextRec::NewState.
    268  */
    269 void GLAPIENTRY
    270 _mesa_InitNames( void )
    271 {
    272    GET_CURRENT_CONTEXT(ctx);
    273    FLUSH_VERTICES(ctx, 0);
    274 
    275    /* Record the hit before the HitFlag is wiped out again. */
    276    if (ctx->RenderMode == GL_SELECT) {
    277       if (ctx->Select.HitFlag) {
    278          write_hit_record( ctx );
    279       }
    280    }
    281    ctx->Select.NameStackDepth = 0;
    282    ctx->Select.HitFlag = GL_FALSE;
    283    ctx->Select.HitMinZ = 1.0;
    284    ctx->Select.HitMaxZ = 0.0;
    285    ctx->NewState |= _NEW_RENDERMODE;
    286 }
    287 
    288 
    289 /**
    290  * Load the top-most name of the name stack.
    291  *
    292  * \param name name.
    293  *
    294  * Verifies we are in selection mode and that the name stack is not empty.
    295  * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
    296  * and replace the top-most name in the stack.
    297  *
    298  * sa __struct gl_contextRec::Select.
    299  */
    300 void GLAPIENTRY
    301 _mesa_LoadName( GLuint name )
    302 {
    303    GET_CURRENT_CONTEXT(ctx);
    304 
    305    if (ctx->RenderMode != GL_SELECT) {
    306       return;
    307    }
    308    if (ctx->Select.NameStackDepth == 0) {
    309       _mesa_error( ctx, GL_INVALID_OPERATION, "glLoadName" );
    310       return;
    311    }
    312 
    313    FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
    314 
    315    if (ctx->Select.HitFlag) {
    316       write_hit_record( ctx );
    317    }
    318    if (ctx->Select.NameStackDepth < MAX_NAME_STACK_DEPTH) {
    319       ctx->Select.NameStack[ctx->Select.NameStackDepth-1] = name;
    320    }
    321    else {
    322       ctx->Select.NameStack[MAX_NAME_STACK_DEPTH-1] = name;
    323    }
    324 }
    325 
    326 
    327 /**
    328  * Push a name into the name stack.
    329  *
    330  * \param name name.
    331  *
    332  * Verifies we are in selection mode and that the name stack is not full.
    333  * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
    334  * and adds the name to the top of the name stack.
    335  *
    336  * sa __struct gl_contextRec::Select.
    337  */
    338 void GLAPIENTRY
    339 _mesa_PushName( GLuint name )
    340 {
    341    GET_CURRENT_CONTEXT(ctx);
    342 
    343    if (ctx->RenderMode != GL_SELECT) {
    344       return;
    345    }
    346 
    347    FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
    348    if (ctx->Select.HitFlag) {
    349       write_hit_record( ctx );
    350    }
    351    if (ctx->Select.NameStackDepth >= MAX_NAME_STACK_DEPTH) {
    352       _mesa_error( ctx, GL_STACK_OVERFLOW, "glPushName" );
    353    }
    354    else
    355       ctx->Select.NameStack[ctx->Select.NameStackDepth++] = name;
    356 }
    357 
    358 
    359 /**
    360  * Pop a name into the name stack.
    361  *
    362  * Verifies we are in selection mode and that the name stack is not empty.
    363  * Flushes vertices. If there is a hit flag writes it (via write_hit_record()),
    364  * and removes top-most name in the name stack.
    365  *
    366  * sa __struct gl_contextRec::Select.
    367  */
    368 void GLAPIENTRY
    369 _mesa_PopName( void )
    370 {
    371    GET_CURRENT_CONTEXT(ctx);
    372 
    373    if (ctx->RenderMode != GL_SELECT) {
    374       return;
    375    }
    376 
    377    FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
    378    if (ctx->Select.HitFlag) {
    379       write_hit_record( ctx );
    380    }
    381    if (ctx->Select.NameStackDepth == 0) {
    382       _mesa_error( ctx, GL_STACK_UNDERFLOW, "glPopName" );
    383    }
    384    else
    385       ctx->Select.NameStackDepth--;
    386 }
    387 
    388 /*@}*/
    389 
    390 
    391 /**********************************************************************/
    392 /** \name Render Mode */
    393 /*@{*/
    394 
    395 /**
    396  * Set rasterization mode.
    397  *
    398  * \param mode rasterization mode.
    399  *
    400  * \note this function can't be put in a display list.
    401  *
    402  * \sa glRenderMode().
    403  *
    404  * Flushes the vertices and do the necessary cleanup according to the previous
    405  * rasterization mode, such as writing the hit record or resent the select
    406  * buffer index when exiting the select mode. Updates
    407  * __struct gl_contextRec::RenderMode and notifies the driver via the
    408  * dd_function_table::RenderMode callback.
    409  */
    410 GLint GLAPIENTRY
    411 _mesa_RenderMode( GLenum mode )
    412 {
    413    GET_CURRENT_CONTEXT(ctx);
    414    GLint result;
    415    ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
    416 
    417    if (MESA_VERBOSE & VERBOSE_API)
    418       _mesa_debug(ctx, "glRenderMode %s\n", _mesa_enum_to_string(mode));
    419 
    420    FLUSH_VERTICES(ctx, _NEW_RENDERMODE);
    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 #ifdef DEBUG
    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  */
    496 void _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