PlateauのCityGMLをBlenderで読み込むアドオンを書いた



# Plateauの公式で配布されているデータがちょっと使いづらい
みなさん、Plateauのデータ活用されているでしょうか？
そもそもPlateauってなにという人は[こちら](https://www.mlit.go.jp/plateau/)の公式サイトを見てください。
見てない方向けに端的に言うと建物3DデータなどをCityGMLという形式で配っています。
また、そのほかにもFBXとOBJファイルでも配られていてこちらは、一見手軽に使えそうな気がするんですが、平面直角座標系という座標系で三次元座標に変換されているのですが、この座標系の場合中心が遠くになってしまう場合が多く非常に使いづらいです。
そのため、編集モードで全選択して中心に持ってくるなどの方法が紹介されていますが、この場合緯度経度と三次元座標の対応がわからなくなってしまいます。
この方法だと地域メッシュの中心が中心には来ず、ずれた位置に来ることになります。

# BlenderでCityGMLを直接読み込みたいと思わないかね
ということでそもそもBlenderで直接CityGMLを読み込めればいいのではということで
GMLを読みこむアドオンというのがあったので利用してみたところ、まともに読み込めませんでした。

ということで難儀していたのですが、Blenderのスクリプトをいじっていた経験を思い出して自分で書けるのでは？？？と思ったので作りました。

手始めに、物体を破壊するアニメーションなどをボーンアニメーションに変換するスクリプトを[アドオン化](https://github.com/nneri/blender-rtbone)したりしました。

https://github.com/nneri-hin/Plateau-Blender-Importer


# 特徴
前段が長くなったのですがアドオンの紹介です

- 読み込みが比較的軽量かもしれない。(再帰関数を多用しているのでそんなに軽量ではない）
- 建物を読み込む際は建物ごとのオブジェクトとなる
- 指定した地域メッシュコードの中心をオブジェクトの中心にできる
- テクスチャも読み込めます
- 重複頂点は読み込み時に結合されます
- 道路データなど広い範囲が一つのファイルに入っているデータを軽量にするため距離制限をかけて読み込み可能

# 使い方

1. まず、[こちらのGithubのページ](https://github.com/nneri-hin/Plateau-Blender-Importer)のCodeからzipファイルをダウンロードします。
1. Blenderのプリファレンスのアドオンのインストールから落としたzipファイルを開きます。
1. 有効のチェックボックスをいれます
1. ファイル->インポート->Plateau City GML(.gml)を選びます
1. 読み込みたいファイルを選び、右側のOrigin Japan Mesh Codeに中心にしたい地域メッシュコードを入れます。
1. 必要に応じてScaleの値を変更します（オブジェクトのサイズを変えられます）
1. 必要に応じて距離制限をかけます（値を+にして入れるとkm単位で緯度方向、経度方向に制限をかけて読み込めます）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2643512/0718d0eb-6fbd-a202-bb64-e159dc60e6cb.png)
こんな感じで読み込めます。デフォルトの中心の地域メッシュコードは東京駅のあたり

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2643512/4a6ded19-1b41-308e-9c2b-433e48bd94e0.png)
スケール等倍で読み込む場合はビューポートの表示制限を長くすると見えるようになります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2643512/30e0d600-2386-dfa3-5065-57a1a48ab61c.png)
こんな感じでガタガタ見えますがおそらく深度周りの浮動小数点の精度の問題でしょうこのスケールで扱うとどうしようもないのかもしれない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2643512/f64748fa-330b-f708-335b-3dfb3015de64.png)
レンダリングするとちゃんと正常に見えます、レンダリング時もやはりカメラの描画距離を延ばすとちゃんと描画されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2643512/55255e95-fe1d-41fd-0e92-d10f2a3d38d8.png)
カメラを選択してカメラ設定のレンズの設定の終了をデフォルトの100mから10000m(10km)に変更しています
余談ですが、ここに10kmとか入力するとメートルに変換してくれます




