前回の記事ではOmniverseでのKit App Templateを使ったアプリの構築・ビルド・起動までの流れをご紹介しました。
今回はExtensionを作りオリジナルの機能を作ってみましょう。
前回と同様、またkit-app-templateの下でrepo.bat template newを実行します。
repo.bat template new
ナビゲーションで、今後はExtensionを選びます。
- Extension Templateを選びます。今回はPythonを使ったサンプル実装を行うため、Basic Python Extensionを選択します。
- Extensionの名前を入力します。
- Extensionの表示名を入力します。
- バージョンを入力します。
Extensionを作ることができました!
エクスプローラー上で新しいExtensionの関連ファイルが
作成されていることを確認できます。
次にExtensionのサブディレクトリの中にあるExtension.pyを開きます。
この中にはpython言語でデフォルトのExtension用のコードが記述されています。
この中で新しいExtesion用のコードを実装していきましょう。
ここでは独自ウィンドウで
Hello Omniverse!と表示するExtensionを作ります。
UIのサンプルコードを参考にして、python プログラムを実装してください。
on_startup関数はアプリが作成され、Extensionを作成しtらちじゅ
import omni.ui as ui
def on_startup(self, _ext_id):
"""This is called every time the extension is activated."""
print("[my_company.my_python_extension] Extension startup")
self._window = ui.Window("My hoge", width=300, height=200 )
with self._window.frame:
with ui.VStack():
ui.Label("Hello, Omniverse!")
def on_shutdown(self, _ext_id):
if self._window:
self._window.destroy()
self._window = None
書き終えたら、.kitファイルにこのExtension
の依存関係について追記します。
"my_company.my_python_extension" = {}
書き終わったら、repo.bat buildを実行します。
repo.bat build
ビルドが終わったらアプリケーションを起動します。
repo.bat launch
Applicationで確認してみましょう。
先ほど実装したウィンドウとテキストが
表示されていることが確認できます。
さらにステップアップし、StageにあるUSDの操作をするようなサンプルプログラムを作ってみましょう。
import omni.ui as ui
import omni.ext
import omni.usd
import omni.kit.commands
import carb
from pxr import Usd, UsdGeom, Sdf, Gf
# Functions and vars are available to other extensions as usual in python:
# `my_company.my_python_extension.some_public_function(x)`
def some_public_function(x: int):
"""This is a public function that can be called from other extensions."""
print(f"[my_company.my_python_extension] some_public_function was called with {x}")
return x**x
# Any class derived from `omni.ext.IExt` in the top level module (defined in
# `python.modules` of `extension.toml`) will be instantiated when the extension
# gets enabled, and `on_startup(ext_id)` will be called. Later when the
# extension gets disabled on_shutdown() is called.
class MyExtension(omni.ext.IExt):
"""This is a blank extension template."""
# ext_id is the current extension id. It can be used with the extension
# manager to query additional information, like where this extension is
# located on the filesystem.
def on_startup(self, _ext_id):
"""This is called every time the extension is activated."""
print("[my_company.my_python_extension] Extension startup")
self._window = ui.Window("Local Object Mover", width=300, height=100)
with self._window.frame:
with ui.VStack():
self._button = ui.Button("Move Object", clicked_fn=self.on_button_click)
self._usd_context = omni.usd.get_context()
def on_button_click(self):
self._stage = omni.usd.get_context().get_stage()
# Get default prim.
defaultPrim = self._stage.GetDefaultPrim()
# Default prim path.
defaultPrimPath = defaultPrim.GetPath().pathString
print("DefaultPrim : " + defaultPrimPath)
# Cone path.
orgPath = "/World/Cone"
cone_prim = self._stage.GetPrimAtPath(orgPath)
translate_attr = cone_prim.GetAttribute("xformOp:translate")
if not translate_attr:
translate_attr = cone_prim.CreateAttribute("xformOp:translate", Sdf.ValueTypeNames.Float3)
translate_attr.Set(Gf.Vec3f(0.0, 0.0, 0.0))
current_translate = translate_attr.Get()
# Move the cube by 10 units on the X axis
new_translate = Gf.Vec3f(current_translate[0] + 10.0, current_translate[1], current_translate[2])
translate_attr.Set(new_translate)
print(f"Moved cube to: {new_translate}")
def on_shutdown(self):
"""This is called every time the extension is deactivated. It is used
to clean up the extension state."""
print("[my_company.my_python_extension] Extension shutdown")
if self._window:
self._window.destroy()
self._window = None
importの内容
omni.ui
Omniverse Kit におけるUI(ユーザーインターフェイス)構築用のモジュールです。Window や Button など、基本的なUIパーツを提供します。
omni.ext
Omniverseの拡張機能を作る際に必要なインターフェイスを提供するモジュールです。拡張は omni.ext.IExt を継承して実装します。
omni.usd
USD のステージ(シーンデータ)を扱うための Omniverse 固有のヘルパーモジュールです。get_context() を使うことでステージへのアクセスが簡単に行えます。
omni.kit.commands
Omniverse Kit 内で提供される各種コマンドを使うためのモジュールです。UI 操作などからコマンドを実行して履歴管理・アンドゥ/リドゥを行う場合などに利用します。
###carb
NVIDIA の低レベルライブラリ群(カーボン)のモジュールです。ログ出力やプラグイン管理など、Omniverse Kit の内部機能と連携する際に使用します。
pxr のモジュール (Usd, UsdGeom, Sdf, Gf)
Pixar が開発した USD (Universal Scene Description) を扱うための低レベルAPIです。シーン内の各種プリム(オブジェクト)や、Sdf で定義されるパス、Gf の線形代数機能(ベクトルや行列など)を使用します。
USDの位置更新のための実装
今回はボタンを押した時に、Stageの中にあるUSDを動かすためのコールバック関数をon_shutdown(self)で実装しています。その中の処理について解説していきます。
USD ステージの取得
omni.usd.get_context().get_stage() で、現在のステージ(シーン)の参照を取得します。
Default Prim の確認
defaultPrim = self._stage.GetDefaultPrim() でステージ内のデフォルトプリムを取得し、パスをコンソールに出力しています。ここでは動作確認程度で実際の処理には使っていません。
Cone Prim の取得
今回は先ほどのようなConeオブジェクトを動かすサンプルとします。
orgPath = "/World/Cone" として、シーン内の /World/Cone にあるプリム(円錐)を取得します。これを cone_prim として扱い、移動属性(xformOp:translate)を操作しています。
xformOp:translate 属性
USD のトランスフォームは通常 xformOp:translate や xformOp:rotateXYZ、xformOp:scale といった形でプリムに設定されます。ここでは GetAttribute("xformOp:translate") で既に存在しているかを確認し、なければ CreateAttribute() で新たに作成しています。Sdf.ValueTypeNames.Float3 を使うことで、「ベクトル型の属性である」という定義を行っています。
Gf.Vec3f
Pixar USD のライブラリである Gf(geometry foundation)のベクトル型です。Set() でこのベクトルを使って実際のトランスフォーム値を更新します。
位置の更新
current_translate で現在の位置を取得し、X 軸方向(配列インデックス [0])に 10.0 加算して新しい位置ベクトルを作成しています。これにより /World/Cone プリムが X 軸方向に 10 ユニット移動します。
なお上記の内容までは下記のYouTubeでご紹介しています。Omniverse kit app templateを使ったアプリ開発の流れについての基本的なチュートリアルを紹介してますので、ぜひご確認ください!