VR 用の Python API v2 (2020.1 以降)

VR 用の Python API は、ボタンを押すなどのデバイス アクションを、Python スクリプトを介して実装される実際の機能に接続する概念に従います。API はプラットフォームに依存しないため、異なる VR コントローラに対してスクリプトを 1 回だけ作成する必要があります。唯一の例外は、入力デバイスのボタンです。あるデバイスが別のデバイスとは異なるボタンを提供する場合があります。したがって、Python スクリプトで対応するインタフェースを使用するときに注意する必要があります。

テレポートなどの VR の機能は、VRED ではデバイス インタラクションと呼ばれ、タイプは vrdDeviceInteraction です。これらのインタラクションには、ボタンを押すなどの操作を反映するタイプ vrdDeviceAction のデバイス アクションがいくつか含まれています。テレポートの場合、ターゲット アークをアクティブにするデバイス アクション、テレポートを実行する別のアクション、およびターゲット アークを非アクティブにするさらに別のアクションがあります。

図 1 - 複数のデバイス インタラクションを含むアクティブなインタラクション グループ

VRED には、インタラクション グループの概念もあります。各デバイス インタラクションはインタラクション グループの一部であり、一度にアクティブになるグループは 1 つだけです。このようなグループがアクティブな場合、含まれるすべてのインタラクションもアクティブになり、逆も同様です。各種のグループは、VR で異なるツールが選択されたときなど、ユーザ インタラクションのコンテキストをすばやく変更するのに便利な方法です。左コントローラのボタン A を押すなどの各デバイス アクションは、インタラクション グループで 1 回しか発生しないことに注意してください。これにより、複数の機能が同時にトリガされなくなり、異なるインタラクション間の副作用が回避されます。

インタラクション グループ、デバイス インタラクション、およびデバイス アクション間の関係を正しく理解するには、図 1 を参照してください。

API は、入力デバイスを表すタイプ vrdVRDevice のオブジェクトへのアクセスも提供します。これらのオブジェクトは、各デバイスの状態を読み取って管理するための追加機能を提供します。

インタフェースの使用法

VR Python インタフェースの出発点はクラス vrDeviceService です。このサービスにより、デバイス インタラクションおよび VR 入力デバイスの作成、アクセス、および管理が可能になります。

新しいデバイス インタラクションを作成する

vrDeviceService はメソッド createInteraction を提供します。このメソッドは、パラメータとして新しいインタラクションの名前を取ります。既定では、利用可能なすべてのインタラクション グループでインタラクションがサポートされます。インタラクション グループの例は「Locomotion」です。これには、テレポートやポインタなど、VRED の既定のインタラクションが含まれます。新しいインタラクションがこのグループの一部になる場合、一部のボタンと対応するアクションが既定のインタラクションで既に使用されており、それ以上使用できないことに注意してください。次のコード行は、インタラクションを作成する方法を示しています。

myInteraction = vrDeviceService.createInteraction("MyInteraction")

インタラクションはサービスを使用して作成され、「MyInteraction」という名前を取得し、現在利用可能なすべてのインタラクション グループでサポートされます。

vrdDeviceInteraction オブジェクトでは、そのメソッド createControllerAction を使用してデバイス アクションを作成できます。このメソッドは、パラメータとして実際のアクションを記述する文字列を取ります。文字列の形式は <Side>-<Element>-<Event> で、たとえば「left-trigger-pressed」のようになります。パラメータの詳細については、VRED に付属の Python マニュアル([ヘルプ] > [Python マニュアル])を参照してください。メソッドによって返される vrdDeviceAction は、次の例に示すように、アクションを Python スクリプトの関数に接続するために使用できる「triggered」という名前の信号を提供します。

def myFunction():
    print("My function is called")

myInteraction = vrDeviceService.createInteraction("MyInteraction")
myAction = myInteraction.createControllerAction("any-xa-pressed")
myAction.signal().triggered.connect(myFunction)

まず、呼び出されたことを出力する関数が実装されます。デバイス アクションは、デバイス インタラクション オブジェクトによって作成され、コントローラで X/A ボタンが押された場合に信号を送信します。最後の行は、デバイス アクションの信号オブジェクトのメンバーである「triggered」信号を、呼び出される実際の関数に接続します。

デバイス アクションが既に使用されている場合は、別のインタラクション グループにインタラクションを追加し、このグループをアクティブにする必要があります。

def myFunction(action, device):
    print("My function is called")

myInteraction = vrDeviceService.createInteraction("MyInteraction")
myInteraction.setSupportedInteractionGroups(["MyGroup"])
vrDeviceService.setActiveInteractionGroup("MyGroup")
myAction = myInteraction.createControllerAction("any-touchpad-pressed")
myAction.signal().triggered.connect(myFunction)

