Sign in to follow this  
broady

Glut and more, need help

Recommended Posts

Hello dear guys, it's 2 months i am coding with glut and opengl and i have some questions. I have a lot of questions so if i miss something (and will be so) i will write again. Suppose i want to load an image in memory and later display it with glDrawPixels. I want to store pixel data in a 1d array. Obviously the array has to be allocated dinamically to match the exatc size of pixel data. This is a pseudo-code i used:
//global var
GLubyte* img;
GLint w,h;

GiveWidth(){  //same for height
..Bind/Load image...
return ilGetInteger(IL_IMAGE_WIDTH);
}


LoadImage(){
...bind/load the image...
img = new GLubyte[w*h*4]; //4 is for r,g,b and a channels
... copy pixel data from ilGetData() to the array pointed by img.....
ilDeleteImage;
}

void display(){
....
glDrawPixels(..w,h,...,img);
glFlush();
}

I have to define w,h and img as global variables since i have to use all of 'em later in the display callback function or they wont be visible inside the display callback function. Is this possible? I khnow i have to avoid using global vars, but how can i do this with Glut? EDIT: MoreOver, when can i call delete[] img; if img is used by display? 2) More a c++ quesion. I khnow the analogy between 1D arrays and pointers. And this analogy is infact used in my code. Is there the same analogy between multidim arrays and pointers? i.e. if i use int b[3], b is a pointer to the first element in the array. Infact if i wanna define a function to recieve an array i can even write f(int* ptr) and call it with f(b). Can i do the same stuff with multidim array? Thanks a lot guys, really appreciated

Share this post


Link to post
Share on other sites
I don't think there is a good way to avoid globals in glut. Glut isn't really advised for modern projects for a variety of reasons, the most pressing being that it is no longer in active development and is very old. You can continue to use it for learning, but keep in mind that you probably can't get rid of the globals until you move another library. If you want to do so now, consider SDL.

The main thing with textures in opengl is that you don't usually need to keep the original texture data around after you load it. The general idea is:

1. load texture into memory, in your case using devil
2. construct the opengl object and fill it in (glGenTextures, glTexImage2D, ...)
3. delete the pointer you loaded the image into, the only thing you need here is the GLuint you got from glGenTextures
4. when using the texture, just glBindTexture(GL_TEXTURE_2D,that GLuint you saved)

glTexImage2D moves a copy of the texture data to the video card (or the video section of memory on integrated systems) so you no longer need to keep it around. If you need the size of the texture so you can draw things in the ratio of their original dimension, find out the width and height and store those variables when you load the texture, you don't need any complicated methods to find those out later.

Share this post


Link to post
Share on other sites
Thanks for the time DaedalusOwnsYou. For now ill continue to use glut since i am just learning all.

A replace to my 2nd question would be appreciated.

Thanks
Bro

Share this post


Link to post
Share on other sites
If you want to stick with glut but don't want to have globals hanging around everywhere, you can put all of your important stuff in a class and then have the glut callback functions call the methods in the class. Like this...


// Some class where all the
class SomeClass
{
public:
SomeClass();

void draw();
keyInput(unsigned char key, int x, int y);
void mouseInput(int button, int state, int x, int y);

// Other methods

private:

// Some more methods

GLubyte* img;
GLint w,h;
};



In your main.cpp...

// Have one global instance of your class
SomeClass instance;

// Your free functions call the member functions

void drawCallback()
{
// Pass the buck on to the methods
instance.draw();
}

void keyCallback(unsigned char key, int x, int y)
{
instance.keyInput(key, x, y);
}

// and so on...





Share this post


Link to post
Share on other sites
hey mjp,

thanks for thr time and for the code :D

1 more question: other than sdl what can i use? any suggestion? I need something easy and fast since i have 2 days to end my "work".

thanks
bro

Share this post


Link to post
Share on other sites
If you only have 2 days to finish a project, don't bother trying to learn a new library for windowing. Just something to think about for the next project.

Another good choice for a cross-platform windowing library is SFML.

I completely missed your second question first time around! The easiest way to treat a multidimensional array using pointers is just to allocate the whole thing as a 1D array and index it by multiplying one index by the dimension.

Example

int width = 10;
int height = 15;
int* array = new int[width*height];

int x = 2;
int y = 5;
int value = array[x+y*width];

//to delete the array
delete[] array;

Here the values are stored row by row, so y*width is the start of the 'y'th row of data. After adding x we have the index of the x,y coordinate in the array.

If you really want to allocate a multidimensional array you'll have to do something like this:

int width = 10;
int height = 15;
int** array = new int*[width];
for (int x=0;x<width;x++) {
array[x] = new int[height];
}

int x = 2;
int y = 5;
int value = array[x][y];

//to delete the array
for (int x=0;x<width;x++) {
delete[] array[x];
}
delete[] array;

Notice we have to allocate each row of the array individually. The advantage is that we can index using [x][y] instead of doing x+y*width.

Passing these around in code you would do something like:

//first method
void f(int* array);

//second method
void f(int** array);

And then just access as normal. Might have to pass the size with the array, depending on what you are doing.

If you like the second method more, and even if you are using the first really, consider using std::vector. That's what it's for after all. If you aren't familiar with std::vector go look it up in the docs, or just google it. It's really nice. Trust me.

Share this post


