using glReadPixels() on a texture

Started by
6 comments, last by Foobar of Integers 17 years, 9 months ago
I've got an interesting little problem here... I'm making an isometric tile map in GL, which is working quite well, but I need to be able to figure out which tile a point lies on. The way I chose to do this was via a "mouse map" picture, with different colors that tell you what tile your point is in (once you calculate the general area of the mouse) It works very well, but the issue is that I'm using glReadPixels() to get the color of the texture at a certain point. That works, but in order for it to work, I need to actually draw the mouse map on the screen somewhere, since glReadPixels() reads from the framebuffer, which is rather ugly and wouldn't work in the game. There's two things I could do, and I need help with one: 1 - Draw the mousemap before rendering the frame, figure out where my point lies on, and then clear the screen after it. It would work, but some day I may need to read several different points, and clearing the screen twice per frame isn't exactly speedy, using glReadPixels() ONCE per frame takes a large framerate hit as it is. 2 - Store the mouse map texture in memory and read the value of a texel whenever I need to. This is ideal, but I don't know how to read individual pixels without drawing to the screen first. This is what I need some help on. [Edited by - Foobar of Integers on July 1, 2006 8:16:51 PM]
"ok, pac man is an old gameand, there are faces which is eatin up shits" - da madface
Advertisement
I think the 2nd way of handling things is more efficient as you guessed. May i ask how you render the mousemap on the screen. How it looks when it's on screen?

1) Are you rendering it on top of a 3D mesh? If this is the case i think gluUnProject() will help you. It gives the world space position of a point in window space. This and the camera's position make a ray which you could trace to find out which tile is hit.

2) Are you rendering it as a 2D quad which covers the whole screen? If you know which part of the texture is visible on screen, then you can easily find the pixel by simply reading the texture as:

MouseMap[OffsetX + MouseX + (OffsetY + MouseY) * MouseMapWidth]

OffsetX and OffsetY are the coordinates of the mousemap that correspond to the window origin (0,0).

Maybe if you can post a screenshot of how your mousemap looks like on screen (and how you render it), i (or maybe someone else) can help better.

Sorry if the above seemed too obvious.

HellRaiZer
HellRaiZer
I'm not using 3D or anything, I'm using a 2D display, which is created with gluOrtho2D(). The current way I'm using the mousemap is to draw it (it's a 96x48 image) in the bottom left corner of the screen, read the needed pixels, then draw over it with a black rectangle, and then render the frame.

This is the mouse map:



And this is a screen of the map engine, the mouse map is in the bottom left corner, but you can't see it

Edit: I'm not sure you're trying to answer what I'm asking. I know how to get coordinates on the mouse map, but I want to know how to get them without drawing it to the screen.

[Edited by - Foobar of Integers on July 2, 2006 10:14:59 PM]
"ok, pac man is an old gameand, there are faces which is eatin up shits" - da madface
If you know where to read using glReadPixels() then i think it's easy to read the mousemap without doing any texture or glReadPixels().

Suppose you are doing something like this:

unsigned char pixel[3];glReadPixels(mousemapPos_x, mousemapPos_y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &pixel[0]);


This can be done without rendering and reading from the frame buffer if you have the mousemap data into an array.

unsigned char pixel[3];pixel[0] = MouseMapData[(mousemapPos_x + mousemapPos_y * MousemapWidth) * 3 + 0];pixel[1] = MouseMapData[(mousemapPos_x + mousemapPos_y * MousemapWidth) * 3 + 1];pixel[2] = MouseMapData[(mousemapPos_x + mousemapPos_y * MousemapWidth) * 3 + 2];


What i was trying to ask in the previous post was whether you know the exact location of the mouse on the mousemap. If you know that the mouse position corresponds to the (mousemapPos_x, mousemapPos_y) pixel in the mousemap, then there is no need to render and then read back from the framebuffer.

The offset thing i mentioned previously was in case your mousemap was bigger than the screen, and you rendered it with some parts outside of the screen.

If the above is completely wrong, can you post the glReadPixels() call in case to try and figure out a way to convert it to a memory read?

HellRaiZer
HellRaiZer
The above isn't wrong, you're completely right, and that's what I've been wanting to do. Unfortunately, I'm having trouble copying the mouse map data into an array.