この例では、メソッド setSupportedInteractionGroups とのインタラクションでサポートされているインタラクション グループを追加で設定します。その後、サービスのメソッド setActiveInteractionGroup を使用して、グループがアクティブ化されます。これにより、グループ「Locomotion」で既に使用されているタッチパッドを再度割り当てることができます。アクティブなグループでデバイス アクションを複数回使用すると、未定義の動作が発生することに注意してください。VRED の既定のインタラクション グループ「Locomotion」では、次のアクションが既に使用されています。

サイド 要素 イベント
両側 Touchpad 押す
両側 Touchpad 押さない
両側 Touchpad 触れる
両側 Touchpad 触れない
両側 CustomTrigger 押す
両側 CustomTrigger 押さない
両側 CustomTrigger 触れる
両側 CustomTrigger 触れない
両側 Menu 押す
両側 Menu 離す
両側 Thumb 押す
両側 Thumb 押さない
両側 Thumb 触れる
両側 Thumb 触れない
両側 YB 押す
両側 YB 離す

vrdDeviceInteraction オブジェクトは、デバイス アクションとそれが属するインタラクション グループを管理するためのより多くの機能を提供します。詳細については、VRED の Python マニュアル([ヘルプ] > [Python マニュアル])を参照してください。VRED の例には、新しいカスタムデバイス インタラクションの実装を示すスクリプト vr/customInteraction.py も含まれています。

既定のアクションの一部を使用する必要がある複雑なインタラクションは、カスタム インタラクション グループに含めて、必要に応じてこのグループをアクティブ化することをお勧めします。これは、たとえばメニュー エントリを使用して実行できます。詳細については、「xR の[ホーム]メニュー エントリを作成する」を参照してください。また、仮想ボタンを使用して、コントローラのタッチパッドの既定 アクションを再配置することもできます。詳細については、「タッチパッドの仮想ボタンの設定」を参照してください。

既定のデバイス インタラクションに接続する

テレポートやポインタなどの既定のインタラクションは、シーン内のオブジェクト間の移動やインタラクションのための基本的な機能を既に提供しています。この機能は、関数を既定のインタラクションのアクションに接続することにより拡張できます。これを達成するための最初のステップは、vrDeviceService から既定のインタラクションを取得することです。

def printNodeName(action, device):
    node = device.pick().getNode()
    print(node.getName())

pointer = vrDeviceService.getInteraction("Pointer")
pointerExecute = pointer.getControllerAction("execute")
pointerExecute.signal().triggered.connect(printNodeName)

この例では、ユーザがポインティング レイを持つシーン内でオブジェクトを「クリック」したときにトリガされるポインタの「execute」アクションを使用します。また、ポインタには「prepare」および「abort」というアクションもあり、ターゲット レイのみをアクティブまたは非アクティブにします。vrDeviceService.getInteractions を使用して、利用可能なさまざまなインタラクションをクエリできます。各インタラクションは、VR 入力デバイスに関連する利用可能なすべてのアクションを取得するメソッド getControllerActions も提供します。スクリプト vr/connectToDeviceActionSignal.py には、既定のインタラクションに接続する方法を示す別の簡単な例が含まれています。

コントローラとトラッカーを操作する

VR の入力デバイスは、クラス vrdVRDevice で表されます。このクラスは、ボタン、位置、触覚フィードバック、およびデバイスのビジュアライゼーションへのアクセスを提供します。vrdVRDevice のすべてのメソッドの詳細については、VRED に付属の Python マニュアル([ヘルプ] > [Python マニュアル])を参照してください。

VR デバイスを取得する

デバイス オブジェクトを取得するにはいくつかの方法があります。最初の方法は、vrDeviceService メソッド getVRDevice です。

leftController = vrDeviceService.getVRDevice("left-controller")

これは、左側のコントローラを表すオブジェクトを返します。string パラメータはデバイスの名前です。左側のコントローラには常に「left-controller」という名前が付けられ、右側のコントローラには常に「right-controller」という名前が付けられます。これはハードウェアに依存せず、VRED でサポートされるすべてのコントローラで機能します。対応するコントローラが接続されるとすぐに変更が適用されることに注意してください。コントローラの利き手が変わると、VRED は「古い」左コントローラのすべての設定を「新しい」左コントローラに適用します。

getVRDevice メソッドは HTC Vive Tracker でも機能します。

tracker = vrDeviceService.getVRDevice("tracker-1")

