STLファイルをDracoに圧縮して爆速3Dビューアを作ってみる

こんにちは。
株式会社Oh my teethでテックリードをやっているTerukiです。

[前回の記事](https://qiita.com/omt_teruki/items/2c6c803807c051d19d19)から少し間が空いてしまいましたが2本目の記事を書いていきます:pray_tone1:
# TL;DR
- STLをDracoに変換するとSTLファイルのサイズがだいたい10分の1ほどにできる
- この記事を読めばSTLをDracoで圧縮したファイルを作れる
- Oh my teethの[無料歯型スキャン予約時](https://reservation.oh-my-teeth.com/scan?tags=qiita_drc&code=QiitaSTL)にプロモーションコード「QiitaSTL」を入力するとスキャンしたSTLファイルが貰える

# 概要
DracoというGoogleが開発している3Dモデルのファイル形式があります。
STLファイルをDracoに変換するとファイルサイズがだいたい10分の1ほどにできます。
Webで3Dビューアを実装する際、STLのままロードするとファイルサイズが大きくなりモバイル回線だと表示される前にユーザーが離脱してしまいます。

Oh my teethでは[前回の記事の概要](https://qiita.com/omt_teruki/items/2c6c803807c051d19d19#%E6%A6%82%E8%A6%81)の通り歯型の3Dデータをレンダリングしていますが、歯型のSTLが上下顎合わせると30MB近くになるためせっかく作ったビューアも触られない、そもそも歯型が表示されることに気付いてもらえないなどが課題でした。

この記事ではSTLをDracoに変換する部分とThree.jsでレンダリングするところまで説明できればと思います。

# 準備するもの
- NodeJS
- 適当なエディタ

前回に引き続き基本TypeScriptでやっていきます。

# やってみる (STL→Draco変換)
Three.jsでDracoをレンダリングしようにもまずは既存のSTLを変換できないとお話になりません。
STLをDracoに変換するにはDracoのリポジトリにあるコードを自前でビルドする必要があります。

公式でバイナリを配布しているか探してみましたが私は見つけられませんでした。

https://github.com/google/draco

公式に各環境に合わせたビルド手順が書かれているので細かいことは[公式](https://github.com/google/draco/blob/master/BUILDING.md)に投げます:pray:が、この記事ではまっさらなWSL Ubuntu 22.04でやってみます。

ファイル準備
```sh
mkdir ~/work
cd ~/work
git clone https://github.com/google/draco.git
cd draco
mkdir out && cd out
```

必要なツールをインストール
```sh
sudo apt-get update
sudo apt-get install cmake g++
```

プロジェクトファイル作成
```sh
cmake ../
```

ビルド
```sh
make
```

※make -j 12などオプションをつけるとパラレルでビルドするので速くなります。12の部分は環境に合わせて変更ください:pray_tone1:

ここまでくるとカレントディレクトリに`draco_encoder`と`draco_decoder`のシンボリックリンクができていると思います。

これでエンコーダーの準備ができたので前回の記事も使ったStanford_Bunnyを変換してみます。

```sh
wget https://upload.wikimedia.org/wikipedia/commons/4/43/Stanford_Bunny.stl
./draco_encoder -i Stanford_Bunny.stl -o bunny.drc
```
出典: https://commons.wikimedia.org/wiki/File:Stanford_Bunny.stl

私の環境では下記のような結果になりました。

```sh
mrtska@Vega:~/work/draco/out$ ls -lh Stanford_Bunny.stl bunny.drc
-rw-r--r-- 1 mrtska mrtska 5.4M Jan 24  2020 Stanford_Bunny.stl
-rw-r--r-- 1 mrtska mrtska 491K Mar 26 02:03 bunny.drc
```
5.4Mが0.5Mほどまで小さくなっているのが分かります。すごい！

# やってみる (Three.jsでDracoをレンダリング)
stlをdrcに変換することができました。
ただ、変換してサイズが小さくなってもレンダリングできなければ何の意味もありません。

[前回の記事で使ったリポジトリ](https://github.com/ohmyteeth/threejs-stl-sample)を拝借して続きを開発してみます。

```sh
git clone https://github.com/ohmyteeth/threejs-stl-sample.git
```

と言っても実はものすごく簡単でThree.js側はSTLLoaderをDRACOLoaderに変えるだけです。
このDRACOLoaderはWebAssemblyのDracoデコーダーを使ってデコードするため、別途デコーダーのwasmとそのラッパーを用意してあげます。

このラッパーとwasmはThree.jsのパッケージの中に入っているのでここでは`src`配下にコピーして使います。
実運用ではCDNか何かに置いておくのが良さそうです。

```sh
cp node_modules/three/examples/jsm/libs/draco/draco_decoder.wasm src/
cp node_modules/three/examples/jsm/libs/draco/draco_wasm_wrapper.js src/
```

`main.ts`の修正

```patch:main.ts
 import * as THREE from 'three';
-import { STLLoader } from 'three/examples/jsm/loaders/STLLoader';
+import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
```

```patch:main.ts
-// STLファイルをロードしてレンダリング
-const stlLoader = new STLLoader();
-stlLoader.load('./Stanford_Bunny.stl', (geometry) => {
+// DRCファイルをロードしてレンダリング
+const dracoLoader = new DRACOLoader();
+dracoLoader.setDecoderPath('/src/');
+
+dracoLoader.load('./bunny.drc', (geometry) => {
     const material = new THREE.MeshPhongMaterial({
         color: 0xFFFFFF
     });
```

これで準備はできました。`npm run dev`して見てみましょう。

![drc](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192685/4d5f7117-cedf-301e-15d2-e33b4aa81b26.png)

[前回の記事の最後の部分](https://qiita.com/omt_teruki/items/2c6c803807c051d19d19#%E6%93%8D%E4%BD%9C%E3%81%A7%E3%81%8D%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B)と比較してみても目視では全然分かりませんね！

最終形のコードはこちら:
https://github.com/ohmyteeth/threejs-drc-sample/blob/main/src/main.ts

# 歯型STLプレゼントキャンペーン
前回の記事と同様、歯型STLプレゼントキャンペーンのご紹介です。

Oh my teethはマウスピース矯正ブランドの1つです。
詳細なサービスの説明は[公式サイト](https://www.oh-my-teeth.com/?tags=qiita_drc_bottom)にお任せしますが、tech系の内容については以前Techableに寄稿した[こちらの記事](https://techable.jp/archives/187394)が面白いかもしれません。

また、マウスピース矯正に興味がある方に[無料歯型スキャン](https://www.oh-my-teeth.com/posts/free-3d-scan?tags=qiita_stl_bottom)でスキャンした歯型のSTLとDRCをお渡しします:tada:

[予約フォーム](https://reservation.oh-my-teeth.com/scan?tags=qiita_drc_bottom&code=QiitaSTL)

スキャン後に私から上顎と下顎のSTLファイルをお渡しします！
上記予約フォームのリンクか、プロモーションコード欄に「QiitaSTL」と入力してご予約ください。

歯医者だけど歯医者っぽくないtech感ある体験ができるので気軽にお越しいただければと思っています。

# おわりに
前回に続いてThree.jsの記事をお送りしました。
Oh my teethでは未来の歯科体験を実現していくためにテクノロジーに力を入れているのでこういった発信をどんどんしていけたらと思っています。

次回はまた違った観点で記事にできたらと思っています。
ではでは！