チュートリアル 5: Web インタフェースを使用して Python スクリプトによって VRED をリモートでコントロールする方法

Web インタフェースを使用して Python スクリプトによって VRED をリモートでコントロールする方法について説明します。

サンプル スクリプトをダウンロードする

サンプル スクリプトに移動する

チュートリアルの PDF をダウンロードする

ビデオ キャプション: VRED Pro の Python チュートリアルへようこそ。Christopher といいます。本日は、VRED の Web インタフェースを使用して、Python スクリプトによって VRED をリモートでコントロールする方法について説明します。

皆さんは、VRED の Web インタフェースとその使用方法については既に理解しているかもしれません。アクティブにすると、VRED は Web アプリをホストする Web サーバを起動し、ブラウザを使用して VRED をリモートでコントロールします。カメラを移動したり、ライブ ビデオ ストリームを使用したり、定義した任意のバリアント セットをトリガしたりできるため、デザイン レビューやお客様向けプレゼンテーションに最適です。

Web インタフェースを有効にするには、[基本設定]に移動して[Web インタフェース]までスクロールする必要があります。ここで、Web インタフェースを有効にすることができます。変更を適用すると、基本的にローカルホスト上で Web インタフェースを使用する準備が整います。しかし、iPad でアクセスできるようにするには、たとえば、ホスト アクセスを[制約なし]に設定するか、iPad を IP アドレスとともにホスト リストに追加する必要があります。

テストの場合、ホスト アクセスを[制約なし]に設定してもまったく問題はありませんが、セキュリティ上、常にホスト リストを使用する必要があります。同じネットワークにいる場合は、使用しているデバイスの IP アドレスを入力するだけで、Web インタフェースに接続することができます。他にもいくつかの設定がありますが、このチュートリアルではそれらのすべてを説明しません。すべての設定については Web インタフェースの基本設定に関するオンライン マニュアルを参照してください。

ここでは iPad で VRED シーンに接続していますが、指でシーンを制御し、バリアント セットを切り替えることができます。スナップショットを撮ったり、設定を変更したりすることができます。簡単なプレゼンテーションを行う場合は、これらの機能で十分でしょう。しかし、VRED ストリーミング アプリを使用すると、ターミナルで実行される Python スクリプトを直接入力することもできます。新しいシーンを作成し、シーングラフにボックスを追加するサンプルが既に用意されています。

ターミナルを使用して、シーンから任意の Python コードを実行することができます。ターミナルのサンプルのように直接 Python API を使用してジオメトリを作成するか、スクリプト エディタで定義された関数あるいは読み込まれた Python モジュールの関数など、シーンからアクセスできる関数を呼び出します。ここで、ターミナルは VRED でのスクリプト エディタと同様に動作します。入力した内容はすべて直接実行され、シーンと相互作用します。

ここまで、Python ターミナル上で既定のストリーミング アプリを使用して VRED をコントロールする方法を学習しました。カスタム Python スクリプトを呼び出すもう 1 つの便利な方法は、バリアント セットを使用することです。「Scripts」あるいは任意の名前のバリアント セット グループを作成し、実行する Python コードを含むバリアント セットを追加します。これにより、特定のアクションのボタンのためだけにカスタム インタフェースをプログラミングする必要がなくなります。既定のストリーミング アプリは既にこの機能への対応をしており、バリアント セットにメニュー エントリを作成します。

もちろん、VRED と対話できる独自の Web インタフェースを構築することもできます。この操作を行う場合、リモート アプリを完全にカスタマイズして、必要に応じてスタイルを設定できます。

まず、エンドポイントを使用して VRED と対話する方法を示します。エンドポイントは、実行する Python コードを含む Web インタフェースへの GET 要求です。ここでは、バリアント セットを選択する方法と、カスタム Web アプリ内で他の Python 関数をトリガする方法のサンプルを示します。Web インタフェースによって提供されるエンドポイントに、他のクエリ Python 関数を送信することができます。

最初のサンプルでは、「Python」エンドポイントを使用します。これにより Python コマンドが VRED に送信され、戻り値は想定されません。Python コマンドは「value」パラメータとともに提供され、URL エンコーディングする必要があります。ボタンをクリックしてメッセージをエンドポイントに送信し、ターミナルにメッセージを出力できるようになりました。バリアント セットを選択する場合も同じ方法を使用します。ここでは、シーンで定義されているバリアント セットを使用して、黒いメタリック マテリアルを選択します。

Web インタフェースからデータを返すことができるもう 1 つのエンドポイントがあります。このエンドポイントの名前は「pythoneval2」です。Python エンドポイントと同様に、コマンドを VRED に送信できますが、戻り値も管理する必要があります。この場合、すべてのビューポイントを要求して、コンソールに出力するビューポイントのアレイを受け取ります。GET 要求によって返されるデータ形式を確認する必要があります。この場合は、javascript アレイに評価できる文字列です。

カスタム Web インタフェースを構築する最新のアプローチは、オートデスクがバージョン 2021 で導入した Web API を使用することです。Web API は、Web アプリに統合された javascript アプリケーションで、Python のエンドポイントを使用しなくても、VRED の Python API に完全にアクセスすることができます。Web API は、Python API バージョン 2 のすべてのモジュールと関数をサポートします。