トラッカーの名前は常に「tracker-」となり、番号が付加されます。この番号は、トラッカーが接続された順序に従って変わることに注意してください。一部のアプリケーションではこれが問題になる可能性があるため、メソッド vrDeviceService.getVRDeviceBySerialNumber を使用してデバイスをシリアル番号で識別することもできます。一部のデバイスでは、デバイスに保存されているシリアル番号は印刷されているシリアル番号と異なります。この場合、API は vrdVRDevice メソッド getSerialNumber を介したシリアル番号の読み取りをサポートします。これは、デバイスが接続されている場合にのみ有効な値を返します。サンプル スクリプト vr/printAllDeviceSerialNumbers.py は、すべてのシリアル番号を出力します。特定のシリアル番号を取得する簡単な方法は、サンプル スクリプトの実行中に一度に 1 つのデバイスのみを接続することです。

メソッド getVRDevice および getVRDeviceBySerialNumber は、デバイスが接続されていない場合でも常にオブジェクトを返します。このオブジェクトの設定は、デバイスが接続される前に行われ、対応するデバイスが接続されるとすぐに適用されます。

一部のデバイスの名前は、接続または再接続時に変更される可能性があるため、同じデバイス タイプには getVRDevice または getVRDeviceBySerialNumber のいずれかを使用する必要があります(たとえば、トラッカーまたは HTC Vive コントローラの側面の番号)。そうしないと、2 つの異なるオブジェクトが同じ物理デバイスを表し、望ましくない動作を引き起こす可能性があります。

すべてのデバイスが必要な場合、vrDeviceService はメソッド getConnectedVRDevices を提供します。このメソッドは、現在接続されているすべてのデバイスを返します。

ジオメトリを VR デバイスへ接続する

vrdVRDevice クラスは、デバイスの変換を表すノードを返すメソッド getNode を提供します。ノードは、新しいシーンでクリーンアップされないため、子ノードを直接追加する目的で使用するべきではありませんが、次の例に示すように親の制約に使用することができます。

deviceNode = myDevice.getNode()
constraint = vrConstraintService,createParentConstraint([deviceNode], myNode, False)

タッチパッドで仮想ボタンを設定する

HTC Vive のような一部のコントローラには、より多くのボタンをシミュレートするために異なる領域に分割できるタッチパッドがあります。クラス vrdVRDevice は、メソッド addVirtualButton を提供します。このメソッドは、2 つの引数を取ります。1 つはタイプ vrdVirtualTouchpadButton で、もう 1 つはそれが属する対応する非仮想ボタンの文字列です。

padTop = vrdVirtualTouchpadButton("padtop", 0.0, 1.0, 270.0, 90.0)
padBottom = vrdVirtualTouchpadButton("padbottom", 0.0, 1.0, 90.0, 270.0)

この例は、2 つの vrdVirtualTouchpadButton オブジェクトの作成から始まります。コンストラクタの最初の引数は新しいボタンの名前を取り、2 番目と 3 番目の引数はボタンが配置されている半径を示します。この場合、タッチパッドの完全な半径を使用します。4 番目と 5 番目の引数は、ボタンが配置されているタッチパッド上の角度を示します。これらの 2 つの場合、最初のボタンはタッチパッドの上半分、2 番目のボタンは下半分をカバーします。図 2 は、2 つのボタンのレイアウトを示しています。ボタンが重なっている場合、信号を送信するボタンは 1 つだけです。重複するボタンを作成しないことをお勧めします。

図 2: 円形タッチパッドの仮想ボタンのレイアウト。上部(青)に 1 つのボタン、下部(橙)に 1 つのボタン

仮想ボタンのインスタンス化の後、それらはコントローラに追加されます。

controller.addVirtualButton(self.padTop, "Touchpad")
controller.addVirtualButton(self.padBottom, "Touchpad")

メソッド vrdVRDevice.addVirtualButton は 2 つの引数を取ります。1 つは vrdVirtualTouchpadButton オブジェクトで、もう 1 つは仮想ボタンが配置される実際のボタンの名前です。VRED では、これは現在、HTC Vive コントローラの丸いタッチパッドに対してのみ機能します。

デバイス アクションを作成するときに、新しく追加された仮想ボタンを通常のボタンのように使用できるようになりました。

myAction = myInteraction.createControllerAction("left-padtop-pressed")

仮想ボタンの使用方法に関するその他の例については、スクリプト vr/virtualControllerButtons.py または vr/groupTeleport を参照してください。

VR コラボレーション セッション

コラボレーション セッションの管理には VRED Python API を使用します。これには、一般的な設定の調整だけでなく、ユーザの処理と参加者間での Python コマンドの送信が含まれます。

一般的なセッション管理

クラス vrSessionService は、VRED のコラボレーション ユーザ インタフェースでも使用可能な関数へのアクセスを提供します。これには、たとえば、セッションへの参加またはセッションからの退出、接続の確認、シーンのアップロードなどが含まれます。

