Jump to content
  • Advertisement
Sign in to follow this  
BytePtr

OpenGL Need help on improving / modifying my OpenGL cube face picking algo / code

This topic is 2563 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi.

Im drawing lot's of cubes with OpenGL in my 3D map editor.
In this editor i need feature called: cube face picking and it must be precise!
So user can click on any cube face and my program should detect precisely what block face user clicked: was it lid, left, right, top or bottom.

I Googled alot about it and found practically nothing (-working). So i thinked about a bit and decided to add together few of different methods and tools.
So i decided to use: OpenGL color picking, STL std::map, and OpenGL selection using glLoadName (selection buffer) for naming my block faces.

Each block face has name assigned: LEFT, RIGHT, TOP, BOTTOM, LID. I can click on blocks and OpenGL gives me very precise results.
This works fine.
gluUnProject has problems detecting cube faces, so i decided to drop it.

I draw each cube with unique colors.

http://content.gpwik...nique_Color_IDs

I use only this snippet:
m_colorID[0] = gColorID[0]; m_colorID[1] = gColorID[1]; m_colorID[2] = gColorID[2]; gColorID[0]++; if(gColorID[0] > 255) { gColorID[0] = 0; gColorID[1]++; if(gColorID[1] > 255) { gColorID[1] = 0; gColorID[2]++;

I call this function before drawing cube. I did a swapbuffers and saw that each block really has unique color.
This color snippet is called in every frame, like drawing all the blocks. It's also called each frame.

I invented an algorithm that is like this:


These are my std::map functions..

typedef long int RGB; // RGB will hold unique colors (packed RGB values)
typedef long int cardinal;

map<RGB, long int> colorCoordBlock;
map<long int, RGB> coordColorBlock;


void Init_Helper()
{
coordColorBlock.clear();
colorCoordBlock.clear();
};

// add cube to std::map
// block color will be the key and it's position (x,y,z packed) will be the value
void AddBlockRGBPos(cardinal rgb, cardinal blockPos)
{
colorCoordBlock[rgb] = blockPos;
coordColorBlock[blockPos] = rgb;
};

// returns cube position (packed x,y,z) with that (rgb) color value
cardinal GetBlockByRGB(cardinal rgb)
{
return colorCoordBlock[rgb];
};

// returns RGB color value by cube position
cardinal GetRGBByPos(cardinal blockPos)
{
return coordColorBlock[blockPos];
};



1) in each frame generate unique color for cube
2) draw each cube with this unique color
3) add this cube with unique color to std::map, with value as: current cube position (packed x,y,z)
4) on_mouse_click() do a glReadPixels() and read pixel value from current mouse_x, mouse_y
5) Do a GetBlockByRGB() and you will get coordinate of the current block which is under the cursor.
6) with face names and thanks to glLoadName, i know which face is clicked and i know exact cube position of which face was just clicked.


This eventually works pretty fine. But the problem is that: it needs double click on cubes to see the results.
I must double click on every cube. I don't understand why.

Probably my std::map function doesn't get some initial rgb values or something and after second drawing frame with unique colors, it get's the values and
works. Then i click another block and same thing.

If i click once on cube at X 1, Y 1, Z2 and then just click on the next block at: X 2, Y 1, Z2 then the block at
X 1, Y 1, Z2 will be actually updated. Now if i click anywhere else, then the cube at X 1, Y 2, Z2 will be modified.

It works like one block (cube) behind always.

I hope you guys understand. Maybe somebody even knows what i could do differently?
Why i need to double click on cubes?


Also, maybe i don't need to generate unique colors in every frame?
Could this be done differently? Just once? But how?


I can use std::maps, vectors, anything. Just would like to make it better and fix the double-click bug.

TIA.

Share this post


Link to post
Share on other sites
Advertisement
After few hours of messing around with it and experimenting, i even decided to drop color picking and just use glLoadName() and such things with std::map.
Just must make the hierarchical naming to work and im in "heaven" with face picking. I at least hope that.


I assigned unique name for each cube (simple algo: "Some_Constant + Packed_XYZ" as a result i have unique cube name) and used same std::map methods.
Works with single click and it's very precise.
Now all i have to do is assign names to block faces also, like before with glPushName or whatever and it should work fine.

Few good tuts:
http://www.lighthous...opengl/picking/
http://fly.cc.fer.hr.../chapter12.html

Share this post


Link to post
Share on other sites
You should investigate a little in ray picking. It's an alternative to color picking.

You can create a ray pointing out from your camera whenever you click your mouse. You can then use this ray for intersection tests against the triangles that make up your cubes. If you do this you got access to which object got clicked, which triangle got clicked, where exactly on the triangle you clicked (might be handy if you want to know it's uv or something) and the distance to the point you clicked on.

Share this post


Link to post
Share on other sites
Murdocki, that's all good, but if this method doesn't return me any coordinates where the click with mouse was done, then it's completely useless for me.
In my case, coordinates of cubes (blocks) are key for editing the map. Without coordinates i can't do anything.

And my 3D math is very poor to write anything working. I have one code example, that does some math with picking to figure out face which was clicked.
But it's in French and even with translation i don't understand what he is doing. Because of my poor 3D math knowledge.

Share this post


Link to post
Share on other sites
Not quite sure what you mean by coordinates where you clicked. In your step four you got cursor coordinates. rayOrigin + rayDirection * distance = your world position if this is the space you are doing your calculations in. The method returns a boolean whether or not a triangle was hit, you know which triangle belongs to which cube so you could also get the cube's position.

There's an example in the directx sdk. You can skip all the directx code and just only use the picking example presented there.

Share this post


Link to post
Share on other sites
Ok, no prb. I will take a look, maybe im wrong.

But with name buffer stuff i just was too lazy to mess with name hierarchy and decided to pack 2 things into one "unsigned long int".
The packed cube x,y,z position and name of current cube face. A bit of bit manipulation and << and | operators and i have them in one place.
If i need XYZ i can unpack it easily, if i need face name, i can unpack it also...

And this all works just perfectly. Unless i run out of GL_STACK space for name selection, im satisfied.


I wonder why OpenGL returns me 0 always with: glGetIntegerv(GL_MAX_NAME_STACK_DEPTH, &i);
Almost everybody has this problem, if i Google. Maybe it's my ATI "again".

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!