Link to post
Share on other sites
I don't khnow how to thanks u guys. My code now rocks :D I used the class approach suggested by MJP and i have a more clear idea about those dinamic arrays. When i have some free time ill look to std::vector. Thanks DaedalusOwnsYou and MJP.

There is a but... ill past come code:

The method CreateImage:

void image::CreateImage(std::string str)
{
ILboolean imgload;
static ILuint texid;
ilGenImages(1,&texid);
ilBindImage(texid);

imgload = ilLoadImage(str.c_str());

if(imgload)
{
std::cout<< "success loading\n";
imgload = ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE); // 8 bit per pixel
if (!imgload)
{
/* error conveting */
std::cout << "Error converting\n";
}
else
{
std::cout<<"success converting\n";
this->w = ilGetInteger(IL_IMAGE_WIDTH);
this->h = ilGetInteger(IL_IMAGE_HEIGHT);
GLubyte array[ilGetInteger(IL_IMAGE_WIDTH) * ilGetInteger(IL_IMAGE_HEIGHT) * 4];
ILubyte* ptr = ilGetData();

for (int i = 0; i < ilGetInteger(IL_IMAGE_WIDTH) * ilGetInteger(IL_IMAGE_HEIGHT) * 4; i++){
array[i] = ptr[i];
}

this->img = array;
}

}
else
{
/* error loading */
std::cout << "error loading";
}
ilDeleteImage(texid);

}




It works strange. Look this link where i have uploaded the image:

[url=http://img528.imageshack.us/my.php?image=imgrn2.png]img[/url]

The 2 black lines are added by me to show where the image ends. The bg is white since outside of the body i have a zero value for the alpha channel and blend is enable. Why are there those fricking pixels on the top of the image? This is really strange since the code in the method is for the most copied and pasted from the old code (the one i left for the class approach) wich still works perfectly.

To be clearer ill add the draw method

void image::Draw(){glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, img);}




and the display callback:

void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glColor3f(0.0, 0.0, 0.0);

glBegin(GL_LINES);
glVertex2f(0.0 , png.GetHeight());
glVertex2f(png.GetWidth(), png.GetHeight());
glVertex2f(png.GetWidth(), png.GetHeight());
glVertex2f(png.GetWidth(), 0.0);
glEnd();

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
png.Draw();
glDisable(GL_BLEND);
glFlush();
}




I can be satispied for today but.
Goodnight and thanks again for the support.
Bro

Share this post


Link to post
Share on other sites
I'm not to sure about the artifact you're seeing, but the biggest gotcha is right here:

GLubyte array[ilGetInteger(IL_IMAGE_WIDTH) * ilGetInteger(IL_IMAGE_HEIGHT) * 4];
...stuff...
this->img = array;



You really, really, really need to allocate that memory on the heap, rather than on the stack, so you maintain it while you need it outside of the scope of that method.

You're doing this:

void f() {
int myThing[20];
}


Which means that when f ends, myThing is out of scope and gets reclaimed. If we pass myThing to something outside of the function (like setting a class variable to it like in your code) the compiler can (and most likely will) reuse that memory for other things. You need to be doing this:


void f() {
int* myThing = new int[20];
delete[] myThing;
}


This means that the compiler knows that myThing is something I care about, and it won't get rid of it untill that "delete[] myThing" line. You can move the delete command to the destructor of your class, or wherever you are free to get rid of the image data. This ensures that nothing else will get written to that area of memory while you care about it.

Now that I think about it, I bet that will solve your visible problem too, as well as being correct coding practice.

One final note: it's good to remember to actually call those deletes. Doing a new without a corresponding delete is called a "memory leak", which means that the program allocates more memory than it frees, at least until the whole thing exists. Not too serious in this case, but it can be a major headache later on and you should understand how to avoid that problem early.

Good luck!

Share this post


Link to post
Share on other sites
Hey Daedalus,
thanks thanks.

The code you saw below is a method of the image class. Here is its interface:


class image
{
public:
image();
void CreateImage(const std::string);
int GetWidth() const;
int GetHeight() const;
// soon a GetData
void Draw();
void ReverseData();
void keyInput(unsigned char key, int x, int y);
void MouseInput(int button, int state, int x, int y);

private:
GLubyte* img;
GLint w,h;
};



The construnctor just sets img to Null and w,h to zero. The method i use to "really" create the image is the one u have seen before: CreateImage(string). So to create and use an object i do:


image myimg;
myimg.CreateImage("sample.png");

//in the display callback
myimg.draw();



I have to use new so i am sure the allocated data wont be touched, all right. In a typical glut program (moslty a main + some callback) where do i use the destructor? I didn't use the new/delete just cause i don't khnow where i am free to call the destructor.

Thanks again and again
Bro

Share this post


Link to post
Share on other sites
I'm going to assume that you have image as a global, so in your initialization area you call image.CreateImage. I'm pretty sure the constructors for global gets called before main, and the destructors right after. So if you delete the image data in the destructor of your image class, and the image is just a value, not a pointer to an image, the destructor will be called automatically for you when the program exits, and you just have to delete that pointer in the destructor.

Someone else needs to back me up on this, because I don't think I've ever used a global class value that wasn't a pointer.

You might want to do that instead.

Change your

Image myImg;

to

Image* myImg;

Then in main (or your init function) make a single new image like so

myImg = new Image;
myImg->CreateImage("filename.thing");

You'll have to change all of your myImg.thing to myImg->thing but then you know when myImg's destructor gets called; it gets called when you call it by "delete myImg" at the end of main.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this