Skip to content

Conversation

@Milotrince
Copy link
Contributor

@Milotrince Milotrince commented Nov 17, 2025

Description

nice-to-have feature, allows users to integrate interactive viewer plugins.

  • Remove dependency on pynput
  • gs.vis.keybindings.Keybind callback registration
  • in gs.options.ViewerOptions:
    • disable_keyboard_shortcuts replaced with disable_default_keybinds
    • add disable_help_text

Related Issue

Resolves #713

Motivation and Context

  • ViewerPlugin enables directly interfacing with pyviewer mouse/keyboard controls.
  • Replaces pynput
  • Simple keybind callback registration

How Has This Been / Can This Be Tested?

added tests/test_viewer.py

Screenshots (if appropriate):

Screenshot 2026-01-30 at 16 08 08 registered keys automatically show in help text :)

Checklist:

  • I read the CONTRIBUTING document.
  • I followed the Submitting Code Changes section of CONTRIBUTING document.
  • I tagged the title correctly (including BUG FIX/FEATURE/MISC/BREAKING)
  • I updated the documentation accordingly or no change is needed.
  • I tested my changes and added instructions on how to test it for reviewers.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

@duburcqa
Copy link
Collaborator

This feature looks very convenient!

@Milotrince
Copy link
Contributor Author

  • Should we keep under genesis/ext/pyrender/interaction/plugins/ or genesis/vis/plugins ?
  • Do we want to use Vec3, Quat, Color, Ray, etc. classes in the codebase?
  • now that pyrender viewer mouse/keyboard interaction is exposed, we can get rid of pynput in examples

@duburcqa
Copy link
Collaborator

Should we keep under genesis/ext/pyrender/interaction/plugins/ or genesis/vis/plugins ?

Do you consider this as a "universal" plugin or something pyrender-specific? This would give you the answer :)

Do we want to use Vec3, Quat, Color, Ray, etc. classes in the codebase?

I'm not a fan of these objects. Overly complicated and not very pythonic in my view.

now that pyrender viewer mouse/keyboard interaction is exposed, we can get rid of pynput in examples

That would be great! But we need a way to expose this in a clean way.

@Milotrince Milotrince force-pushed the viewer_plugin branch 2 times, most recently from 98601bb to 1e672b0 Compare November 24, 2025 21:42
@duburcqa
Copy link
Collaborator

duburcqa commented Jan 1, 2026

@Milotrince If you could finish this PR I would be happy to review it and merge it :)

@Milotrince Milotrince marked this pull request as ready for review January 11, 2026 04:56
@Milotrince
Copy link
Contributor Author

Milotrince commented Jan 11, 2026

