Let's write β

プログラミング中にできたことか、思ったこととか

DracoとAFrameを組み合わせて使ってみた

Dracoのインストール

Emscriptenのインストール

私はMac環境なのでhomebrewでemscriptenbrew install emscriptenでインストールしました。 インストール後は一度emccを動かしbrew info emscriptenの指示に従って~/.emscriptenを編集しましょう

Dracoのビルド

Dracoのビルドは公式のREADME.mdに従うとうまくいきます。ファイルがゴリッと生成されるので作業用フォルダを作ってその中でやるのを忘れないようにしましょう。 export EMSCRIPTEN=にどの値を設定するかは~/.emscriptenEMSCRIPTEN_ROOTの値を参考にしましょう。

AFrameのビルド

AFrameにはthree.jsが埋め込まれているのですが、現状の最新のリリース版(0.4.0)ではthree.jsが82系をつかっており、一方Dracoの方は83系をつかっているので想定しているAPIのバージョンが合わないので上手く使えません。しかし、Aframeのmasterでは(2017/01/18 01:58 確認)83系をつかっているのでこちらをビルドして使いましょう。ビルドについてはREADME.mdに従えば問題ありません。aframe-master.min.jsができるのでこれを利用します。

DRACOLoader.jsの取得

DracoのサンプルにDRACOファイルをthree.jsのBufferGeometryに変換するためのDRACOLoader.jsが用意されています https://github.com/google/draco/blob/master/javascript/example/DRACOLoader.js

今回はコレからサンプルに強く紐付いている箇所を削除し利用しています

https://github.com/pocket7878/draco-aframe-example/blob/master/DRACOLoader.js

サンプルデータのダウンロード

サンプルのdraco圧縮されたファイルはexample/modelsフォルダ内に入っているウサギのモデルを今回は利用しました。 もとのオブジェクトデータはhttps://graphics.stanford.edu/data/3Dscanrep/にあるようです。

コード

Dracoファイルをロードするdraco-modelコンポーネントを登録

汎用的に利用できるようにするためにdracoのモデルをURLからロードするdrc-modelというコンポーネントを AFrameに登録します。

const DracoModule = Module;

AFRAME.registerComponent('drc-model', {
    schema: {
        drcUrl: {type: 'string'}
    },
    init: function() {
        this.model = null;
        this.drcLoader = new THREE.DRACOLoader();
        this.drcLoader.crossOrigin = '';
    },
    
    update: function() {
        var data = this.data;
        if (!data.drcUrl) { return ; }
        this.remove();
        this.loadObj(data.drcUrl);
    },
    
    remove: function() {
        if (!this.model) { return; }
        this.el.removeObject3D('mesh');
    },
    
    loadObj: function(drcUrl) {
        var self = this;
        var el = this.el;
        var drcLoader = this.drcLoader;
        drcLoader.load(drcUrl, function(bufferGeometry) {
            var material = new THREE.MeshStandardMaterial({vertexColors: THREE.VertexColors});

            let geometry;
            // Point cloud does not have face indices.
            if (bufferGeometry.index == null) {
              geometry = new THREE.Points(bufferGeometry, material);
            } else {
              bufferGeometry.computeVertexNormals();
              geometry = new THREE.Mesh(bufferGeometry, material);
            }
            
            // Compute range of the geometry coordinates for proper rendering.
            bufferGeometry.computeBoundingBox();
            const sizeX = bufferGeometry.boundingBox.max.x - bufferGeometry.boundingBox.min.x;
            const sizeY = bufferGeometry.boundingBox.max.y - bufferGeometry.boundingBox.min.y;
            const sizeZ = bufferGeometry.boundingBox.max.z - bufferGeometry.boundingBox.min.z;
            const diagonalSize = Math.sqrt(sizeX * sizeX + sizeY * sizeY + sizeZ * sizeZ);
            const scale = 1.0 / diagonalSize;
            const midX = (bufferGeometry.boundingBox.min.x + bufferGeometry.boundingBox.max.x) / 2;
            const midY = (bufferGeometry.boundingBox.min.y + bufferGeometry.boundingBox.max.y) / 2;
            const midZ = (bufferGeometry.boundingBox.min.z + bufferGeometry.boundingBox.max.z) / 2;

            geometry.scale.multiplyScalar(scale);
            geometry.position.x = -midX * scale;
            geometry.position.y = -midY * scale;
            geometry.position.z = -midZ * scale;
            geometry.castShadow = true;
            geometry.receiveShadow = true;
            self.model = geometry;
            
            el.setObject3D('mesh', geometry);
            el.emit('model-loaded', {format: 'drc', model: geometry});
        });
    }
});

アセット経由でロードできない

現状ではa-asset-itemでロードしようとして

<a-asset-item id="banny-drc" src="banny.obj></a-asset-item>

というふうにすると、上手くロードできません。 Three.jsのキャッシュやらa-asset-itemのロードのresponseTypeやらの兼ね合いで上手くロードできませんので、 現状はURL経由でdrc-model内部でTHREE.DRACOLoader()でロードしています。

a-scene上で利用する

上記のようなコンポーネントを登録しておくとa-sceneの中で簡単にロードできます

<a-scene>
      <a-sky color="#ECFFFF"></a-sky>
      <a-entity drc-model="drcUrl: ./bunny.drc" position="0 1.25 -2"></a-entity>
</a-scene>

のようにしてシーンにウサギのモデルを載せます。

結果

f:id:Pocket7878_dev:20200326192327p:plain

これでこのようにAFrame上にDracoで圧縮されていたモデルがデコードされて表示され無事VRで見ることができました。

コード

今回のコードはpocket7878/draco-aframe-exampleにアップロードしておきました.