PLATEAUの街データを軽量化してスマホアプリで表示するまで~Mac

UNITY

PLATEAUのデータは以下からダウンロード

3D都市モデル(Project PLATEAU)東京都23区 - G空間情報センター
航空測量等に基づき取得したデータから建物等の地物を3次元で生成した3D都市モデルです。 商用利用も含め、どなたでも無償で自由にご利用いただけます。 特徴 3D都市モデルとは、都市空間に存在する建物や街路といったオブジェクトに名称や用途、建設年といった都市活動情報を付与することで、都市空間そのものを再現する3D都市空間情...

FBXがよい感じだった

図郭マップで必要な区画のデータを確認して使うイメージ

FBXダウンロードデータの内容

bldg

→建物。lod1は軽いけどテクスチャ貼られてない。lod2はテクスチャあり

brid

→高架道路とか橋とか

dem

→地面。テクスチャはない;;

tran

→道の線。地図みたいな

unityにはlod2を使うけど重すぎでスマホで動かないので軽量化を行う

Macなので結論としてはMeshBakerを使った

Mesh Baker | モデリング | Unity Asset Store
Get the Mesh Baker package from Ian Deane and speed up your game development process. Find this & other モデリング options on the Unity Asset Store.

MeshBakerを使った軽量化の手順

アセットをいれたら

ヒエラルキーにTextureBakerができるのでそれのInspecterをみる

Open Tools For Adding Objectsボタンでウィンドウを開く

Search For Meshes To Add タブを表示して

Exclude meshes with out-of-bounds UVs のチェックを外す

軽量化したいファイルをヒエラルキーに配置して、親オブジェクトでまとめておく。それを選択した状態で
Add Selected Meshes To Targetボタンを押す

最初のヒエラルキーにTextureBaker(0)のInspecterのObjects To Be Combinedにデータがセットされる。↑上記では4731

その下のCreate Empty Assets For Combined Materialボタンを押して任意の場所にデータを作る。→Texture Back ResultとCombined Mesh Materialにデータがセットされる。

Max Atlas Sizeを減らしたらガビった。一旦8192で

Back Materials Into Combined Material ボタンを押す。しばらく待つ。

完了したらヒエラルキーのTextureBaker(0)の中のMultiMeshBakerを選ぶ

InspectorでOutputをBack Into Prefabにする

ヒエラルキーにEmptyオブジェクトを作成してProjectにドラッグしてプレハブ化する

それをCombined Mesh Prefabにセットする

Use Shared SettingsはNoneにする

Backを押す。しばらくまつ。

ヒエラルキーに作成したプレハブに軽量化されたデータができている。元のFBXは非表示にして確認してみる。

最初のドローコールが3457とかだったのが64!?





軽量化後

WARNING -> applicationDidReceiveMemoryWarning()

対象区画のbldgとbridとdemをセットで設置して、Macでは実行できたけどiPhone実機ではメモリ不足で動かず・・原因を究明すると

demでした!1枚meshで問題ないと思ってたら..

広大な地面の1枚mesh。blenderで編集しようとしたけど超大量の頂点があってめちゃヘビー。平らな板だと街のビルがところどころ浮いちゃうし。

ということでdemの地面meshをterrainに変更した。

Special Thanks!↓

【Unity】オブジェクトからTerrainの地形データを自動生成する - 株式会社ライトコード
Unity(ユニティ)でのマップ生成は大変!ロールプレイングゲームやアクションゲームを作成する中で、最も大

上記より

Object2Terrain.cs

using UnityEngine;
using UnityEditor;

public class Object2Terrain : EditorWindow
{

    [MenuItem("Terrain/Object to Terrain", false, 2000)]
    static void OpenWindow()
    {

        EditorWindow.GetWindow<Object2Terrain>(true);
    }

    private int resolution = 512;
    private Vector3 addTerrain;
    int bottomTopRadioSelected = 0;
    static string[] bottomTopRadio = new string[] { "Bottom Up", "Top Down" };
    private float shiftHeight = 0f;

