Help with DirectGUI coords/positioning

Hello! I have just started using Panda3D a week ago and for the most part find it a joy to use. However, I am at a complete loss to understand how the DirectGUI positioning and scaling works!

In short: I am looking for someone to explain the coordinates and how they are determined (along with scaling).

Here’s some details of why it’s causing me confusion:
I have the window set to 1280x800 in the config prc.

Example 1: OnscreenImage

  • I created an image 1280x400, with the top half transparent (PNG).
  • If I load it up with scale=1,1,1 it’s half the screen wide and all the screen tall (the non-transparent bottom half takes up the bottom half of the screen instead of the bottom 1/4 that I was expecting)

Example 2: DirectEntry
I am trying to place a DirectEntry widget in the bottom of the screen (so the bottom-left edge of the entry widget is attached to the bottom left corner of the screen) – how?

(Side issue: DirectEntry(pos=(x,y,z)), the pos wants a three-tuple instead of a (x,y) … confusing!

To help myself, I tried outputting the widget positions as I moved it around (I print out str(getPos())).

  • As I move it around the third coord is what moves according to the y-axis? This is like (x, z, y)? But if I set the pos as (-1.0, 0, -1.0), it won’t show where it will if I set it (-1.0, -1.0, 0)

  • I looked in the Python Chat example and the background image is scaled at (1.707, 0, 1.707) while the OnscreenText widgets are scaled at 0.5. How did those values get arrived at?

Basically, as you can probably tell, I hope I am missing the piece to the puzzle that allows me to understand how position and scale and the screen size all fit together. I am just trying to put something XxY in size at position (x,y) on the screen (no stretching, shrinking!)…

Any help would be appreciated. Thanks!

By default widgets are positioned using the center of their bounding frame. I know for onscreen text you can change the align to left, center, right, I’m not sure for widgets… 0,0 is the center of the screen, if your screen was square then the screen coordinates would go from -1, 1 horizontally and -1, 1 vertically. Since your screen is not it may be different.

Yes I know how you feel it puzzled me the first time too, think of the aspect2d layer still being part of the 3d scene just on the x,z plane. you may notice that by default the camera of the 3d scene is looking down the positive y axis, this is one reason why I think the 2D layer is x,z another is that typically in 3D scenes the z axis is up/down. So yes for pos=(x,y,z) x is horizontal position and z is vertical AFIK y doesn’t do much.

just look at the size scaled at 1 and see if it is too big. Most gui objects are huge at 1x scale

Thanks for your feedback. I’ll definitely be playing with more. I know my post was a bit jumbled but I’m not finding any consistency in the way the GUI library acts.

It seems nearly impossible to just draw a window say 200x300 pixels and have it displayed in the corner of the window, no stretching but 1:1. Same with fonts. I can’t see actually designing a real UI with this weird stretching/scaling method…

Anyway, if others have more insight on how the scaling works and positioning, feel free to post here. As I learn more I’ll post more, too, since I can’t believe I’m the only one having this difficulty!

It’s actually quite simple to do this, but I certainly agree it’s not at all obvious. The easiest way is to use egg-texture-cards to generate a card of the appropriate size.

Say your window is 1280x800, and you want to display a bitmap of size 200x300 in the exact center of the screen. Paint your image file of size 200x300, let’s call it image.png, then use the command:

egg-texture-cards -o image.egg -p 400,400 image.png

Then, to display it, use:

label = DirectLabel(geom = 'image.egg', relief = None)

If you want to put it in the lower left, you could use:

egg-texture-cards -o image.egg -p 400,400 -g 0,1,0,1 image.png
label = DirectLabel(geom = 'image.egg', relief = None, parent = base.a2dBottomLeft)

This is assuming you don’t want to do any math. If you don’t mind doing the arithmetic yourself, you can avoid the egg-texture-cards trick, and do something like:

egg-texture-cards -o image.egg image.png
label = DirectLabel(geom = 'image.egg', relief = None, scale = (200.0*2.0/1280.0, 1, 300.0*2.0/800.0))

The high-level answer about what’s going on: there are two scene graph roots for drawing gui. The top level, render2d, is always scaled in the range -1 … 1 in both X and Y. When the window is wider than it is tall, this means that objects parented to render2d are stretched horizontally. To avoid this, we have aspect2d, which is scaled in the range -1 … 1 along the shortest part of the window, usually Y, and whatever the proportional units are in X. This means in a 1280x800 window, aspect2d ranges from -1.6 … 1.6 in X and -1 … 1 in Y.

For more about what the funny parameters in egg-texture-cards mean, try consulting the manual, or try egg-texture-cards -h.

Why is it all so complicated? Panda3D is a 3-D engine, not a 2-D engine. It works natively in 3-D units, not in pixels. OpenGL is not pixel-accurate by definition. In fact, the low-level OpenGL (or DirectX) renderer really doesn’t know how big your window is at the time it’s issuing drawing commands, so it’s not possible to scale things automatically to be 1-to-1 pixel accurate, at least not at the very low level.

David

Thanks, David!

I figure it’s just the way I’m looking at it and expecting.

I will definitely look into the egg-texture-card, that seems to be exactly what I’m looking for. I’ll post this weekend if I have any other questions.

Thanks for the information and writeup!

Is the egg-texture-cards application windows only or do you know if you can build it on OS X?

Another question on scaling.

  1. Since I don’t have the egg-texture program on OS X, I am using the image keyword to load in images in-game instead of an egg. While not as good a solution, should be fine for testing the concepts, etc.
label = DirectLabel(
            image='gui/window.png',
            relief=None,
            image_scale=(200.0/1280.0, 1, 300.0/800.0))

This works great except for one thing: the x-axis is squished! I tried a 128x128 texture also (and yes, I do have texture-power-2 off which does work) to make sure and the same thing.

Basically the 200x300 on-screen is 125x300 and 128x128 is 80x128. This is a factor of 0.625. The screen is definitely at the correct resolution (1280). Any clues as to what’s causing it to be off?

(Changing the x-axis portion of scale to “200.0/1280.0*1.6” does make it correct)

  1. Is there a way to get the window’s resolution on run-time?

Thanks again for your help!

this is for onscreenimage though :
discourse.panda3d.org/viewtopic.php?t=4151

This is the method I use to set the position and scale of DirectGUI objects

Add the line below to your config.prc file (which is in my C:\Panda3D-1.5.2\etc folder).
direct-gui-edit 1

Add this line in your init function.

            self.accept("p", self.getWidgetTransformsF)  

Add these lines below your init function to make a new function

    def getWidgetTransformsF(self):
                  for child in aspect2d.getChildrenAsList():
                     print child, "  position = ", child.getPos()  
                     print child, "  scale = ", child.getScale()  

While running your py file, change the position of the objects by middle mouse clicking and dragging them and change their scale by Ctrl + middle mouse clicking and dragging them.

Hit the P key to print out your positions and scales.

##############################

If anyone could improve this that would be great.

###1
I wanted to only print out the ones that were changed. I created a function:

    def getWidgetTransforms111F(self):
                    for child in aspect2d.getChildrenAsList():
                        self.child1 = child

I called it at the the end of the init function to get the original positions.

I then wrote

   def getWidgetTransformsF(self):
                 for child in aspect2d.getChildrenAsList():
                       if child.getPos() != self.child1.getPos()
                            print child           # for testing
                            print self.child1   # for testing
                            print child, "  position = ", child.getPos()  

and called it with

            self.accept("p", self.getWidgetTransformsF)  

and noticed that the names where not equal. I’m guessing the order in getChildrenAsList() is changing.

###2
I wanted to print out the names I gave the Direct objects but I’m getting lines like the one below:
render2d/aspect2d/DirectCheckButton-pg1

If you like it that way :
discourse.panda3d.org/viewtopic.php?t=2642

I downloaded the two zipped folders. I then copied the scripts into the =A2Deditor folder. Now, how do I incorporate this into a py file that I am writing?

Do I place my py file into this folder also and include

import World

in my file?

And am I right in assuming that

A2Ded.save( aspect2d, 123 )

will tagg all my GUI objects (123 -1, 123-2, …)

This is because the default parent of DirectLabel is aspect2d, which already compensates for the nonequal scales of x and y. But you are compensating for it again, which means it gets compensated twice. You can either set parent = render2d, and use the scales you have, or you can leave the parent at aspect2d, and scale both x and y by the height of the window, rather than scaling x by the width and y by the height.

Note that egg-texture-cards certainly builds on OS X. It should be provided as part of the OS X build, but I don’t know anything about that particular package.

David

@Brian :
just import the editor class and instantiate it as in world.py
I wouldn’t suggest to save the entire aspect2d, because it relies on static children order. If someday you move a node under other parent, its tag would be different, depends on the new parent. I suggest to save() each group that you know its children order won’t change, so it’s possible to move that group altogether across different parents.