tags:

views:

52

answers:

1

Edit: Updated code. Now works well enough.

Foreword. I am an OpenGL newb tasked with maintaining an app that, among other things, uses wxWidgets and OpenGL to display a variety of image formats as backgrounds for reference in overlaying user items.

The original code for displaying images broke up the image into 512x512 chunks and made textures out of them, throwing away any part of the image that didn't fit into a whole chunk. I need to change this behavior. I increased the size of the array that will hold my named textures, I handled the edge cases of my loops, wherin I set a different size subimage to grab, or I change the texture coordinates to map properly on the partial chunks.

However, build2DMipmaps pukes when I try to pass it a pointer to my subimage when my subimage doesn't have enough data to fill a 512x512 chunk. The amount of data pointed to by pointer being fed to that function is the only thing that is different on the last iteration of each column. It was my intent later to set correct texture coordinate mapping to account for less data. Why does build2dmipmaps puke? Am I using it wrong? Am I doing something somewhere else wrong?

So far, my problem is in the first set of nested for loops. when the inner loop is on its last iteration, say, htiles is 7 and y is 6, the subimage grabbed is a valid subimage (according to wxWidgets) meaning that it has data in it, but then we get to mipmaps and I get an access violation error.

Below you will find the code. Thanks in advance for your time.

int width,height,wtiles,htiles;
width=bg.GetWidth();
height=bg.GetHeight();
bgwidth = width;
bgheight = height;
wtiles=(int)floor((float)width/(float)MAX_TEX);
htiles=(int)floor((float)height/(float)MAX_TEX);

glEnable(GL_TEXTURE_2D);

//there are two types of tiles here, whole tiles and partial tiles
//wtiles and htiles are the number of whole tiles
//there is one row and one column of partial tiles

unsigned int h_error,w_error;
w_error=width % MAX_TEX;
h_error=height % MAX_TEX;

//Below, if the image dimensions break neatly into MAX_TEX chunks,
//then there is no error(leftover).  This usage here allows me to take advantage
//of the logic already made.  If there is error, keep it and add a tile count
//if there is not, then make MAX_TEX the default dimension for ALL texture dimensions

(w_error == 0) ? w_error = MAX_TEX : wtiles++;
(h_error == 0) ? h_error = MAX_TEX : htiles++;


ntiles=wtiles*htiles;
bgtiles=new unsigned int[ntiles];
glGenTextures(ntiles, bgtiles);
float dialogFactor = ntiles / 100.0;

//now to split the tiles out
for(int x=0;x<wtiles;x++)
    for(int y=0;y<htiles;y++){
        dialog.Update((int) ((x*htiles + y) / dialogFactor), "Generating Textures");

        wxImage bloc=bg.GetSubImage(wxRect(x*MAX_TEX,y*MAX_TEX,
                                            (x == wtiles-1) ? w_error : MAX_TEX,
                                            (y == htiles-1) ? h_error : MAX_TEX
                                            )
                                    );

        glBindTexture(GL_TEXTURE_2D, bgtiles[x+(htiles-(y+1))*wtiles]);
        unsigned char *dat=(unsigned char *)bloc.GetData();
        //printf("%d\n",dat[25]);

        glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
        glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
        //gluBuild2DMipmaps(GL_TEXTURE_2D,GL_COMPRESSED_RGB_ARB,
                                        //(x == wtiles-1) ? w_error : MAX_TEX,
                                        //(y == htiles-1) ? h_error : MAX_TEX,
            //GL_RGB,GL_UNSIGNED_BYTE,(GLvoid *)dat);

        glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
        glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_ARB,
                                        (x == wtiles-1) ? w_error : MAX_TEX,
                                        (y == htiles-1) ? h_error : MAX_TEX,
                                        0, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)dat);

    }

    //marvelous, now make a simple display list to blit out the whole mess
    background=glGenLists(1);//dlist made!
    glNewList(background, GL_COMPILE);
    glEnable(GL_TEXTURE_2D);
    glColor4f(1,1,1,1);

    float h_offset,w_offset;

    glDisable(GL_DEPTH_TEST);
    glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
    {
        glPushMatrix();
        for(int x=0;x<wtiles;x++)
            for(int y=0;y<htiles;y++){
                dialog.Update((int) ((x*htiles + y) / dialogFactor), "Generating Display List");
                //if(x==wtiles-1 && y==htiles-1)printf("%f %f\n",(x+1)*MAX_TEX - width *.5,(y+1)*MAX_TEX - height *.5);
                glBindTexture(GL_TEXTURE_2D, bgtiles[x+(y)*wtiles]);
                glBegin(GL_QUADS);

                //if we are on our last column or row, adjust the texture size accordingly.
                //unless the image has been divided evenly, in which case there is no need for offset.
                (w_error != 0 && x == wtiles - 1) ? w_offset = MAX_TEX - w_error : w_offset = 0;
                (h_error != 0 && y == 0)          ? h_offset = MAX_TEX - h_error : h_offset = 0;

                //apparently, if NPOTS textures are supported, their tex map coords are normalized
                // aka 0-1.

                //god this is an ugly mess.
                //for some reason, the ordering of vertices is correct,
                //but the texture coords need to be this way.  It looks wrong but the program functions
                //the offset garbage is to handle placement of the sections of textures that aren't a full
                //MAX_TEX x MAX_TEX
                //conveinently, if the image does break up into even full squares, the errors are still 0.. fuck


                glTexCoord2d(0,1); glVertex3f(x*MAX_TEX - width *.5,(y*MAX_TEX - height *.5)+h_offset,0);
                glTexCoord2d(1,1); glVertex3f(((x+1)*MAX_TEX - width *.5)-w_offset,(y*MAX_TEX - height *.5)+h_offset,0);
                glTexCoord2d(1,0); glVertex3f(((x+1)*MAX_TEX - width *.5)-w_offset,(y+1)*MAX_TEX - height *.5,0);
                glTexCoord2d(0,0); glVertex3f(x*MAX_TEX - width *.5,(y+1)*MAX_TEX - height *.5,0);
                glEnd();
            }
            glPopMatrix();
    }

    glDisable(GL_TEXTURE_2D);
    glEnable(GL_DEPTH_TEST);
    glEndList();
    //that's that
    glDisable(GL_TEXTURE_2D);
A: 

openGL textures must be POT : Power Of Two, i.e. 32*32, 32*64, 1024*512, ...

Your edge images are not, so you have the following solutions :

  • always make your texture a 512x512 one, fill the border with black, handle the UV coordinates with care
  • use the ARB extension to get openGL deal with NPOT textures, see http://www.opengl.org/wiki/NPOT_Texture . This is what you do without knowing. But the GLU function that builds mipmaps doesn't know how to deal with NPOT textures, so you have to roll your own.

And intermediate (and IMHO best) solution is to get the nearest power of two for each of your dimensions. For instance, if your input edge texture is 512*6 (because you're unlucky ^^) create a 512*8 (8 = 2^3) texture, fill the last two columns with a random color, let glu handle the mipmaps, deal with your UVs.

Calvin1602
NPOT has been a core part of GL for a while now. Don't use gluGenerateMipmaps, use the core GL functionality. glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
Bahbar
+1, even if this is ugly because it is not *really* a texture parameter. (but yeah, it's the way you're supposed to do)
Calvin1602
So, assuming that my hardware supports the NPOT ARB extension, the answer is to invoke the extension, then make my 1 NPOTs texture (as opposed to dividing it up) and use the core OpenGL mipmap call?
Joshua
Code now works well enough. I originally wanted 0,0 to be in the exact center of any image I generate, but for now this is enough. Thank you all for your time and help!
Joshua