Svelte で3D表示を行うライブラリ Threlte #4: シーンコントロールツールセット Theatre.js for Threlte

# シリーズ

### Main

* [Svelte で 3D 表示を行うライブラリ Threlte #1: パッケージの作成とライブラリ概要](https://qiita.com/takavfx/items/0d4c6b831628d628b468)
* [Svelte で 3D 表示を行うライブラリ Threlte #2: エクストラパッケージを使用する](https://qiita.com/takavfx/items/e71db089198bba1b122b)
* [Svelte で 3D 表示を行うライブラリ Threlte #3: glTF シーンを扱う](https://qiita.com/takavfx/items/aa05a313ef9e450b3731)
* Svelte で 3D 表示を行うライブラリ Threlte #4: シーンコントロールツールセット Theatre.js for Threlte
* [Svelte で3D表示を行うライブラリ Threlte #5: XR への対応](https://qiita.com/takavfx/items/6a499286c8a125c7b65d)

### Extra

* [Svelte で 3D 表示を行うライブラリ Threlte Ex1: Svelte5 対応を試す](https://qiita.com/takavfx/items/13feec35d60fe629eb26)

# Theatre.js for Threlte

シーンの中ではオブジェクトのエディットやマテリアルのエディットなども UI として提供したくなることもある。

こうした要求に対して、[Theatre.js](https://www.theatrejs.com) というものがある。
Threlte では、このツールセットを Svelte コンポーネントとして扱う事が出来量にするパッケージの提供があり、これを取り込むことによって簡単に Theatre.js の UI を展開することが出来る様になっている。

今回はこの Theatre の組み込み方や使い方に関して触れていく。

https://threlte.xyz/docs/reference/theatre/getting-started

## インストール

Theatre は別パッケージとして扱われているので、別途インストールする必要がある。

```bash
npm install @threlte/theatre @theatre/core @theatre/studio
```

* @threlte/theatre
    * Theatre.js コンポーネントを Threlte(Svelte) コンポーネントとして扱える様にするパッケージ。
* @theatre/core
    * Theatre.js 本体
* @theatre/studio
    * Theatre.js のエディタ UI 部分を管理しているパッケージ。

https://www.theatrejs.com

## 基礎の組み込み

まずは、`Theatre` コンポーネントをアプリケーションに組み込む。
Scene の上流階層に入れるイメージ。

```svelte:App.svelte
<script lang="ts">
	import { Canvas } from '@threlte/core';
	import { Theatre } from '@threlte/theatre';
	import Scene from './Scene.svelte';
</script>

<Canvas>
	<Theatre>
		<Scene />
	</Theatre>
</Canvas>
```

すると、この時点で画像の様に Theatre.js の UI が追加される。（とてもサクっと！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/a2f7ae5c-825b-f937-d9d4-e9bf279f0d8b.png)

## パラメーターコントロールを可能にする

### `<SheetObject>` の追加

コントロールを可能にするには、まず `<SheetObject>` で認識させたいオブジェクトをコンポーネント構造に組み込む必要がある。

`key` 要素は、その子コンポーネントとなるものをグループ化する目的で名前を付ける。
複数設定していくことは可能だが、`key` の値は「ID」としての認識を受けるので、内部でマテリアルパラメーターなどを同期させたい場合などの特殊な場合を省いて、基本はユニークにすべきだと思われる。

```svelte:Scene.svelte
<script lang="ts">
    import { SheetObject } from '@threlte/theatre';
</script>

<!-- 省略 -->

<SheetObject key="Sphere">
    <T.Mesh position.y={1.2}>
        <T.SphereGeometry />
        <T.MeshStandardMaterial color="grey" roughness={0.0} />
    </T.Mesh>
</SheetObject>


<SheetObject key="Box">
	<T.Mesh position={[-1.5, 1.2, 0]}>
		<T.BoxGeometry />
		<T.MeshStandardMaterial color="grey" roughness={0.0}>
		</T.MeshStandardMaterial>
	</T.Mesh>
</SheetObject>

```

![2024-01-28 10.37.20 localhost d5f3b73c807f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/f9220ed0-5dc7-9e71-b427-cab8be4bae06.png)



### パラメーターコントロール

シーンオブジェクトのパラメーターをコントロールできるようにするには、以下の二つを設定する。

* `<SheetObject>` への `let:Sync` の追加。
* `<Scyn>` を、コントロールしたいコンポーネントの**子コンポーネント**として追加。

```diff_svelte:Scene.svelte
+<SheetObject key="Sphere" let:Sync>
	<T.Mesh position.y={1.2}>
		<T.SphereGeometry />
		<T.MeshStandardMaterial color="white">
+			<Sync color roughness metalness />
		</T.MeshStandardMaterial>
	</T.Mesh>
</SheetObject>
```

![output3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/b8a96aa8-eba8-c2cd-ec0b-97cc1814527d.gif)

:::note warn
上記の `key` の値を同値にして、同様の `<Sync>` を含む場合、それらのオブジェクトは先に宣言された方のパラメーター更新と同じ影響を受けてしまう。

```svelte:Scene.svelte
<SheetObject key="Sphere" let:Sync>
	<T.Mesh position.y={1.2}>
		<T.SphereGeometry />
		<T.MeshStandardMaterial color="grey" roughness={0.0}>
			<Sync color roughness metalness />
		</T.MeshStandardMaterial>
	</T.Mesh>
</SheetObject>

<SheetObject key="Sphere" let:Sync>
	<T.Mesh position={[-1.5, 1.2, 0]}>
		<T.BoxGeometry />
		<T.MeshStandardMaterial color="grey" roughness={0.0}>
			<Sync color roughness metalness />
		</T.MeshStandardMaterial>
	</T.Mesh>
</SheetObject>
```

![2024-01-28 10.34.57 localhost fc18d1723506.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/181cc601-ec3e-8c45-fe56-995119d82652.png)
:::

### Transform コントロール

シーンオブジェクトの Transform をコントロールする場合は、以下の二つを設定する。

* `<SheetObject>` への `let:Transform` の追加。
* `<Transform>` をコントロールしたいコンポーネントの**親コンポーネント**として追加。

```diff_svelte:Scene.svelte
+<SheetObject key="Sphere" let:Transform>
+	<Transform>
		<T.Mesh position.y={1.2}>
			<T.SphereGeometry />
			<T.MeshStandardMaterial color="grey" roughness={0.0} />
		</T.Mesh>
+	</Transform>
</SheetObject>
```


![output2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/9d326ae1-fab2-3387-05f0-253dd0abd698.gif)

## 要素の一括適用

例えば透過処理設定を内包するコンポーネント全体に対して行いたい場合がある場合、`<Declare>` 要素を使用する。

```svelte:Scene.svelte
<SheetObject let:Declare>
  <Declare
    props={{ transparent: false }}
    let:values
  >
    <T.MeshBasicMaterial transparent={values.transparent} />
  </Declare>
</SheetObject>
```

![output5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/2ffb5e95-f9cd-71ed-dbcc-3b856d2ab69b.gif)


# その他の特徴

## パラメーターのローカル保存

Theatre.js では、パラメーターの変更等は基本的に local storage への保存が実行されている。
なので、ページ更新などを行っても変更履歴はローカル保存される。

![output4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/7cb747df-310a-a8e2-afba-58a39ad86775.gif)

---

:point_right:次のシリーズを読む
[Svelte で3D表示を行うライブラリ Threlte #5: XR への対応](https://qiita.com/takavfx/items/6a499286c8a125c7b65d#ar)