I tried using SDL and actually accessing the pixels to copy some simple bytes into an array the size of the picture (0 for center, 1 for NW, 2 for NE, etc) but it sort of blew up in my face. I also tried reading the pixels beforehand and printing the data to a text file, which refused to work for whatever reason. If I could get photoshop to directly output the picture as a text file, that'd rock so much.
"ok, pac man is an old gameand, there are faces which is eatin up shits" - da madface
Quote:Original post by Foobar of Integers
The above isn't wrong, you're completely right, and that's what I've been wanting to do. Unfortunately, I'm having trouble copying the mouse map data into an array.

I tried using SDL and actually accessing the pixels to copy some simple bytes into an array the size of the picture (0 for center, 1 for NW, 2 for NE, etc) but it sort of blew up in my face. I also tried reading the pixels beforehand and printing the data to a text file, which refused to work for whatever reason. If I could get photoshop to directly output the picture as a text file, that'd rock so much.


I haven't used SDL but if you have access to the raw pixel data then converting them to your format shouldn't be too hard.

Something like the following (all from the top of my head, sorry if there is a mistake)
char* result;  // array declared for mouse_map width * heightint index = 0; // temp index into the above array // loop over the raw data, taking into consideration that it is RGB (check it isn't BGR if BMP)for (int i = 0; i < (width * height * 3); i+=3){    // Read the 3 components (RGB) from the image    const unsigned char r = raw_date;    const unsigned char g = raw_data;<br>    const unsigned char b = raw_data;<br> <br>    // compare to expected values and map to new value<br>    if (r == 0 && g == 255 && b == 0)<br>        result[index++] = 0;  // top left<br>    else if (r == 255 && g == 255 && b == 255)<br>        result[index++] = 1;  // center<br>    else if (…)<br>    {<br>       // etc…<br>    }<br>}</pre><br><br>The check for the value should then simply be<pre>location = mouseMap[mouse_x + mouse_y * width];</pre><br><br>Hope that helps<br>
a little hint: i am not sure but i think SDL stores raw pixel data in BGRA.

you may have something like that:

SDL_Surface *image = IMG_Load("jsdf");glTexImage2d(GL_TEXTURE_2D,0,GL_RGBA,image->w,image->h,0,GL_RGBA(not sure about the format like said above), GL_UNSIGNED_BYTE,image->pixels);


image->pixels is your pointer to the pixel data.
Well, I've fixed it, and now it works without reading pixels at all, just an array access. At the beginning of the game, before the buffer is flipped for the first time, it draws the mouse map, reads each pixel, and stores the value in an array. Then you can just read from the array, it's much faster at the expense of a mere few KB of memory. I'll post the code for the hell of it:

int IsoMap::LoadMouseMap(const char* file){	printf("Allocating memory for mouse map: %i KB\n", (int)(TILEWIDTH * TILEHEIGHT * sizeof(MouseMapValue)) / 1024);	m_pMouseMap = new MouseMapValue*[TILEHEIGHT];		for(int i = 0; i < TILEHEIGHT; i++)	{		m_pMouseMap = new MouseMapValue[TILEWIDTH];	}		TextureHandle t = m_pTextureManager->LoadTexture(file);	if(t == -1) return -1;		GLuint* temp = m_pTextureManager->GetTexture(t);		ISODrawFromBottom(0, m_ScreenH, TILEWIDTH, TILEHEIGHT, temp);	for(int row = 0; row < TILEHEIGHT; row++)	{		for(int col = 0; col < TILEWIDTH; col++)		{			float RGB[3];						glReadPixels(col,  TILEHEIGHT - row, 1, 1, GL_RGB, GL_FLOAT, RGB);									if(ISOIsColor(1.0, 1.0, 1.0, RGB)) // white			{				m_pMouseMap[row][col] = MM_CENTER;			}			else if(ISOIsColor(1.0, 0.0, 0.0, RGB)) // R			{ 				m_pMouseMap[row][col] = MM_NE;			}			else if(ISOIsColor(0.0, 1.0, 0.0, RGB)) // G			{				m_pMouseMap[row][col] = MM_NW;			}			else if(ISOIsColor(0.0, 0.0, 1.0, RGB)) // B			{				m_pMouseMap[row][col] = MM_SE;			}			else if(ISOIsColor(1.0, 1.0, 0.0, RGB)) // Y			{				m_pMouseMap[row][col] = MM_SW;			}			else			{				//this happens for pixel 0, 0 for some reason, but it works fine anyway.								m_pMouseMap[row][col] = MM_INVALID;			}					}	}	delete temp;	return 0;}
"ok, pac man is an old gameand, there are faces which is eatin up shits" - da madface

This topic is closed to new replies.

Advertisement