メインコンテンツへスキップ

シミュレーションチャネル

概要​

シミュレーションチャネルは、デバイスの状態をやり取りしたり、セッションやデバイスへのコマンドを送信したりするために使用される、高頻度で双方向のWebSocketチャネルです。

デフォルトの URL: ws://localhost:10001

ポートは設定で変更できます。

コアに関する取り決め(重要):

  • クライアントが接続すると、サービスはすべてのデバイスの完全な一覧を含む「初期インベントリメッセージ」を送信します。
  • その後、サーバーは、クライアントから受信したメッセージごとに、正確に1つのState Updateメッセージを送信します。
  • 各「State Update」には、すべてのデバイスの状態(およびステータス)が含まれます。
モジュール

このページでは、シミュレーションチャネルのコアプロトコルとコマンドについて説明します。

追加のモジュールや機能は、独自のコマンドや状態フィールドを登録することで、システムを拡張することができます。これらについては、「モジュール」のセクションで個別に説明しています。

プロトコルのルール​

クライアントメッセージごとに1つの応答​

サービスは、クライアントから受信した各メッセージに対して、すべてのデバイスの状態を含む 1つの State Update メッセージを送信します。

これは次のような意味です:

  • デバイスの状態に関する最新のスナップショットが必要な場合は、何らかのメッセージ(セッションコマンド、プローブコマンド、またはデバイスコマンド)を送信する必要があります。
  • このチャネルは、クライアントメッセージによって駆動される「ティック」ループのように動作します。

力を加えずにポーリングを行う(プローブコマンド)​

力を加えたりシミュレーションパラメータを変更したりすることなく、状態の変化を監視したい場合は、プローブコマンドを使用してください:

  • inverse3 向けの probe_position
  • Verse Grip 向けの probe_orientation

プローブコマンドにはコマンドデータは含まれておらず、デバイス情報の照会を強制するだけです。これにより、次のステート更新時に位置・向きのデータが最新の状態になります。

プローブコマンドが不要な場合があります

