🎑 查看器交互#
Genesis viewer支持鼠标和键盘交互,用于相机控制、录制、可视化切换等功能。 此功能可以通过自定义按键绑定和查看器插件轻松扩展。
添加按键绑定#
在 scene.build() 之前使用 scene.viewer.register_keybinds(...) 注册按键绑定。
import genesis.vis.keybindings as kb
...
is_running = True
def stop():
global is_running
is_running = False
scene.viewer.register_keybinds(
kb.Keybind("greetings", kb.Key.G, kb.KeyAction.PRESS, callback=lambda: print("Hello!")),
kb.Keybind("quit", kb.Key.ESCAPE, kb.KeyAction.PRESS, callback=stop),
# 添加任意数量的按键绑定!
)
scene.build()
while is_running:
scene.step()
注册的按键绑定会显示在查看器的说明覆盖层中,以便用户一目了然地发现控制方式。
要在创建场景时禁用默认查看器控件和/或隐藏帮助文本,请设置相应的选项以便于使用:
scene = gs.Scene(
viewer_options=gs.options.ViewerOptions(
enable_help_text=False, # 隐藏说明文本
enable_default_keybinds=False, # 禁用默认查看器快捷键
),
)
查看器插件#
查看器插件通过自定义输入处理和可视化来扩展交互式场景查看器。 它们接收鼠标和键盘事件,可以在每帧绘制调试几何体,并在每个模拟步骤运行逻辑——非常适合用于在网格上拾取点或用鼠标拖动刚体等工具。
在 scene.build() 之前使用 scene.viewer.add_plugin(plugin) 添加插件。
示例脚本可在 examples/viewer_plugin/ 下找到。
鼠标交互插件#
MouseInteractionPlugin 允许您点击并拖动场景中的刚体。
您可以通过直接设置刚体位置(默认)来移动刚体,或施加弹簧力以获得更真实的物理效果。
完整示例脚本可在 examples/viewer_plugin/mouse_interaction.py 找到。
使用 --use_force 标志启用弹簧力代替位置控制。
scene.viewer.add_plugin(
gs.vis.viewer_plugins.MouseInteractionPlugin(
use_force=True, # False = 设置位置, True = 弹簧力
spring_const=1000.0,
color=(0.1, 0.6, 0.8, 0.6),
)
)
自定义插件#
您可以通过继承 ViewerPlugin(如果需要屏幕到世界的光线投射,则继承 RaycasterViewerPlugin)来实现自定义插件,并使用 scene.viewer.add_plugin() 添加它们。
ViewerPlugin 基类#
自定义插件继承 gs.vis.viewer_plugins.ViewerPlugin。
场景构建后,查看器会调用 build(viewer, camera, scene);请重写此方法以存储引用并设置状态。
事件钩子返回 EVENT_HANDLED(或 True)表示事件已被消耗,或返回 None 以让其他插件或默认查看器处理。
方法 |
描述 |
|---|---|
|
鼠标移动 |
|
按住按钮拖动鼠标 |
|
鼠标按钮按下(在初始按下时调用一次) |
|
鼠标按钮释放 |
|
鼠标滚轮 |
|
键盘按键按下 |
|
键盘按键释放 |
|
窗口大小改变 |
|
每次 |
|
每帧调用,用于自定义绘制 |
|
查看器关闭时调用 |
from genesis.vis.viewer_plugins import ViewerPlugin, EVENT_HANDLED, EVENT_HANDLE_STATE
class MyPlugin(ViewerPlugin):
def build(self, viewer, camera, scene):
super().build(viewer, camera, scene)
# self.viewer, self.camera, self.scene 已设置
def on_key_press(self, symbol: int, modifiers: int) -> EVENT_HANDLE_STATE:
if symbol == ord("x"):
# 执行某些操作
return EVENT_HANDLED
return None
def on_draw(self) -> None:
# 例如通过 self.scene.draw_debug_*() 绘制调试几何体
pass
RaycasterViewerPlugin 和屏幕空间光线#
对于点击选择或 3D 拖动行为,您需要从相机穿过鼠标位置的光线。
继承 RaycasterViewerPlugin 而不是 ViewerPlugin;它维护一个光线投射器并提供 _screen_position_to_ray(x, y),返回世界坐标系中的 Ray(原点和方向)。
RaycasterViewerPlugin 还会重写 update_on_sim_step(),以便光线投射器在每个步骤与场景保持同步。
from genesis.vis.viewer_plugins import RaycasterViewerPlugin, EVENT_HANDLED
class PickerPlugin(RaycasterViewerPlugin):
def on_mouse_press(self, x: int, y: int, button: int, modifiers: int):
if button != 1:
return None
ray = self._screen_position_to_ray(x, y)
hit = self._raycaster.cast(*ray)
if hit is not None and hit.geom:
link = hit.geom.link
world_pos = hit.position
# ...
return EVENT_HANDLED
return None
使用 scene.viewer.register_keybinds() 注册按键(例如退出、切换模式);这些按键绑定会显示在查看器的键盘说明覆盖层中,以便用户轻松发现它们。
示例:网格点选择器#
MeshPointSelectorPlugin 使用鼠标光线投射来选择刚体网格上的点。
点击可添加或删除点;选中的点显示为球体,可以吸附到网格。
关闭时,插件将选中的点(以链接局部坐标表示)写入 CSV 文件,这在需要获取局部位置以在实体上放置传感器时非常有用。
完整示例脚本可在 examples/viewer_plugin/mesh_point_selector.py 找到。
from genesis.vis.viewer_plugins import MeshPointSelectorPlugin
scene.viewer.add_plugin(
MeshPointSelectorPlugin(
sphere_radius=0.004,
sphere_color=(0.1, 0.3, 1.0, 1.0),
hover_color=(0.3, 0.5, 1.0, 1.0),
grid_snap=(-1.0, 0.01, 0.01), # -1 = 该轴不吸附
output_file="selected_points.csv",
)
)
scene.build()