CS 553 -- Winter Quarter 2009

Project #10: Volume Interaction

130 Points

Due: March 8


This page was last updated: February 27, 2009


Requirements:

  1. Put this project number and your name in the title bar.

  2. Create a volume of nodes like you did in Projects #2, #3, #4, and #5:
    struct node Nodes[NX][NY][NZ];

    Pick NX, NY, and NZ such that each is a power of 2.

  3. For each node, compute its scalar value S and its associated R, G, B. Use the same scalar value function and color-mapping scheme that you used before. You won't need to store each node's X, Y, and Z, but you can if you want to.

  4. Create a range slider that will determine what range of S will be allowed to be seen.

  5. Create a normal slider that will determine the opacity that the visible S values will have.

  6. Rig the slider callback so that it will force a recomposite to take place the next time Display() is called.

  7. Write 3 routines, FillXY(), FillXZ(), and FillYZ(). These will fill one of 3 Texture arrays:

    unsigned char   TextureXY[NZ][NX][NY][4];
    unsigned char   TextureXZ[NY][NX][NZ][4];
    unsigned char   TextureYZ[NX][NY][NZ][4];
    

    Note that:

    unsigned char   TextureXY[NZ][NX][NY][4];
    

    means "NZ slices of an NX by NY 4-component texture".

    These hold 4 components, not 3, because you will be using an alpha value as well.

    void
    FillXY( void )
    {
        int x, y, z, zz;
        float alpha;	// opacity at this voxel
        float r, g, b;	// running color composite
    
        for( x = 0; x < NX; x++ )
        {
            for( y = 0; y < NY; y++ )
            {
                r = g = b = 0.;
                for( zz = 0; zz < NZ; zz++ )
                {
                    // which direction to fill:
    
                    if( Zside == PLUS )
                        z = zz;
                    else
                        z = ( NZ-1 ) - zz;
    
    
                    if( << this scalar value is not in the range you want to view >> )
                    {
                        r = g = b = 0.;
                        alpha = 0.;
                    }
                    else
                    {
                        r = Nodes[x][y][z].r;
                        g = Nodes[x][y][z].g;
                        b = Nodes[x][y][z].b;
                        alpha = MaxAlpha;
                    }
    
                    TextureXY[zz][y][x][0] = (unsigned char) ( 255.*r + .5 );
                    TextureXY[zz][y][x][1] = (unsigned char) ( 255.*g + .5 );
                    TextureXY[zz][y][x][2] = (unsigned char) ( 255.*b + .5 );
                    TextureXY[zz][y][x][3] = (unsigned char) ( 255.*alpha + .5 );
                }
            }
        }
    }
    

  8. Use the Xrot, Yrot, and Scale transformations, like sample.cpp did.

  9. Use double-buffering.

Determining Which Direction to Draw:

Depending on the rotation of the volume, you will want to draw the parallel planes in the X, Y, or Z direction, and draw them MINUS-to-PLUS or PLUS-to-MINUS. The following code tells you which of these cases is the one you will need.

// which way is a surface facing:

const int MINUS = { 0 };
const int PLUS  = { 1 };


// what is the major direction:

#define X    0
#define Y    1
#define Z    2


    . . .


int    Major;			// X, Y, or Z
int    Xside, Yside, Zside;	// which side is visible, PLUS or MINUS


    . . .

//
// determine which sides of the cube are facing the user
//
// this assumes that the rotation is being done by:
//
//    glRotatef( Yrot, 0., 1., 0. );
//    glRotatef( Xrot, 1., 0., 0. );
//


void
DetermineVisibility()
{
    float xr, yr;
    float cx, sx;
    float cy, sy;
    float nzx, nzy, nzz;	// z component of normal for x side, y side, and z side

    xr = Xrot * ( M_PI/180. );
    yr = Yrot * ( M_PI/180. );

    cx = cos( xr );
    sx = sin( xr );
    cy = cos( yr );
    sy = sin( yr );

    nzx = -sy;
    nzy =  sx * cy;
    nzz =  cx * cy;


    // which sides of the cube are showing:
    // MINUS or PLUS tells us which side is facing the viewer

    Xside = ( nzx > 0.  ?  PLUS  :  MINUS );
    Yside = ( nzy > 0.  ?  PLUS  :  MINUS );
    Zside = ( nzz > 0.  ?  PLUS  :  MINUS );


    // which direction needs to be composited:

    if( fabs(nzx) > fabs(nzy)  &&  fabs(nzx) > fabs(nzz)  )
        Major = X;
    else if( fabs(nzy) > fabs(nzx)  &&  fabs(nzy) > fabs(nzz)  )
        Major = Y;
    else
        Major = Z;
}

Drawing:

    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );

    filter = GL_NEAREST;
    if( Bilinear )
        filter = GL_LINEAR;
    else
        filter = GL_NEAREST;

    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter );
    glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
    glEnable( GL_TEXTURE_2D );


    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
    glEnable( GL_BLEND );


    if( Major == Z )
    {
        if( Zside == PLUS )
        {
            z0 = -1.;
            dz = 2. / (float)( NZ - 1 );
        }
        else
        {
            z0 = 1.;
            dz = -2. / (float)( NZ - 1 );
        }

        for( z = 0, zcoord = z0; z < NZ; z++, zcoord += dz )
        {
            glTexImage2D( GL_TEXTURE_2D, 0, 4, NX, NY, 0, GL_RGBA, GL_UNSIGNED_BYTE, &TextureXY[z][0][0][0] );

            glBegin( GL_QUADS );
            
                glTexCoord2f( 0.f, 0.f );
                glVertex3f( -1.f, -1.f, zcoord );
    
                glTexCoord2f( 1.f, 0.f );
                glVertex3f( 1.f, -1.f, zcoord );
    
                glTexCoord2f( 1.f, 1.f );
                glVertex3f( 1.f, 1.f, zcoord );
    
                glTexCoord2f( 0.f, 1.f );
                glVertex3f( -1.f, 1.f, zcoord );

            glEnd();
        }
    }

  . . .

Grading:

Item Points
3D transformation 10
Correctly composites 40
Correctly changes compositing direction 20
Handles the S range correctly 30
Handles the opacity correctly 30
Potential Total 130