複数のファイルを同時に読み込む場合は基準となるメッシュはそのままに読み込むといいと思います。
距離制限は主に道路データなど向けです。
こちらビルよりかなり広い範囲で読み込む設定となっていて

緯度経度を中心にしたい人は現状ではソースコード改変してもらうしかありません（UIのいじり方よく理解してないので）
python使えれば改変自体は難しくないと思います

# 問題点、注意点など
- 過去のアドオンが入った状態だと一度Blenderを落とさないと正常にアップデートできません。(__init__.py以外が再読み込みされないため、対処法教えて)
- 平面直角座標系ではなくヒュベニの公式を利用しているため、ほかのデータと重ねた際にずれるかも
- テクスチャバグ、一部の建物でテクスチャがおかしくなるバグを確認。理由は一つの建物に複数のテクスチャが割り当てられていることが原因だと思ってます
- ~~再帰読み込みを多用していてpython的に遅いと思われる（何とかしたいけどおそらく難しいのでやらない）~~ 重いのは別のところだったようだ(追記の方に詳しく)

# 余談
ファイルに含まれるLoadGml.pyはBlenderのbpyなどに依存せずPython標準のみで動作しています。
なのでBlender以外のPythonで使いたい場合は一応使えます。
pythonで3Dのポイントデータ使う用途がBlender以外にあるかは知りませんが。


# 追記 (2022/12/30) 
 **修正点その1 重かった部分の解消、そのほか**
メインはその他の方なんですが、データを読み込んだ時オブジェクトが現在のコレクションの直下に大量生成されており、管理上大変不便だったのでコレクションを作ってその下に置くように修正しました。
高速化はその副産物です。
Blenderアドオン中でオブジェクトを生成する際、メッシュを作成してそれをオブジェクトとして登録することで初めてUI上で見えるようになるというのがBlenderの仕様なのですが、オブジェクト化する際にcontextの下に置くという風にしていたのですが、その部分が重かったようで、作成したコレクションの下に作成したオブジェクトをリンクするという方式の方が断然早かったようです。
(contextが何かというのはBlenderでスクリプトをやるとわかると思います）


```python
# 旧バージョン
object_utils.object_data_add(context, n_mesh, operator=None)

#新バージョン
collection = bpy.data.collections.new(name)
bObject = bpy.data.objects.new(obj.id,n_mesh)
collection.objects.link(bObject)
```
というか単にobject_utilsが重かったのか。
いろいろ調べた感じbpy.opsは重いのでなるべく使わない方がいいっぽいのですがおそらくその系統。
contextなどを使うとおそらくUI周りを経由したりするので重いのかもしれません。
Blenderアドオン開発は調べ方難しくて困る。


 **修正点その2 モデルのバグの解消**

この記事を書いた段階では気づいてなかったんですが、Zファイトを起こす場合がありました。
Plateauのデータにはlod0,lod1,lod2の3段階あるのですが、複数のlodがある場合全部描画するという方式を取っていました。(テクスチャがある場合だけロジックを変えていたはず）
で、複数のlodレベルあるなら一番細かいのだけとればいいんじゃないかとやってみようと思ったのですが、さらに問題がありました。
なんと、lod0のデータ、屋根の部分の情報しかありません、しかもlod1とほぼ同じ形状。
同一座標の頂点はマージするという仕様のためlod1とlod0の頂点がマージされてさらにおかしなことになってた。つらい
ちなみにlodに関する情報は4種類あって以下のような感じになっています

|  |  |
|:-:|:-:|
| 　lod1RoofEdge |  Lod0の屋根部分のデータ |
|  lod0FootPrint | Lod0の接地面のデータ  |
|  lod1Solid |  Lod1のデータ |
|  lod1Solid |  Lod2のデータ |

実際には、lod2はリンクになっていてlod2MultiSurfaceの下にデータがありました。
とりあえずlodが細かい方優先、ただしlod1FootPrintがない場合はLod1を使わないという方針で組みました
ただし、あんまりうまく行ってないと思われます、どうもlod0FootPrintの情報ないんじゃないかと思ってます。
真面目にドキュメント読み込んでないのであんまりわからない。