したがって、API バージョン 1 を呼び出す場合は、前のサンプルと同じように、提供されたエンドポイントを使用します。Web インタフェースからスクリプトを読み込むことで、Web API をアプリに追加できます。API を読み込むことができる新しい javascript モジュールを作成します。次に、注釈を追加する関数とすべての注釈を戻す関数を追加します。また、VRED に注釈を追加するたびに、信号イベントに接続します。これは簡単なセットアップで、通常は、これを本格的な Web アプリに含めます。ここで、一般的な考え方を紹介します。

関数を HTML ドキュメントで表示するには、グローバル スコープに追加する必要があります。そのためには、ウィンドウ オブジェクトに関数を追加します。HTML ドキュメントでは、タイプ モジュールとして新しい javascript ファイルにリンクします。ボタンの 1 つをクリックすると、関数を呼び出すことができます。

このサンプルから、サービスを参照して javascript 関数を呼び出すことで、Python バージョンと同様に関数を呼び出すことができます。また、Promise やエラー処理など、javascript の優れた機能を使用して、非同期に受け取るデータに対処することもできます。

このサンプルを実行するには、ローカル Web サーバを使用してファイルを提供する必要があります。これは、ローカルで実行している場合、クロスオリジン リソース ポリシーにより javascript ファイルへのアクセスができないためです。そこで、ノードと一緒にインストールしたツール HTTP サーバを使用します。ただし、任意のサーバを使用することもできます。

ローカル Web サーバが提供するアドレスでページにアクセスすることができます。ボタンをクリックすると、シーンに注釈が追加されたことがわかります。また、すべての注釈のリストを要求すると、コンソールに新しい注釈も表示されます。またオートデスクは、VRED の「examples」ディレクトリに Web アプリのサンプルを含めました。そのため、独自のカスタム Web アプリを作成する場合は、これも良い開始点になります。ストリーム js のサンプルから始めるのが一般的です。これは最も単純なサンプルです。ただし、ストリーム アプリは react.js アプリに基づいており、少し複雑です。

Web インタフェースとその Web API は、VRED との対話にまったく新しい可能性をもたらします。完全にカスタマイズされたストリーミング アプリを簡単に構築し、エンジンの上にまったく新しいツールを構築できるようになりました。

今日の説明はこれで終わりです。この動画を楽しんでいただけたことを祈ります。また次回、お会いしましょう。


Python サンプル コード

これは、「チュートリアル 5: Web インタフェースを使用して Python スクリプトによって VRED をリモートでコントロールする方法」ビデオに付属する Python サンプル スクリプトです。

ヒント:

以下のファイルが含まれた ZIP ファイルをダウンロードするには、ここをクリックしてください。

custom-api.js

import { api } from 'http://localhost:8888/api.js';

// Connect event listener (signal)
api.vrAnnotationService.annotationsAdded.connect(() => console.log('Annotations added'));

// Add an annotation with name and text to the scene
function addAnnotation(name, text) {
    api.vrAnnotationService.createAnnotation(name)
    .then(() => {
        api.vrAnnotationService.findAnnotation(name)
        .then(a => a.setText(text));
    })
    .catch(() => console.error('Add annotation failed'));
}

// Get a list of all annotations
function getAnnotations() {
    api.vrAnnotationService.getAnnotations()
    .then(annotations => annotations.forEach(a => console.log(a)))
    .catch(() => console.error('Get annotations failed')); 
}

// Add both functions to the global scope
// So that we can call them from the html document
window.addAnnotation = addAnnotation;
window.getAnnotations = getAnnotations;

custom_web_interface_endpoints.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8"/>
        <script>
            // Send a generic python command without return value
            function sendPythonCommand(command) {
                var http = new XMLHttpRequest();
                var url = 'http://localhost:8888/python?value=' + encodeURI(command);
                http.open('GET', url, true);
                http.send();
            }

            // Send a python command with expecting a return value
            function sendPythonCommandWithReturnValue(command) {
                var http = new XMLHttpRequest();
                var url = 'http://localhost:8888/pythoneval2?value=' + encodeURI(command);
                http.open('GET', url, true);
                http.onreadystatechange = function() {
                    if (this.readyState == 4 && this.status == 200) {
                        var response = eval(this.responseText);
                        console.log(response);
                    }
                };
                http.send();
            }

            // Write a message to the terminal
            function logInfo(message) {
                sendPythonCommand("logInfo('"+message+"')");
            }

            // Select a variant set 
            function selectVariantSet(variantSet) {
                sendPythonCommand("selectVariantSet('"+variantSet+"')");
            }

            // get all viewpoints
            function getViewpoints() {
                sendPythonCommandWithReturnValue('getViewpoints()');
            }
        </script>
    </head>
    <body>
                <button onclick="logInfo('This is a message')">Print Info Message</button>

        <button onclick="selectVariantSet('Black Metallic')">Variant: Select Black Metallic</button>

        <button onclick="getViewpoints()">Request Viewpoints</button>
    </body>
</html>

custom_web_interface_web_api.html

<!DOCTYPE html>
<html>
    <head>
        <script type="module" src="custom-api.js"></script>
    </head>
    <body>
                <button onclick="addAnnotation('New Annotation', 'This is a new annotation')">Add annotation</button>
        <button onclick="getAnnotations()">Get all annotations</button>
    </body>
</html>