Confused on propagation and filtering of input events through controls/to SubViewport

Godot Version

4.6

Question

Hey, guys, first time posting here. I’m working on a tool in Godot and while what I have works, after some fiddling, I’d like to understand why it only works this way. This is a cross post from Reddit and I’ll link to my previous Reddit thread, as there was some discussion there but I’m still not satisfied:

https://www.reddit.com/r/godot/comments/1rjxxdv/confused_on_propagation_and_filtering_of_input/

I have a node graph that looks like

Control_A
|_SubViewportContainer
| |_SubViewport
|   |_Node3D
|     |_Node3D
|     |_Camera3D
|     |_DirectionalLight
|     |_Meshinstance3D
|_Control_B
|_Control_C
  |_Checkbutton

The MeshInstance3D has colliders attached, as it’s actually its own scene w/ scripting. It’s set up to react to its derived Area3D’s mouse_entered, mouse_exited and input_event.

The SubViewportContainer is set up to display below Control_B and Control_C based on Godot rendering upwards through the node graph.

The SubViewport is there because this tool will be required to let the user flip back and forth via a set of tabs between at least three different 3D environments; correspondingly it’s set up with its own custom World3D.

Control_B is acting as a layer that takes mouse input to move the Camera3D and draw a selection box. It has custom scripting using _input(). It’s actually its own scene with a ColorRect inside it, as it’ll be reused twice in this tool.

Control_C is the preliminary form of a GUI layer, currently only holding a checkbutton rigged to switch the Camera3D between perspective and ortho. It has no scripting, and its child checkbutton is simply rigged via a signal to a function in the Camera3D’s script.

Now, I have the SubViewport’s ObjectPicking toggled on, obviously, as I want to be able to mouse-select meshes in the SubViewport, after the GUI and selection box Controls (_C and _B) are done with mouse input.

Previously what I tried, after reading through the documentation, was having their mouse filters set to “Pass”, as that seemed to make logical sense - the input should be propagated up from the bottom of the tree and eventually arrive in SubViewportContainer as long as nothing ever called Viewport.set_input_as_handled() or Control.accept_event(), right?

However, that didn’t seem to work. Events percolated through the controls themselves just fine - I could both click the Checkbutton in _C and draw out the selection box in _B - but it was as though events weren’t making it through to the SubViewport for me to be able to detect mousovers or click events on the Meshinstance.

Switching _B and _C to invisible made detection of said events suddenly work properly. Obviously this was not acceptable, but coupled with the fact that it still allowed events to trigger _B to move the camera, so I know events were passing through _B to interact with the SubViewport and finally the Meshinstance3D, it showed that the issue wasn’t a bug in the Meshinstance3D scripting.

I tested to see if the issue was isolated to a specific Control, switching one to visible and the other to invisible, to no avail - only one needed to be visible to block the input, it didn’t matter which.

So I took to searching around online, and finally found this:

https://forum.godotengine.org/t/subviewportcontainer-blocks-mouse-input/98499/2

It’s not quite the same, but I tried switching the mouse filter on Controls _B and _C to “Ignore” and what do you know, suddenly I have GUI visibility, selection box, camera control AND object picking.

The thing that baffles me is, why does it work this way? As far as I can tell “Ignore” should cause the input events to skip Control_B and Control_C and any children they have entirely. And why does “Pass” seem to block inputs from reaching the SubViewportContainer?? It all seems kinda counterintuitive, or at least poorly explained in the tooltips and documentation.

I’d like to understand this fully, as in these cases not understanding a system means not only do you not understand why it doesn’t work, but if it does work you don’t understand why and how to keep it working, and I don’t want any surprises here; I certainly don’t want to be relying on an accidental fluke state for program functionality. As mentioned, I already posted this on Reddit, and I feel like I got a partial lead, but I’d like to really understand if this is a documentation issue or a bug, or just me being boneheaded. It especially doesn’t seem to make sense to me since, given what the mouse filter tooltip says about “Ignore” would appear to make any Control that needs to use _gui_input() incompatible with an underlying SubViewportContainer? This is especially concerning since I suspect I’m going to have to move some code in the script for Control_B to _gui_input() to make sure the box only draws within the bounds of the control.

I wrote about SubViewports here, see if this helps:

1 Like

Thanks, but sorry, I don’t think that’s got much to do with my issue, that’s for coordinates and transforms, and I’m looking for info on event propagation?

That is actually what it talks about as well.

Are you referring to the bit about having to manually push events if you’re using a SubViewport with a TextureRect instead of a SubViewportContainer?

Yes. You need to push the events yourself, but there is currently a bug that prevents some events to get passed, which makes it slightly more complicated than it should be.

I’m not using a TextureRect though, I’m using a SubViewportContainer which

does all the work for us under the hood

…and reading what you wrote that bug sounds like it only happens if you’re manually forwarding this stuff anyway:

Note: Due to (what I would personally call) a bug we can only forward InputEventMouseMotion events to the SubViewport if we sent it a NOTIFICATION_VP_MOUSE_ENTER first.

As mentioned in my initial post, my issue is that Ignore and Pass are behaving in a counterintuitive manner and one which makes it look to me like any Control having to use _gui_input() will be incompatible with an underlying SubViewportContainer, which can’t be intentional, surely.

Hmm, dunno. I have some controls in those SubViewports in SubViewportContainers and they work just fine.

Well, thank you for trying to help anyway. I think I might just enquire on the GitHub.