a2dTopLeft, a2dTopRight, a2dBottomLeft, a2dBottomRight equivalent for base.pixel2d?

Is there a a2dTopLeft, a2dTopRight, a2dBottomLeft, a2dBottomRight equivalent for base.pixel2d?
I want to attach GUI elements to the top-right and top-left of the screen while positioning and sizing them in pixels, which pixel2d allows. I guess a custom Task can be used and the resolution and aspect ratio of the monitor checked every frame and the GUI updated, but I’m hoping there’s a more standard approach here?

The coordinates themselves are already what you describe. And in fact, you can attach a node by simply setting its position. However, when you change the window size, you really need to recalculate the position, the problem is why do it automatically when few people will find it useful and will use it, and it’s a waste of CPU time. And for that reason, it’s not there by default.

Sorry I understand neither your answer nor your argument.

Why would you want to recalculate position manually when resizing window? And how is that any conceivable waste of CPU time in 2022, or even in 2002?

This is what I want to do: basically behavior identical to any other Windows GUI program. When you resize their windows you don’t resize their icons or button sizes, they remain constant and are determined by either a global scale value inside the progam or depend on the OS DPI settings, not window resolution/size.

If you attach a node to a2dTopLeft or others but have it parented to pixel2d, it means you need to run setPos() in a Task if your window is resizeable. I’m not sure how that’s any more efficient for the CPU. (but again, I’m not sure how something this simple would be a CPU usage concern, whether you do it manually or the engine does it)

Correct me if I misunderstood, but I guess your answer is to keep repositioning the pixel2d child node to the position of a2dTopLeft, etc every frame in a Task?

If Panda provided p2dTopLeft, etc. that is exactly what Panda would be doing, having a task to position these nodes every frame. Doing this in a task in Python is so trivial that it will not have any measurable effect on your frame time, however, you actually only need to do this in a window-event handler, not in a task.

It sounds like a reasonable feature request, however, to make these additional nodes available.

Panda is a 3D engine, so coordinates are ultimately provided to the GPU in NDC coordinate space (-1 to 1) even for GUI items, that is why the coordinate systems aren’t like in other GUI frameworks. It is going to require a transformation one way or the other before reaching the GPU.

Sure I can do it manually, just wanted to make sure I’m not being redundant by doing something which already exists natively.

I don’t think it matters much if Panda3D is a 3d engine or not, with regards to determining if this should be supported or not. Tools like Unity are used for writing GUI programs with 3d features, like a 3d model or CAD viewer.

Yes, you are 99% right, but you have written more text on the forum than the code required for this task. :wink:

from direct.showbase.ShowBase import ShowBase
from panda3d.core import NodePath

class MyApp(ShowBase):

    def __init__(self):
        ShowBase.__init__(self)

        self.old_size = (0, 0)

        self.center = NodePath("center")
        self.center.reparent_to(pixel2d)

        self.p2dTopRight = NodePath("p2dTopRight")
        self.p2dTopRight.reparent_to(pixel2d)
        
        self.p2dTopLeft = NodePath("p2dTopLeft")
        self.p2dTopLeft.reparent_to(pixel2d)


        smiley = loader.load_model("smiley.egg")
        smiley.set_scale(20)
        smiley.reparent_to(self.p2dTopLeft)

        smiley = loader.load_model("smiley.egg")
        smiley.set_scale(20)
        smiley.reparent_to(self.p2dTopRight)

        smiley = loader.load_model("smiley.egg")
        smiley.set_scale(20)
        smiley.reparent_to(self.center)

        taskMgr.add(self.update, 'update')

    def update(self, task):
        size = (base.win.get_x_size(), base.win.get_y_size())
        if size != self.old_size:
            self.center.set_pos(int(base.win.get_x_size()/2), 0, -int(base.win.get_y_size()/2))
            self.p2dTopRight.set_pos(base.win.get_x_size(), 0, 0)
        self.old_size = size
        return task.cont

app = MyApp()
app.run()

But do not forget the panda is used on mini computers. And too much ShowBase functionality is unnecessary there. For example, I now use this method of creating an application.

I got a bonus that the window opens instantly, no need to wait until the ShowBase window is initialized. In some cases, the user was able to increase the rendering speed to the buffer by simply abandoning ShowBase. Although before that he tried to enable multithreaded rendering, but stumbled over bugs. So the question is moot, adding a bunch of functionality and tasks to the already large ShowBase.

Just don’t accidentally break it…

I tried using base.accept(“window-event”, myMethod) but this seems to be overwriting an existing method that Panda had set up for window-event?
The reason I suspect this is that pixel2d stops respecting aspect ratio of its children during window resizing, after I provide my own method to base.accept.

Here’s a test script to show the issue:

from panda3d.core import *
from direct.showbase.ShowBase import ShowBase
base = ShowBase()

smiley = loader.loadModel("smiley")
smiley.reparentTo(pixel2d)
smiley.setScale(300)

def myMethod(arg):
	pass

base.accept("window-event", myMethod)

base.run()

If you comment out the base.accept line and resize the window, you will notice the sphere is preserving its aspect ratio.

I think you’re already beginning to understand. Although of course it is better to fix it by expanding ShowBase with the necessary logic. This would be correct from all points of view. But I prefer to implement this on top of ShowBase.

Yes, ShowBase itself uses base.accept to catch the same event, so you override its own handler.

You can call base.windowEvent(arg) in your own handler to upcall to ShowBase’s handler. Or, catch the event on a different DirectObject.

Thank you, that works.

None of this seems to be in the documentation? Any info regarding base.windowEvent or needing to upcall it if you use base.accept(“window-event”) and base.accept(“window-event”) overwriting an existing window event in ShowBase.
There’s also no info that shows up for “WindowHandler” here: Search — Panda3D Manual

Even the term “upcalling” is new to me, and googling it it’s still confusing why it’s used here:
“(computing, programming) A call from a lower-level subsystem, such as a kernel or framework, to a higher-level subsystem, such as user code.”

This is all pretty confusing, I wouldn’t find a solution if I didn’t ask.

So don’t get me wrong, I appreciate all the support, but you should really consider just adding top-left, top-right, bottom-left, bottom-right child nodes for pixel2d, as you can see it’s not straightforward to do it yourself without breaking something else and then figuring out how to fix that, or using less efficient Task that is called every frame.