設定に加えて、このサービスを使用して共同セッションでのインタラクションのロジックを実装することもできます。その場合、addSyncNodesyncNode などのメソッドを提供して、すべてのユーザのノードの変換と可視性を同期します。これは、複数のユーザがシーン内のオブジェクトを同時に操作できる必要がある場合に役立ちます。

セッション ユーザ

クラス vrSessionService は、セッションに参加する他のユーザへのアクセスも提供します。その場合、メソッド getUsergetUsers、および getRemoteUsers を使用できます。これらはそれぞれオブジェクト、タイプ vrdSessionUser のオブジェクトのリストを返します。このクラスは、ID、名前、および HMD が使用されるかどうか、どの HMD が使用されるか、などの基本的なユーザ プロパティへのアクセスを提供します。また、ユーザのアバターを構成するノードへのアクセスも提供します。これには、身体の各部位の追跡マトリックスが含まれます。

セッション ユーザの自動カラー

VRED Stream アプリを使用して各セッション ユーザに未使用のカラーを割り当てるには、vrSessionServicesetUserColor メソッドを使用します。セッション ユーザの自動カラー設定を有効にするには、userColor パラメータに NULL 値(0,0,0,0) を設定します。

Python コマンドを他のユーザへ送信する

セッション サービスは、セッション内の任意のユーザに Python コードを送信するメソッドを提供します。これは、受信者側でローカルに実行されます。

各ユーザに Python コードを送信する簡単な方法は、vrSessionService により、そのメソッド sendPython で直接提供されます。ドです。このメソッドを使用すると、セッション内のすべてのユーザが同じ Python コードを受け取り、それを実行します。これは、ユーザ固有の部分を持たないより一般的なコードを送信する必要がある場合に特に役立ちます。

vrSessionService,sendPython("print('Hello')")

これらの文字列で変数を使用する場合、受信側ユーザがローカルの VRED コンソールに入力するかのようにコードが実行されるため、変数は受信側にも存在する必要があることに注意してください。

ユーザ名を含むウェルカム メッセージなど、ユーザ固有のコードを送信する必要がある場合は、選択したユーザのみにコマンドを送信することもできます。

users = vrSessionService.getRemoteUsers()
for user in users:
    name = user.getName()
    user.sendPython("print('Hello {0}')").format`(name)`)

最初のステップでは、現在 vrSessionService.getRemoteUsers を使用してシーンに接続しているすべてのユーザを取得します。これは、それぞれのユーザを表す vrdSessionUser オブジェクトのリストを返します。この例では、ユーザを反復処理し、各ユーザ オブジェクトでメソッド getName を呼び出して名前を取得します。その後、メソッド sendPython が呼び出されて、Python コードが送信されます。このメソッドは、文字列として Python コードを取ります。

Python コードの送信時に必ずしもすべてのユーザが存在しない場合があるため、サービスは、ユーザがセッションに参加またはセッションから退出するときに送信される信号も提供します。これらの信号に関数を接続することが可能です。

def userArrived(user):
    print(user.getUserName() + " has arrived")

def userLeaves(user):
    print(user.getUserName() + " has left")
vrSessionService.userArrives.connect(userArrived)
vrSessionService.userLeaves.connect(userLeaves)

この例では、引数としてタイプ vrdSessionUser のオブジェクトをとる 2 つの関数が実装され、対応する信号に接続されます。

vrSessionService および vrdSessionUser のすべてのメソッドの詳細については、VRED の Python マニュアル([ヘルプ] > [Python マニュアル])を参照してください。

xR の[ホーム]メニュー エントリを作成する

xR の[ホーム]メニューは、カスタム エントリによって拡張できます。これは、クラス vrImmersiveUIService で新しいツールを作成することで実現できます。次の例は、単純なプッシュボタンをメニューに追加する方法と、clicked 信号に関数を接続する方法を示しています。

def hasClicked():
    print("click")

tool = vrImmersiveUiService.createTool("MyTool")

tool.setText("My Tool")

icon = QtGui.QIcon()
icon.addFile("myToolIcon.png")
tool.setIcon(icon)

tool.signal().clicked.connect(hasClicked)

没入型 UI サービスを使用して vrdImmersiveTool が作成されます。このツールには、xR の[ホーム]メニューに表示されるテキストとアイコンを設定していくつかの構成を行う必要があります。ツールには各種の信号が付属していますが、この場合、clicked 信号のみが必要で、ツールが機能していることを示すために関数に接続されます。

メニュー内のこれらの追加ツールは、単純なプッシュボタンに限定されません。トグル ボタン、Web コンテンツを含むサブメニュー、または Qt ウィジェット全体を含むサブメニューを追加することもできます。スクリプト例 vr/customMenuButton.py にはより高度な機能が示されています。

VRED に付属のサンプル

VR の例は、Python API の使用方法をより詳細に示しています。それらは VRED の Python API v2 ドキュメントにも記載されています。