🏙️

Babylon.js で物理演算(havok):渋谷の空を飛んでみる

に公開

この記事のスナップショット

渋谷駅 ハチ公口 スクランブル交差点付近

https://playground.babylonjs.com/full.html#CWXZW9

操作は (h)キーを押して確認。

(コードを見たい人はURLから full.html を消したURLを指定してください)

ソース

https://github.com/fnamuoo/webgl/blob/main/082

ローカルで動かす場合、./js 以下のライブラリは 069/js を利用してください。

概要

PLATEAU [プラトー]より渋谷の3Dモデルを作成し、物理形状としての街並みを再現しました。
またキャラクターコントローラーで空中遊泳をできるようにしてみました。

PLATEAU [プラトー]に関してはこちらを参照。

ちなみに、ちょっと情報が古いですが Babylon.js レシピ集vol.3 にも PLATEAU に関する記事があります。今となっては購入してまでの価値はちょっと少ないかな。

  • 第8章 PLATEAUデータを軽量化してブラウザで表示してみよう
  • 第9章 ブラウザに表示したPLATEAUデータにオープンデータをマッピングしてみよう

やったこと

  • PLATEAUで3D都市モデルを作成する
  • モデルを読み込んで物理形状とする
  • キャラクターコントローラーで空中遊泳

PLATEAUで3D都市モデルを作成する

Unity Hub, Unity をインストール、PLATEAU SDK for Unityを設定します。

以上が前準備になります。

次に Unity 上で作成するエリアを選んでメッシュ出力します。

毎回、ネットから地図データを取得するのは面倒なので、あらかじめ、G空間情報センターにある渋谷区のサイトから
CityGML(v4)のデータをダウンロードしておきました。上記のインポートにある「3D都市モデルのエクスポート」の手順にしたがって出力します。適当に出力すると400MBほどになります。激重です。

データ量を削減するのが重要になりますが、下記サイトが参考になります。

テクスチャ(画像)が半分以上を占めるので今回は除外しました。
またメッシュについても LOD2(窓やドア)を除外、他にも天井や内装っぽいものは除外して出力すると 17MB になりました。

モデルを読み込んで物理形状とする

3Dデータは BABYLON.ImportMeshAsync で読み込めましたが、このメッシュから物理形状(PhysicsAggregate)を作ろうとするとエラーが発生します。

Error: No valid mesh was provided for mesh or convex hull shape parameter. Please provide a mesh with valid geometry (number of vertices greater than 0).

エラーといっても最初のメッシュのみで発生するようなので、try catch で無視します。

let files = ["./3d/shibuyaV2c/533935_dem_6697_op/533935_dem_6697_op.glb",
            "./3d/shibuyaV2c/53393585_bldg_6697_op/53393585_bldg_6697_op.glb",
            "./3d/shibuyaV2c/53393585_tran_6697_op/53393585_tran_6697_op.glb",
            "./3d/shibuyaV2c/53393586_bldg_6697_op/53393586_bldg_6697_op.glb",
            "./3d/shibuyaV2c/53393586_tran_6697_op/53393586_tran_6697_op.glb",
            "./3d/shibuyaV2c/53393595_bldg_6697_op/53393595_bldg_6697_op.glb",
            "./3d/shibuyaV2c/53393595_tran_6697_op/53393595_tran_6697_op.glb",
            "./3d/shibuyaV2c/53393596_bldg_6697_op/53393596_bldg_6697_op.glb",
            "./3d/shibuyaV2c/53393596_tran_6697_op/53393596_tran_6697_op.glb",
            ];
for (let file of files) {
    BABYLON.ImportMeshAsync(file, scene).then((result) => {
        for (let mesh of result.meshes) {
            try {  // 必ず、0 番目が下記エラーで失敗するっぽい
                let agg = new BABYLON.PhysicsAggregate(mesh, BABYLON.PhysicsShapeType.MESH, { mass: 0, restitution:0.01}, scene);
                let mat = new BABYLON.StandardMaterial('mat', scene);
                mat.diffuseColor = BABYLON.Color3.Random();
                mesh.material = mat;

            } catch(e) {
            }
        }
    })
}

テクスチャが無くシルエットになってますがご愛敬。

キャラクターコントローラーで空中遊泳

キャラクターコントローラーに空中遊泳もどき(ゆっくり落ちる)は既にBabylon.js の基礎調査:森をつくってみるで着手してますが、今回はゆっくり上昇する改修を行います。今回、ホバーだけでなく、上昇のベクトルを追加します。

let characterRisingON = new BABYLON.Vector3(0, 25.00, 0); // 上昇
let characterRisingOFF = new BABYLON.Vector3(0, 0, 0);
let characterRising   = characterRisingOFF;
let characterHoverON  = new BABYLON.Vector3(0, 17.90, 0); // ホバー(滞空
let characterHoverOFF = new BABYLON.Vector3(0, 0, 0);
let characterHover = characterHoverON;

上昇時と下降時の挙動を変更します。
キー操作、ctrl押下時に keyAction.rise を ON として上昇させます。

var getNextState = function(supportInfo) {
    if (state == "IN_AIR_UP") {
        if (characterController.getVelocity().y > 0) {
            if (keyAction.rise) {
                characterRising = characterRisingON;
                characterHover = characterHoverON;
            } else {
                characterRising = characterRisingOFF;
            }
            return "IN_AIR_UP";
        }
        characterHover = characterHoverON;
        return "IN_AIR";
    } else if (state == "IN_AIR") {
        if (characterController.getVelocity().y > 0) {
            if (keyAction.rise) {
                characterRising = characterRisingON;
                characterHover = characterRisingON;
            } else {
                characterRising = characterRisingOFF;
                characterHover = characterRisingOFF;
            }
            return "IN_AIR_UP";
        }
        if (keyAction.rise) {
            characterHover = characterRisingON;
        } else if (keyAction.down) {
            characterHover = characterHoverOFF;
        } else {
            characterHover = characterHoverON;
        }
        if (supportInfo.supportedState == BABYLON.CharacterSupportedState.SUPPORTED) {
            return "ON_GROUND";
        }
        return "IN_AIR";
    } else if (state == "ON_GROUND") {

まとめ・雑感

メッシュを作って表示するだけなら1日で出来ました。
でもメッシュの加工、削減がテクニカルすぎて難しす。部分的に強弱をつけるなんてプロすぎて、素人には手をだせないです。ランドマーク(特徴的な建物)ぐらいテクスチャを貼りたい。
そしてメッシュデータが大きくてストレージがパンク寸前でツライ。

Discussion