制御コマンド(set_cursor_force、set_cursor_position (など)をすでに送信しているシミュレーションセッションでは、プローブコマンドを送信する必要はありません。デバイスの状態は、常にレスポンスフレームに含まれます。プローブは、デバイスを動作させることなく、デバイスの状態をポーリングする必要があります。監視専用のセッション(例:Haply Hub)向けです。

設定と状態​

  • 初期在庫 メッセージには、デバイスの config、state、status が含まれます。
  • 通常の 州の最新情報 メッセージには、デバイスの state と status が含まれます。

設定を含むスナップショットを再度取得する必要がある場合は、session.force_render_full_state を使用してください。

configure と commands の違い​

デバイスメッセージは、configure と commands の 2 つの別々のマップをサポートしています。これらはそれぞれ異なる目的で使用されます:

マップ目的送信頻度
configure一度だけ設定すればよい永続的な構成:プリセット、基準(basis)、取り付け(mount)、フィルター、モジュール設定1回、または変更時に送信
commands各ティックごとの非永続的な指令:力、位置、トルク毎ティック送信が必要

commands 内のエントリ(set_cursor_force、set_cursor_position、set_angular_torques、set_angular_position)は、ティックごとに1回適用され、保存されません。送信を停止すると、その効果は直ちに失われ、デバイスはアイドル状態に戻ります。再送信しなくてもティックをまたいで保持すべきものには、configure を使用してください。

注記
set_transform これは特別なケースです

set_transform は commands の下に置かれますが、永続的なです。サービスは、新しいトランスフォームを送信するまで、最後に送信されたトランスフォームを保持します。毎ティック送信する必要ありません。とはいえ、その目的はカメラ/シーンのナビゲーションであるため、ティックレートでストリーミングする(例:ユーザーがシーンをパンする際に、フレームごとに1つのトランスフォームを送信する)ことが、連続的な動きのナビゲーションにおける想定される使用パターンです。

座標系​

Haply は、デフォルトでZ-Up の右手座標系を使用します。

2つのconfigureエントリが、座標の解釈と返却方法に影響を与えます:

  • session.configure.basis: 「Haply 」の座標とアプリケーションの座標系との対応関係を変更します(下記の 基礎 (を参照)。
  • inverse3[*].configure.preset: ワークスペースの起点となる位置を指定し、名前付きの工場出荷時設定を選択します(例: arm_front_centered は (0, 0, 0) をワークスペースの中央に配置します)。各デバイスの「Configure」セクションを参照してください。

メッセージ形式​

このセクションでは、メッセージの上位レベルのエンベロープとメッセージの種類について説明します。具体的な例については、このドキュメントの後半に掲載しています。

デバイスグループ​

メッセージは、最上位レベルでデバイスタイプごとにグループ化されます(例: inverse3、verse_grip、wireless_verse_grip)。各デバイスタイプのキーは、デバイスごとのオブジェクトの配列に対応します。

初期インベントリ(サーバー → クライアント)​

WebSocket への接続が確立された直後に、1 回送信されます。

各エントリには以下の内容が含まれます:

  • device_id
  • config
  • state
  • status

参照:例:初期インベントリのペイロード

ステータス更新(サーバー → クライアント)​

クライアントメッセージごとに 1 回送信されます。

各エントリには以下の内容が含まれます:

  • device_id
  • state
  • status

参照:例:State Update のペイロード

セッションコマンドのエンベロープ(クライアント → サーバー)​

セッションコマンドは、現在の接続/セッションに適用されるアクションであり、デバイス固有のものではありません。

{
"session": {
"<command_name>": {
"...": "..."
}
}
}

デバイスコマンドのエンベロープ(クライアント → サーバー)​

デバイスコマンドは、デバイスタイプのキーの下に配列として送信され、1 つのメッセージで 1 台以上のデバイスにコマンドを送信できます。各デバイスのエントリは、configure 地図と commands 両方のマップに対応しています。

{
"<device_type>": [
{
"device_id": "<id>",
"configure": {
"<config_key>": { "...": "..." }
},
"commands": {
"<command_name>": { "...": "..." }
}
}
]
}

同じ種類の複数のデバイスにコマンドを送信するために、1つのメッセージに複数のエントリを含めることができます。 commands は辞書であり、1つのデバイスに対して複数のコマンドを含めることができます。ただし、コマンドの種類ごとに1つだけです。

execute フィールド​

すべての command または configure エントリは、省略可能な "execute" ブール値(デフォルトは true)を受け付けます。"execute": false に設定すると、エントリは解析されますが、適用はされません。これは、すべてのフィールドを常に出力するリフレクションベースのシリアライザ(例:Unity JsonUtility)で役立ちます。

"set_cursor_force": { "execute": false, "vector": { "x": 0.0, "y": 0.0, "z": 0.0 } }

コマンドリファレンス​

セッション​

完全な状態での強制レンダリング​

すべてのデバイスの状態と構成のスナップショットを取得します。

{
"session": {
"force_render_full_state": {}
}
}

セッションの構成​

セッションレベルの永続的な設定は session.configure を通じて送信されます。これはデバイスレベルの configure マップの構成を踏襲しています。

プロフィール​

セッションのプロファイル名を設定します。この名前は、Haply Hub がシミュレーションを識別し、アプリごとのデバイス設定を保存するために使用されます。

{
"session": {
"configure": {
"profile": {
"name": "my_profile"
}
}
}
}
基盤(セッションレベル)​

セッション全体の基底マッピングを設定します。基底マッピングは、Haply の座標がアプリケーションの座標系にどのように変換されるかを表します。 設定後は、すべてのデバイス状態がその基底で返され、送信されるすべての値もその基底で解釈されます。

順列文字列は、Haply の右手系・Z軸が上向きの座標系を基準として表されます。X、Y、Z の順列で、必要に応じて + または - を前置します。例:

  • XYZ、ZYX、+Y-Z+X、X-ZY
  • YZX は、アプリケーションの Y が、Haply の右側、Z が、Haply の前方、X これは、Haply が上方向であることを意味します。

左手系・Z軸が上向きの例(Unreal、X-YZ):

{
"session": {
"configure": {
"basis": { "permutation": "X-YZ" }
}
}
}
警告
session.set_basis からの移行

従来の session.set_basis と session.configure.basis の間で、軸の符号に関する規則が変更されました。旧コマンドでは正常に動作していた順列が、新しいコマンドでは逆方向の変換を生成する場合があります。必要に応じて、軸の符号を反転させてください(例:X-ZY は XZ-Y となります)。

すべてのデバイスで共通のコマンド​

プロービング(全デバイス)​

プロービングコマンドを使用すると、力を加えるなどのシミュレーションの変更を行うことなく、最新の位置・姿勢情報を取得することができます。

  • inverse3: probe_position
  • Verse Grip(verse_grip、wireless_verse_grip): probe_orientation
{
"inverse3": [
{
"device_id": "049D",
"commands": {
"probe_position": {}
}
}
],
"verse_grip": [
{
"device_id": "049D",
"commands": {
"probe_orientation": {}
}
}
],
"wireless_verse_grip": [
{
"device_id": "049D",
"commands": {
"probe_orientation": {}
}
}
]
}

変換の設定(すべてのデバイス)​

デバイスのワークスペース変換(デバイス空間からアプリケーション空間への変換)を設定します。

その他の commands エントリとは異なり、set_transform は永続的なです。このサービスは、次の値が送信されるまで、最後に送信された値を保持します。ティックごとに送信する必要はありませんが、用途がカメラやシーンのナビゲーションであるため、連続的な動きに対しては、ティックレートでストリーミングすることが想定される使い方です(例:ユーザーがシーンをパンしている間、フレームごとに 1 つの変換を送信する)。

{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_transform": {
"transform": {
"position": { "x": 0.0, "y": 0.0, "z": 0.0 },
"rotation": { "x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0 },
"scale": { "x": 1.0, "y": 1.0, "z": 1.0 }
}
}
}
}
]
}
Verse Grip / ワイヤレスVerse Grip

