Forge Viewer シーンへの複数モデルの表示(一部改定・追記)

Forge Viewer シーンへの複数モデルの表示 のブログ記事でご紹介した内容について、Forge Viewer v7 を反映、一部改訂してご案内したいと思います。

Forge Viewer は、Model Derivative API で SVF 変換したデザインを、ストリーミング配信を使って表示することが出来ます。通常、表示は、変換に使用したシードファイル(CAD 毎に異なる返還前のデザイン ファイル)単位で実装することになります。ただ、コンフィギュレータなど、単一のシードファイルにないモデルを、同一シーンに表示させたいことがあるかもしれません。

Forge Viewer の JavaScript ライブラリでは、そのような用途のために LoadModel メソッドを用意しています。もちろん、この方法で表示させたいモデルも、事前に Model Derivative API で SVF 変換しておく必要があります。

実際の使用では、Forge Viewer:v7 コード などでカンバス内にメインとなるモデルを表示、ジオメトリのロード完了後に LoadModel メソッドを利用する事になります。LoadModel メソッドの最初のパラメータには、カンバス上に表示させたいサブモデルの URN を指定します。この URN 指定では、メインモデルを表示で指定した Base64 エンコードしたシードファイルの URN ではなく、変換済の SVF を示す URN を直接指定します。分かりやすくご紹介するなら、シード ファイルを変換した際に作成されるマニフェストで、SVF を指し示す次の URN 文字列となります。

JSON形式のデータの一部を示しており、urnプロパティが強調表示されています。

ここで Base64 エンコードしたシードファイルの URN を指定してしまうと、Viewer コード実行時に次のエラーが表示されるので分かりやすいはずです。

エラーメッセージが表示されているダイアログボックス: 'Sorry The file extension loaded into the Viewer is not supported. Try a different file.'

上記例だと、指定すべき値は “dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6ZnBkLWphcGFuLWF2cGc1ZmdyaDVxYnBvOGhrMTV
sc3p6ZzhkcmZrbnJvdXdtd2QwcDhsbXNlMzJwN29qb3h6NXB6b251dGktN2cvVG93ZXItTm9Cb3VuZG
FyeS5ud2Q” ではなく、”urn:adsk.viewing:fs.file:dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6ZnBkL
WphcGFuLWF2cGc1ZmdyaDVxYnBvOGhrMTVsc3p6ZzhkcmZrbnJvdXdtd2QwcDhsbXNlMzJwN29qb
3h6NXB6b251dGktN2cvQnVpbGRpbmctTm9Cb3VuZGFyeS5ud2Q/output/0/0.svf” となります。なお、この値の前に、https://developer.api.autodesk.com/derivativeservice/v2/derivatives/ を挿入して指定することも出来ます。Forge Viewer:PDF 図面 でご紹介した  Autodesk.PDF Extenson を使った PDF ファイルのベクトル表示の場合には、https://developer.api.autodesk.com/derivativeservice/v2/derivatives/ を付加した指定以外受け付けませんのでご注意ください。

LoadModel() メソッドで重要になるのは、各モデルの位置合わせです。一般に、CAD データでは尺度という概念を持ち、用途によっては、更に緯度経度といった位置情報も持ち合わせています。ただし、three.js をベースにする Forge Viewer ライブラリには、そのような概念はありません。

この状態で、メインモデル、サブモデル単位に個別に Model Derivative API で変換、LoadModel() で配置した場合、表示されたモデルが干渉してしまったり(重なって表示されたり)、モデルの大きさが変わってしまう結果になってしまいます。

3Dモデルの電車の外観と内部の表示。左側には電車の前面が、右側には車両内の座席やつり革が見える。

Forge Viewer は、ロードするモデルの境界ボックスの中心をカンバス原点に合わせて配置・表示します。つまり、モデル毎に設定される原点が異なるため、上記のように意図しない結果になってしまいます。

このような場合では、Navisworks にすべてのモデルをインポートして、[単位と変換] ツールで各モデル(ノード)に適切な場所に位置合わせした後に、個々に .nwd ファイル形式でエクスポート、Model Derivative API での SVF 変換を実行することで、シーン内の相対位置を維持したまま(適切な場所に位置合せした状態を維持したまま)モデルを扱うことが出来るようになります。

Autodesk Navisworksのインターフェースで、モデルの位置合わせ設定画面が表示されている。青いトラム車両が描かれ、背景は黒く、上部に青い構造物が見える。