@duburcqa Could I get your opinion on the state of the PR? there are some design decisions I'd like your input on!

  • removed pynput, see examples/* changes for new usage
  • I kept the mouse spring interaction implementation and moved it as a plugin
  • there existed a register key system in pyrender viewer but was not exposed in genesis viewer. I made a Keybind/Keybindings class and exposed register_keybind to viewer

I still need to add tests.

@duburcqa
Copy link
Collaborator

Could I get your opinion on the state of the PR? there are some design decisions I'd like your input on!

Sure ! I will do it this week ! Could you fix the conflicts in the meantime? Thank you :)

@Milotrince Milotrince force-pushed the viewer_plugin branch 2 times, most recently from 7639573 to 0a14941 Compare January 13, 2026 04:17
@duburcqa
Copy link
Collaborator

@duburcqa
Copy link
Collaborator

Could you split the introduction of ViewerPlugin feature, with the ability to interactively add markers on geometries from the viewer? Because this PR is getting quite bug already and both features are quite orthogonal. It would make my life easier for the review.

You can open a (stacked) PR for the second part right away, merging on top of this branch instead of main (with draft status).

@Milotrince
Copy link
Contributor Author

Could you split the introduction of ViewerPlugin feature, with the ability to interactively add markers on geometries from the viewer? Because this PR is getting quite bug already and both features are quite orthogonal. It would make my life easier for the review.

Ok, I removed the mouse_spring and mesh_point_selector plugins. I am keeping the base ViewerPlugin since I interpreted the default viewer controls as a kind of ViewerPlugin. To disable the default viewer key interactions, one would specify gs.options.ViewerOptions(viewer_plugin=None) (as opposed to the default gs.options.ViewerOptions(viewer_plugin=DefaultControlsPlugin)). If this seems strange we can put the default controls back onto Viewer.

You can open a (stacked) PR for the second part right away, merging on top of this branch instead of main (with draft status).

I can't directly open a stacked PR on the Genesis repo since the parent branch lives on my forked repo. I opened one on my fork for sake of preview https://github.com/Milotrince/Genesis/pull/3/changes

@YilingQiao
Copy link
Collaborator

YilingQiao commented Jan 24, 2026

Thank you for the PR! After addressing those comments, I think I'm good with this PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you validate that it works fine for all possible user arguments that may be impacted by your changes?

    parser.add_argument("--ipc", action="store_true", default=False, help="Enable IPC coupling")
    parser.add_argument(
        "--mode",
        type=str,
        default="playback",
        choices=["interactive", "record", "playback"],
        help="Mode: interactive, record (save trajectory), or playback (replay trajectory, default)",
    )
    parser.add_argument(
        "--trajectory",
        type=str,
        default="grasp_cloth1.csv",
        help="Trajectory file to load (for playback mode, default: grasp_cloth1.csv)",
    )
    parser.add_argument("--list", action="store_true", help="List available trajectories and exit")
    parser.add_argument("-v", "--vis", action="store_true", default=False, help="Show Genesis viewer")
    parser.add_argument("--vis_ipc", action="store_true", default=False, help="Show IPC GUI")

@duburcqa
Copy link
Collaborator

Could I get your opinion on the state of the PR? there are some design decisions I'd like your input on!

Overall, I really like what you are doing with this PR. I think it is going in the right direction and is more future proof than what we currently have.

@duburcqa
Copy link
Collaborator

To disable the default viewer key interactions, one would specify gs.options.ViewerOptions(viewer_plugin=None) (as opposed to the default gs.options.ViewerOptions(viewer_plugin=DefaultControlsPlugin)).

Overall, this is a good idea I think. But it could be nice to provide a list of plugins, and make it empty to disable them all, no? Moreover, I would recommend to keep the options disable_keyboard_shortcuts and enable_interaction for convenience, but make them append/remove plugins from the list under the hood.

@Milotrince
Copy link
Contributor Author

Overall, this is a good idea I think. But it could be nice to provide a list of plugins, and make it empty to disable them all, no? Moreover, I would recommend to keep the options disable_keyboard_shortcuts and enable_interaction for convenience, but make them append/remove plugins from the list under the hood.

ah yeah i think a list of ViewerPlugin objects would make more sense. Which do we prefer for the usage, the options/registration pattern or scene.add_viewer_plugin(ViewerPlugin()), @duburcqa @YilingQiao ?

@duburcqa
Copy link
Collaborator

What does add_viewer_plugin exactly? Only register or enable a given plugin?

@Milotrince
Copy link
Contributor Author

Milotrince commented Jan 27, 2026

What does add_viewer_plugin exactly? Only register or enable a given plugin?

how about this: Viewer stores a list of plugins, e.g. [DefaultControlsPlugin(), HelpTextPlugin()], calling add_viewer_plugin(CustomPlugin) enables new plugin by adding it to the list [DefaultControlsPlugin(), HelpTextPlugin(), CustomPlugin()]. The default ones can be disabled with option flags disable_default_keybinds and disable_help_text

@duburcqa
Copy link
Collaborator

duburcqa commented Jan 28, 2026

how about this: Viewer stores a list of plugins, e.g [...]

I think this mechanism is more natural and less disruptive compared to what we have now. What do you think @Milotrince ?

@duburcqa
Copy link
Collaborator

@Milotrince CI is failing!

@Milotrince
Copy link
Contributor Author

Milotrince commented Jan 30, 2026

updated snapshot pr: https://huggingface.co/datasets/Genesis-Intelligence/snapshots/discussions/7
posting the "stacked pr" here again (raycaster changes moved here) Milotrince#3

Comment on lines +5 to +31
class _LazyPygletKeyModule:
"""
Lazy load pyglet to avoid premature initialization.

Note
----
Importing pyglet before OpenGL context is created can lead to segmentation faults on some platforms.
"""

_module = None

def __getattr__(self, name):
if type(self)._module is None:
from pyglet.window import key as pyglet_key

type(self)._module = pyglet_key
return getattr(type(self)._module, name)

def __dir__(self):
if type(self)._module is None:
from pyglet.window import key as pyglet_key

type(self)._module = pyglet_key
return dir(type(self)._module)


Key = _LazyPygletKeyModule()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend to just from pyglet.window import key as pyglet_key on top of every single method that is calling pyglet_key. Another option would be to make pyglet_key an attribute of KeyBindings class at init (via self.pyglet_key = pyglet_key).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I see why you need this. Because this part is user facing... What about exposing our own enum class for keys, that we will internally convert to pyglet. Breaking pyglet encapsulation by asking users to pass pyglet enum is problematic. First, we do not want to get struck with pyglet backend, in the future we may extend this to more backends. Second, the users may try to import pyglet themselves, which would cause segfault.

Comment on lines +48 to +50
from genesis.vis.viewer_plugins import DefaultControlsPlugin

self._viewer_plugins.append(DefaultControlsPlugin())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it necessary to delay import?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just merge your snapshot PR (probably a bit too fast but it is not a big deal). Did you remove old now relevant screenshots if any?

Disabling shadows is not a good unit test, because it is not enabled in the first place if no GPU is detected. Your wireframe and disable shadow systematically (see other unit tests doing this).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Capture keyboard commands

3 participants