Verse Grip および Wireless Verse Grip デバイスでは、変換のうち rotation 効果をもたらすのは成分のみです。グリップは姿勢についてのみ報告するため、position や scale 拡大・縮小や平行移動の対象となるものはありません。position と scale このフィールドは、Inverse3 とのスキーマの一貫性を保つためだけに受け入れられ(スナップショットにもそのまま返されます)、恒等値(position = {0,0,0}、scale = {1,1,1})のまま使用するのが一般的な方法です。

Inverse3 デバイスの設定​

構成エントリは configure マップで送信します。これらは永続的です。一度送信するか、値が変更されたときに送信してください。

プリセット​

{
"inverse3": [
{
"device_id": "049D",
"configure": {
"preset": { "preset": "arm_front_centered" }
}
}
]
}

指定可能な値:device_defaults、arm_front、arm_front_centered、led_front、led_front_centered、custom。

基盤(デバイスレベル)​

{
"inverse3": [
{
"device_id": "049D",
"configure": {
"basis": { "permutation": "ZXY" }
}
}
]
}

マウント​

デバイスのマウント変換を設定します。mount 内の transform ラッパーにはご注意ください。

{
"inverse3": [
{
"device_id": "049D",
"configure": {
"mount": {
"transform": {
"position": { "x": 0.0, "y": 0.0, "z": 0.0 },
"rotation": { "x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0 },
"scale": { "x": 1.0, "y": 1.0, "z": 1.0 }
}
}
}
}
]
}
マウント・スキーマの非対称性

コマンド側(クライアントからサーバーへ)では、変換はラッパーで包まれます:"mount": { "transform": { ... } }。 スナップショット側(サーバーからクライアントへ)では、フラットな構造になります:"mount": { "position": {...}, "rotation": {...}, "scale": {...} }。 これは意図的な設計です。コマンドは統一されたラッパーを使用し、スナップショットは変換を直接シリアライズします。スナップショットの形式をコマンドのペイロードにコピーしないよう注意してください。

減衰​

均一な減衰および/または方向ごとの減衰を制御します。少なくとも 1 つのフィールドを指定する必要があります。

{
"inverse3": [
{
"device_id": "049D",
"configure": {
"damping": { "scalar": 0.5 }
}
}
]
}

方向ごとの減衰を設定することも、両方を同時に設定することも可能です:

"damping": { "scalar": 0.5, "vector": { "x": 0.0, "y": 1.0, "z": 0.0 } }

フォースゲート​

フォースゲートの減衰ゲイン(0.0~1.0)を設定します。

{
"inverse3": [
{
"device_id": "049D",
"configure": {
"force_gate": { "gain": 0.3 }
}
}
]
}

Inverse3 コマンド​

inverse3 デバイスにコマンドを送信するには、inverse3 キーの下に、一致する device_id を含むエントリを含めます。このコマンドはティック単位で有効であり、有効な状態を維持するには、ティックごとに再送信する必要があります。

1つのデバイスにコマンドを送信する場合:

{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}

1つのメッセージで複数のデバイスにコマンドを送信する:

{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
},
{
"device_id": "049E",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}

カーソル位置の設定​

{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_position": {
"position": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}

カーソルの強さの設定​

{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_cursor_force": {
"vector": {
"x": 1.0,
"y": 2.0,
"z": 3.0
}
}
}
}
]
}