位置合せが完了したら、インポート済みのノードを「非表示」にすることで、個別のモデルを保存することが出来ます。

ナビスワークスのインターフェースで、プロジェクトファイル"LRT-Station.nwd"が表示されている。左側には選択ツリー、上部にはツールバーが配置されている。モデルの表示や非表示に関するメニューが開いている。

Navisworks で適切に位置合せした NWD ファイルを個別に Model Derivative API で下 SVF(viewable)して、LoadModel() メソッドで挿入/表示する際には、2 つめの options パラメータで指定する  Global Offset の値を指定することで、Navisworks 上で位置合せした相対位置(オフセット量)を維持しての配置が可能となります。

3Dモデルのトラムと構造物を表示したForge Viewerインターフェースのスクリーンショット。左側にはトラムがあり、右上には位置合わせオプションのボタンが表示されています。
var _viewer = null; var _goffset = 0.0; function initializeViewer() {
    var options = {
        env: 'AutodeskProduction',
        api: 'derivativeV2',  // for models uploaded to EMEA change this option to 'derivativeV2_EU'
        language: 'ja',
        getAccessToken: getCredentials
    };

    Autodesk.Viewing.Initializer(options, function () {
        _viewer = new Autodesk.Viewing.GuiViewer3D(document.getElementById('viewer'));
        startedCode = _viewer.start();
        if (startedCode > 0) {
            console.error('Failed to create a 3D Viewer: WebGL not supported.');
            return;
        }
        urn = "dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6ZnBkLWphcGFuLWF2cGc1ZmdyaDVxYnBvOGhrMTVsc3p6ZzhkcmZrbnJvdXdtd2QwcDhsbXNlMzJwN29qb3h6NXB6b251dGktN2cvTFJULVN0YXRpb24ubndk";
        documentId = 'urn:' + urn;
        Autodesk.Viewing.Document.load(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
        console.log('Initialization complete, loading a model next...');
    });

    function onDocumentLoadSuccess(viewerDocument) {
        var viewables = viewerDocument.getRoot().search({
            'type': 'geometry',
            'role': '3d'
        });

        _viewer.loadDocumentNode(viewerDocument, viewables[0]).then(i => {
            _viewer.setLightPreset(17);
            _viewer.setEnvMapBackground(false);
            _viewer.setGroundShadow(false);
            _viewer.navigation.toPerspective();
        });

        _viewer.setSelectionColor(new THREE.Color(0xFFFF00));
        _viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, onViewerGeometryLoaded);
    }

    function onDocumentLoadFailure() {
        console.error('Failed fetching Forge manifest');
    }

    function onViewerGeometryLoaded(event) {
        _goffset = event.model.getData().globalOffset;
    }
}

function onSuccessCallback(result) {
}

function onErrorCallback(err) {
}

$(document).on("click", "[id^='wo-align']", function () {
    var urn = "https://developer.api.autodesk.com/derivativeservice/v2/derivatives/urn:adsk.viewing:fs.file:dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6ZnBkLWphcGFuLWF2cGc1ZmdyaDVxYnBvOGhrMTVsc3p6ZzhkcmZrbnJvdXdtd2QwcDhsbXNlMzJwN29qb3h6NXB6b251dGktN2cvJUUzJTgyJUFDJUUzJTgzJUE5JUUzJTgyJUI5JUU1JUIxJThCJUU2JUEwJUI5Lm53ZA/output/0/0.svf";
    _viewer.loadModel(urn, { globalOffset: _goffset }, onSuccessCallback, onErrorCallback);
});

$(document).on("click", "[id^='w-align']", function () {
    var urn = "https://developer.api.autodesk.com/derivativeservice/v2/derivatives/urn:adsk.viewing:fs.file:dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6ZnBkLWphcGFuLWF2cGc1ZmdyaDVxYnBvOGhrMTVsc3p6ZzhkcmZrbnJvdXdtd2QwcDhsbXNlMzJwN29qb3h6NXB6b251dGktN2cvJUUzJTgyJUFDJUUzJTgzJUE5JUUzJTgyJUI5JUU1JUIxJThCJUU2JUEwJUI5X2FsaWduLm53ZA/output/0/0.svf";
    _viewer.loadModel(urn, { globalOffset: _goffset }, onSuccessCallback, onErrorCallback);
});

同じモデル群に対して位置合わせ等をテストする場合、もし、コードを修正しても表示に反映されず、期待した結果にならないなら、ブラウザ キャッシュをクリアすると問題が解消することがありますのでお試しください。

By Toshiaki Isezaki