    void OnGUI()
    {

        resolution = EditorGUILayout.IntField("Resolution", resolution);
        addTerrain = EditorGUILayout.Vector3Field("Add terrain", addTerrain);
        shiftHeight = EditorGUILayout.Slider("Shift height", shiftHeight, -1f, 1f);
        bottomTopRadioSelected = GUILayout.SelectionGrid(bottomTopRadioSelected, bottomTopRadio, bottomTopRadio.Length, EditorStyles.radioButton);

        if (GUILayout.Button("Create Terrain"))
        {

            if (Selection.activeGameObject == null)
            {

                EditorUtility.DisplayDialog("No object selected", "Please select an object.", "Ok");
                return;
            }

            else
            {

                CreateTerrain();
            }
        }
    }

    delegate void CleanUp();

    void CreateTerrain()
    {

        //fire up the progress bar
        ShowProgressBar(1, 100);

        TerrainData terrain = new TerrainData();
        terrain.heightmapResolution = resolution;
        GameObject terrainObject = Terrain.CreateTerrainGameObject(terrain);

        Undo.RegisterCreatedObjectUndo(terrainObject, "Object to Terrain");

        MeshCollider collider = Selection.activeGameObject.GetComponent<MeshCollider>();
        CleanUp cleanUp = null;

        //Add a collider to our source object if it does not exist.
        //Otherwise raycasting doesn't work.
        if (!collider)
        {

            collider = Selection.activeGameObject.AddComponent<MeshCollider>();
            cleanUp = () => DestroyImmediate(collider);
        }

        Bounds bounds = collider.bounds;
        float sizeFactor = collider.bounds.size.y / (collider.bounds.size.y + addTerrain.y);
        terrain.size = collider.bounds.size + addTerrain;
        bounds.size = new Vector3(terrain.size.x, collider.bounds.size.y, terrain.size.z);

        // Do raycasting samples over the object to see what terrain heights should be
        float[,] heights = new float[terrain.heightmapWidth, terrain.heightmapHeight];
        Ray ray = new Ray(new Vector3(bounds.min.x, bounds.max.y + bounds.size.y, bounds.min.z), -Vector3.up);
        RaycastHit hit = new RaycastHit();
        float meshHeightInverse = 1 / bounds.size.y;
        Vector3 rayOrigin = ray.origin;

        int maxHeight = heights.GetLength(0);
        int maxLength = heights.GetLength(1);

        Vector2 stepXZ = new Vector2(bounds.size.x / maxLength, bounds.size.z / maxHeight);

        for (int zCount = 0; zCount < maxHeight; zCount++)
        {

            ShowProgressBar(zCount, maxHeight);

            for (int xCount = 0; xCount < maxLength; xCount++)
            {

                float height = 0.0f;

                if (collider.Raycast(ray, out hit, bounds.size.y * 3))
                {

                    height = (hit.point.y - bounds.min.y) * meshHeightInverse;
                    height += shiftHeight;

                    //bottom up
                    if (bottomTopRadioSelected == 0)
                    {

                        height *= sizeFactor;
                    }

                    //clamp
                    if (height < 0)
                    {

                        height = 0;
                    }
                }

                heights[zCount, xCount] = height;
                rayOrigin.x += stepXZ[0];
                ray.origin = rayOrigin;
            }

            rayOrigin.z += stepXZ[1];
            rayOrigin.x = bounds.min.x;
            ray.origin = rayOrigin;
        }

        terrain.SetHeights(0, 0, heights);

        EditorUtility.ClearProgressBar();

        if (cleanUp != null)
        {

            cleanUp();
        }
    }

    void ShowProgressBar(float progress, float maxProgress)
    {

        float p = progress / maxProgress;
        EditorUtility.DisplayProgressBar("Creating Terrain...", Mathf.RoundToInt(p * 100f) + " %", p);
    }
}

地面をterrainに変更したら大丈夫でした!

そのほかのポイント

・ビルのmeshで少し修正したいところがあって直接unityのprobuilderを使って修正したら重くて変更できなくなった。→重いオブジェクトが存在するファイルではprobuilderは使わないほうがよい
Not allowed to call RecalculateNormals() on mesh 'surfaceMember(Clone)'

MeshBakerを使うときに、元ファイルのビルのマテリアルがなぜか(clone)とか(instance)になっているとうまくいかないので、元のbldgのプレハブを置き直すとマテリアルの(instance)が取れてエラーもなくなった

コメント

タイトルとURLをコピーしました