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.

Related Issue

N/A

Motivation and Context

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

How Has This Been / Can This Be Tested?

added tests/test_viewer.py

Screenshots (if appropriate):

Screen.Recording.2025-11-17.at.18.03.51.mov

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 :)

@duburcqa
Copy link
Collaborator

Comment on lines +81 to +82
# Register keybindings
from pyglet.window import key
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you add from pyglet.window import key in your new keybindings module, so that users can do:

from genesis.ext.pyrender.interaction.keybindings import key

This would avoid breaking encapsulation of pyglet, that should be an implementation details for users.

Similarly, it would be great to expose your keybindings module outside ext, that is also supposed to be an implementation details for the end user. This would be relaying not the same mechanism I just mentioned. Ideally the user should be able to replace genesis.ext.pyrender.interaction.keybindings by genesis.vis.keybindings

@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).

Comment on lines +31 to +32
self.rpms = [self.thrust] * 4
self.rpms = np.clip(self.rpms, 0, 25000)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are you sure about your formula?

This is a bit dirty. It would be more clean to allocate rpm once (ndarray) and update its value by reference. By the way, there is no need to clip self.thrust? Here you keep incrementing something that is out of range, which is weird, because the user won't be able to decelerate for a while if accelerating too much beyond the limit.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Assuming the formula is correct:

self.thrust = min(max(self.thrust + self.thrust_delta, 0), 25000)
self.rpms[:] = self.thrust

Copy link
Collaborator

Choose a reason for hiding this comment

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

Similarly, it is really relevant to store both thrust and rpms if they are both the same but expended? Am i missing something?

Comment on lines +104 to +111
scene.viewer.register_keybinds(
*direction_keybinds(key.UP, "move_forward", (1.0, 1.0, -1.0, -1.0)),
*direction_keybinds(key.DOWN, "move_backward", (-1.0, -1.0, 1.0, 1.0)),
*direction_keybinds(key.LEFT, "move_left", (-1.0, 1.0, -1.0, 1.0)),
*direction_keybinds(key.RIGHT, "move_right", (1.0, -1.0, 1.0, -1.0)),
Keybind(key.SPACE, KeyAction.HOLD, name="accelerate", callback=controller.accelerate),
Keybind(key.LSHIFT, KeyAction.HOLD, name="decelerate", callback=controller.decelerate),
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

it would be nice to have a way to detect conflicting key bindings with the default viewer controls, then raise an exception if any, or just a warning eventually.

Comment on lines +156 to +157
target_pos = [robot_init_pos.copy()] # Use list for mutability in closures
target_R = [robot_init_R] # Use list for mutability in closures
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't understand. numpy arrays are already mutable. You can modify then in place. So doing this should not be necessary.

Comment on lines +176 to +177
target_pos[0] = robot_init_pos.copy()
target_R[0] = robot_init_R
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just update them in place: target_pos[:] = robot_init_pos, target_R[:] = robot_init_R


import numpy as np
from huggingface_hub import snapshot_download
from scipy.spatial.transform import Rotation as R
Copy link
Collaborator

Choose a reason for hiding this comment

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

The use of scipy should be avoided if possible, but it was already there before your PR.

Comment on lines -257 to -260
stop = False
step_count = 0

while not stop:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why the stop mechanism had to be removed in favor of try-except?

Comment on lines +144 to +178
def move_forward():
target_pos[0] += KEY_DPOS

def move_backward():
target_pos[0] -= KEY_DPOS

def move_right():
target_pos[1] -= KEY_DPOS

def move_left():
target_pos[1] += KEY_DPOS

def move_down():
target_pos[2] -= KEY_DPOS

def move_up():
target_pos[2] += KEY_DPOS

def roll_ccw():
target_euler[0] += KEY_DANGLE

def roll_cw():
target_euler[0] -= KEY_DANGLE

def pitch_up():
target_euler[1] += KEY_DANGLE

def pitch_down():
target_euler[1] -= KEY_DANGLE

def yaw_ccw():
target_euler[2] += KEY_DANGLE

def yaw_cw():
target_euler[2] -= KEY_DANGLE
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is very verbose for little benefit. What about only exposing translate, rotate, with some "axis: int, is_negative: bool" argument or just axis: np.ndarray, and use partial to keybind them? That would be more flexible and simpler I think.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Could we move this feature in a separate PR?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Same.

target_entity.set_qpos(np.concatenate([target_pos, target_quat]))
# Initialize target pose
target_pos = robot_init_pos.copy()
target_R = [robot_init_R] # Use list to make it mutable in closures
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same as before. No need to use list encapsulation. Just modify in-place the pre-allocated array.


# Robot teleoperation callback functions
def move(dpos: tuple[float, float, float]):
global target_pos
Copy link
Collaborator

Choose a reason for hiding this comment

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

Remove this global line entirely. It is not needed and probably confusing for users.

ray_direction_world = ti_normalize(ti_transform_by_quat(ray_dir_local, link_quat), gs.EPS)

# --- 2. BVH Traversal ---
# FIXME: this duplicates the logic in LBVH.query() which also does traversal
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why FIXME disappeared? You actually implemented the fix?!

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.

2 participants