角度位置の設定​

{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_angular_position": {
"angles": {
"a0": 1.0,
"a1": 2.0,
"a2": 3.0
}
}
}
}
]
}

角度トルクの設定​

{
"inverse3": [
{
"device_id": "049D",
"commands": {
"set_angular_torques": {
"torques": {
"a0": 1.0,
"a1": 2.0,
"a2": 3.0
}
}
}
}
]
}

Verse Grip の設定​

Verse Grip および Wireless Verse Grip デバイスは、preset、basis、mount これについては、Inverse3 と同じです。 configure キーに対応しています。

拡張 Verse Grip​

set_extension_data このコマンドは、ボード拡張通信プロトコルを実装したバージョンのグリップで使用される、Verse Grip の拡張プロトコルの一部です。

グリップ拡張データの設定​

サポートされるデータ長:

  • アップストリーム(クライアント → デバイス):最大 20 バイト。
  • ダウンストリーム(デバイス → クライアント):最大 12 バイト。state.extension_data として、State Update メッセージで返されます。

データ仕様:

  • 配列の長さ:20 バイト
  • 値の範囲:各値は0~255
{
"wireless_verse_grip": [
{
"device_id": "049D",
"commands": {
"set_extension_data": {
"extension_data": [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
]
}
}
}
]
}

例​

例:初期インベントリのペイロード​

WebSocket が接続されると、サービスはデバイスの完全な一覧を含むメッセージを送信します。初期メッセージは次のとおりです。 JSON 形式は次のとおりです:

{
"inverse3": [
{
"device_id": "04BA",
"config": {
"type": "inverse3",
"port": "COM13",
"device_info": {
"major_version": 7,
"minor_version": 1,
"id": "04BA",
"device_type": 4,
"uuid": "2D35F80DD9005F599B68F49944CB04BA"
},
"extended_device_id": "2D35F80DD9005F599B68F49944CB04BA",
"extended_firmware_version": "8C20FDC8010AA1E15AA133CDA2534874",
"gravity_compensation": {
"enabled": true,
"scaling_factor": 1
},
"handedness": "right",
"streaming_mode": "USB",
"torque_scaling": {
"enabled": true
},
"home_return": {
"enabled": false
},
"filters": {
"force_gate": { "gain": 0.3 },
"damping": { "scalar": 0 }
},
"preset": "defaults",
"basis": { "permutation": "XYZ" },
"mount": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
}
},
"state": {
"cursor_position": {
"x": 0.07842738,
"y": -0.14836666,
"z": 0.14297646
},
"cursor_velocity": {
"x": -0.011969013,
"y": 0.0012009288,
"z": -0.043197
},
"angular_position": {
"x": -69.31704,
"y": 137.62952,
"z": 19.832787
},
"angular_velocity": {
"x": 0,
"y": 0,
"z": 0
},
"body_orientation": {
"x": -0.01940918,
"y": 0.7026367,
"z": 0.00048828125,
"w": 0.7113037
},
"current_cursor_force": { "x": 0, "y": 0, "z": 0 },
"current_cursor_position": { "x": 0, "y": 0, "z": 0 },
"current_angular_torques": { "x": 0, "y": 0, "z": 0 },
"current_angular_position": { "x": 0, "y": 0, "z": 0 },
"control_domain": "cartesian",
"control_mode": "force",
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"calibrated": false,
"in_use": false,
"power_supply": true,
"ready": true,
"started": true
}
}
],
"verse_grip": [
{
"device_id": "61548",
"config": {
"type": "verse_grip",
"port": "COM3",
"preset": "device_defaults",
"basis": { "permutation": "XYZ" },
"mount": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
}
},
"state": {
"button": false,
"hall": 0,
"orientation": {
"x": -0.5019531,
"y": 0.8632202,
"z": -0.048095703,
"w": -0.022338867
},
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"error": 0,
"ready": true
}
}
],
"wireless_verse_grip": [
{
"device_id": "0",
"config": {
"type": "wireless_verse_grip",
"port": "COM6",
"major_version": 1,
"minor_version": 4,
"hardware_version": 1,
"streaming_mode": "Radio",
"preset": "device_defaults",
"basis": { "permutation": "XYZ" },
"mount": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
}
},
"state": {
"battery_level": 0.816,
"battery_voltage": 3.77,
"buttons": {
"a": false,
"b": false,
"c": false
},
"hall": 16,
"orientation": {
"x": -0.019866943,
"y": -0.017486572,
"z": 0.05508423,
"w": -0.9963989
},
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"connected": true,
"awake": true,
"ready": true
}
}
]
}

例:状態更新のペイロード​

このサービスは、受信したメッセージごとに、すべてのデバイスの状態を含む状態更新メッセージを1つ送信します。

マシンの状態を確認したい場合は、事前にメッセージを送信しておく必要があります(例:値がゼロであっても、プローブコマンドや力の値など)。力を加えることなく、デバイスを入力ソース(例:位置の追跡)として使用する場合、これは特に重要です。

状態更新メッセージは次の JSON 形式は次のとおりです:

{
"inverse3": [
{
"device_id": "04BA",
"state": {
"cursor_position": {
"x": 0.07842738,
"y": -0.14836666,
"z": 0.14297646
},
"cursor_velocity": {
"x": -0.011969013,
"y": 0.0012009288,
"z": -0.043197
},
"angular_position": {
"x": -69.31704,
"y": 137.62952,
"z": 19.832787
},
"angular_velocity": {
"x": 0,
"y": 0,
"z": 0
},
"body_orientation": {
"x": -0.01940918,
"y": 0.7026367,
"z": 0.00048828125,
"w": 0.7113037
},
"current_cursor_force": { "x": 0, "y": 0, "z": 0 },
"current_cursor_position": { "x": 0, "y": 0, "z": 0 },
"current_angular_torques": { "x": 0, "y": 0, "z": 0 },
"current_angular_position": { "x": 0, "y": 0, "z": 0 },
"control_domain": "cartesian",
"control_mode": "force",
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"calibrated": false,
"in_use": false,
"power_supply": true,
"ready": true,
"started": true
}
}
],
"verse_grip": [
{
"device_id": "61548",
"state": {
"button": false,
"hall": 0,
"orientation": {
"x": -0.5019531,
"y": 0.8632202,
"z": -0.048095703,
"w": -0.022338867
},
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"error": 0,
"ready": true
}
}
],
"wireless_verse_grip": [
{
"device_id": "0",
"state": {
"battery_level": 0.816,
"battery_voltage": 3.77,
"buttons": {
"a": false,
"b": false,
"c": false
},
"hall": 16,
"orientation": {
"x": -0.019866943,
"y": -0.017486572,
"z": 0.05508423,
"w": -0.9963989
},
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"connected": true,
"awake": true,
"ready": true
}
}
],
"custom_verse_grip": [
{
"device_id": "0",
"state": {
"battery_level": 0.816,
"battery_voltage": 3.77,
"buttons": {
"a": false,
"b": false,
"c": false
},
"hall": 16,
"orientation": {
"x": -0.019866943,
"y": -0.017486572,
"z": 0.05508423,
"w": -0.9963989
},
"extension_data": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"transform": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
},
"transform_velocity": {
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 0, "y": 0, "z": 0 }
}
},
"status": {
"ready": true
}
}
]
}