2D User Interface

Hi everyone! :slight_smile:

I hope you are all having a nice weekend!

Here goes my question: I am trying to do a 2D interface on a 3D world, but haven’t found any tutorial on doing it.

So I am switching a glOrtho perspective and drawing the controls on screen. Now, what’s the best and fastest way to handle user interface!?

-Should I use OpenGL picking system with PushName/PopName even though my interface is 2D? Do I have a performance penalty!?

-Or should I map the controls manually, like for example: is mouseposition.x less than A and greather than B AND is mouseposition.y less than C and greather than D ? This seems like a lot of trouble to me, as I as it’s a lot of work and I can only bound square controls (I need to work with sliding bars, etc).

Thanks in advance! :slight_smile:

P.S: are the PushName/ PopName functions the basis used for collision testing!?

My water demo has a hacked 2D OpenGL interface over a 3D scene.

Download the code here, it’s in the water chapter/section:


I don’t use picking, it’s 2D so if you just scale the mouse input to match the GUI viewport you can check for widget intersection, this is what I do and it’s quite efficient.

Hi Dorbie!
Thanks for your answer! :slight_smile:

I have already downloaded your source code, and actually you were my first inspiration before writing the post! :wink:

However I find it a little bit hard to understand the logics behind your code. I am a not a C++ programmer and even though I understand C++, some codes get me a little confused…

I think that the code that does the picking in your source is this one:

int PointInside(Button * butt, float x, float y)
	if(y > butt->ypos-1.0f && y < butt->ypos+1.0f)
		if(x > butt->xpos-butt->width*0.5f-1.0f && x < butt->xpos+butt->width*0.5f+1.0f)
			if(x > butt->xpos-butt->width*0.5f && x < butt->xpos+butt->width*0.5f)
				return TRUE;
			if((butt->xpos-butt->width*0.5f-x) * (butt->xpos-butt->width*0.5f-x) +
				(y - butt->ypos) * (y - butt->ypos) < 1.0f)
				return TRUE;
			if((butt->xpos+butt->width*0.5f-x) * (butt->xpos+butt->width*0.5f-x) +
				(y - butt->ypos) * (y - butt->ypos) < 1.0f)
				return TRUE;
	return FALSE;

However I can’t seem to get it right. This code checks if the mouse coords are in the bounds of the control, right?! So is each control mapped as it’s created with it’s height, width, and x and y start position!?

And if it does that, you have tocheck the bounds for each control you have?! So you actually have to run this sub n times each times the user clicks the mouse? That means the more controls you have, the slower it gets, right?

Although is this more efficient than picking?!

Well… I am just trying to understand your code… Please tell me if I got the right idea and correct me. And yet I can’t understand how do you handle the slide bar.

Thank you so much in advance! :slight_smile:

Yup, each widget is a rectangle capped by semicircles so that’s what you’re seeing there.

If you don’t like it you could trivially add some optimization like grouping widgets and bounds tests, but in fact most buttons are quickly rejected by the first test if you consider the layout. For example inner semicircle tests are only performed if the mouse is right over the widget and that will only happen with one button (if the layout is sane).

The slider bar just uses the mouse position to interpolate the value. This works using the input focus mode, only if the button has input focus do clicks have an effect and only if a slider has been clicked and has focus does the mouse motion generate interpolation input.

Unfortunately the code was hacked together for an OpenGL Challenge competition that was cancelled (the guy seemed to abandon his site right when I decided to code an entry :frowning: ), and it’s only in C, with no data encapsulation (e.g. structs with function pointers). Originally it was only designed to handle a quick & dirty game menu.

I’d recommend you use it in principal but make a generic button class where stuff like the internal bounds class event handling calls are members. Then you could create a container class if you want to bother and give it children to which it can pass events. It’s not that difficult, I’ve done it in the past, getting the highlighting & event handling consistent and intuitive on multiple widgets is the tricky part and I’ve done that for you. (persistent highlight after input focus was there originally to support keyboard navigation in addition to a mouse as you’d get in a game menu, I have another example that does this, in the water demo it’s just vestigial).

P.S. here’s a shot of the original textured lit GUI:

Hi Dorbie! Thanks so much for your answer!

I am still fighting with your code… :slight_smile:

Ok, I got most of your idea, but I can’t understand why you add and subtract 1.0 to the y-button position, and in some other places too.

I am referring to this two lines:

 if(y > butt->ypos-[b]1.0f[/b] && y < butt->ypos+  [b]1.0f[/b])
		if(x > butt->xpos-butt->width*0.5f-[b]1.0f[/b] && x < butt->xpos+butt->width*0.5f+[b]1.0f[/b])

You first check that the y mouse posiiton is between the button y position +/- 1. So are all your buttons of 1 of height?

Up to there, it seems clear.

But why do you add and subract 1 for the x axis too?

I guess that’s the part that checks if the mouse is on the button or not, so the next part of the code should check if the user is on the semi-circles or not. So you make two checks that do some type of test of: x^2 + y^2<1. That seams pretty clear to me, it’s the equation of a circle, so you are checking if the user is inside either semi-circle, or inside the inner rectangle.

However, I dont’ get the first code. The only explanation possible is that both radius and height of the button are equal to 1.

Is this correct?!

The +1 and -1 is there to include the semicircle endcaps because width is the inside rectangle. Tests must start inclusive to be optimal w.r.t. early rejection.

This trivial detail is hardly the point of looking at the GUI code though.

Dorbie thanks so much for the support!

I managed to create an interface of my own now! Thanks. :slight_smile: