Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ jobs:
run synthetic_multi.unitypackage expectations_synthetic_prefabs_stack.json
run synthetic_scene.unitypackage expectations_synthetic_scene.json
run synthetic_scene.unitypackage expectations_synthetic_scene_prefabs.json
run synthetic_scene.unitypackage expectations_synthetic_scene_keep_unused.json
run synthetic_scene.unitypackage expectations_synthetic_scene_prefabs_keep_unused.json
run synthetic_urp.unitypackage expectations_synthetic_urp.json
run synthetic_urp_standard.unitypackage expectations_synthetic_urp_standard.json
run synthetic_hdrp.unitypackage expectations_synthetic_hdrp.json
Expand Down
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ blender -b --factory-startup --python tests/integration_import.py -- _local/unit
blender -b --factory-startup --python tests/make_synthetic_scene_package.py # シーン用の合成パッケージ(_local/unitypackages/ に出る)
blender -b --factory-startup --python tests/integration_import.py -- _local/unitypackages/synthetic_scene.unitypackage tests/expectations_synthetic_scene.json # 読み込む単位 Scenes
blender -b --factory-startup --python tests/integration_import.py -- _local/unitypackages/synthetic_scene.unitypackage tests/expectations_synthetic_scene_prefabs.json # 読み込む単位 Prefabs(LODGroup の非表示)
blender -b --factory-startup --python tests/integration_import.py -- _local/unitypackages/synthetic_scene.unitypackage tests/expectations_synthetic_scene_keep_unused.json # Scenes で使われていない部品を読み込んで隠す(Skip Unused Parts を OFF)
blender -b --factory-startup --python tests/integration_import.py -- _local/unitypackages/synthetic_scene.unitypackage tests/expectations_synthetic_scene_prefabs_keep_unused.json # Prefabs で同じく OFF
blender -b --factory-startup --python tests/make_synthetic_urp_package.py # URP 用の合成パッケージ(ライトが URP の単位になるので別にする。マテリアルを Standard にした _standard も出る)
blender -b --factory-startup --python tests/integration_import.py -- _local/unitypackages/synthetic_urp.unitypackage tests/expectations_synthetic_urp.json # URP Lit の読み方と URP のライト
blender -b --factory-startup --python tests/integration_import.py -- _local/unitypackages/synthetic_urp_standard.unitypackage tests/expectations_synthetic_urp_standard.json # ライトの追加データからの URP の判定
Expand Down
5 changes: 3 additions & 2 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Blender から `.unitypackage` を直接読み込み、メッシュ(アーマ

1〜3 は「FBX マテリアル 1 つに .mat を 1 つ」決める順序。Unity で実際に表示されるのは Renderer の `m_Materials` で、externalObjects は FBX を置いたときの既定値にすぎないので、prefab に割り当てがあれば下記のスロット単位の分割で 1・2 の結果より優先する。 FBX がマテリアルを 1 つも持たない場合は 1〜3 の対象が無いので、prefab の割り当てだけからスロットを作る(下記「マテリアルを持たないモデル」)。

**prefab とモデルの対応**(`core/prefab.py`、Issue #25): prefab の表はモデルごとに作る。Renderer がどのモデルのものかは、メッシュ参照(MeshRenderer と同じ GameObject の MeshFilter、または SkinnedMeshRenderer の `m_Mesh`)の GUID で決め、メッシュ参照が無い Renderer とパッケージ外のメッシュを指す Renderer は使わない。1 つの prefab が複数モデルを含む場合も Renderer 単位で振り分けるので、別モデルの同名オブジェクトには当てはまらない。読み込む単位 Models では、モデルごとに「そのモデルを使う prefab」だけを候補にし、候補をパス順に先勝ちで統合する(`PreparedPackage.table_for`)。表は `hierarchy.Expander` で展開した prefab の配置(`placements`)から作るので、名前の決め方(Unity の複製番号「 (N)」を外す、.meta の表で引いたメッシュ名を優先する)も Scenes 単位と同じ。名前で引けないとき(prefab で GameObject の名前を変えたもの)は、オブジェクト名から求めたメッシュの fileID を Renderer のメッシュ参照と照合する。FBX を置いただけの PrefabInstance で、中への上書きを 1 つも名前で引けないものは表に入れない。Models 単位の統合は、名前に加えてメッシュ参照の fileID でも先勝ちにする(Prefab Variant で名前を変えた行と、後の prefab の元の名前の行が並んだとき、パス順で先の prefab の割り当てを使うため)(Issue #71)。
**prefab とモデルの対応**(`core/prefab.py`、Issue #25): prefab の表はモデルごとに作る。Renderer がどのモデルのものかは、メッシュ参照(MeshRenderer と同じ GameObject の MeshFilter、または SkinnedMeshRenderer の `m_Mesh`)の GUID で決め、メッシュ参照が無い Renderer とパッケージ外のメッシュを指す Renderer は使わない。1 つの prefab が複数モデルを含む場合も Renderer 単位で振り分けるので、別モデルの同名オブジェクトには当てはまらない。読み込む単位 Models では、モデルごとに「そのモデルを使う prefab」だけを候補にし、候補をパス順に先勝ちで統合する(`PreparedPackage.table_for`)。表は `hierarchy.Expander` で展開した prefab の配置(`placements`)から作るので、名前の決め方(Unity の複製番号「 (N)」を外す、.meta の表で引いたメッシュ名を優先する)も Scenes 単位と同じ。名前で引けないとき(prefab で GameObject の名前を変えたもの)は、オブジェクト名から求めたメッシュの fileID を Renderer のメッシュ参照と照合する。それでも引けなければ、表の名前の連番も外して比べ、該当する行が 1 つだけならそれを使う(`prefab.find_renderer`。Issue #155)。FBX のノード名がもともと `Base-Ring.001` のように連番付きだと、同じ FBX を 2 回目に読み込んだとき Blender が `Base-Ring.002` に付け替え、オブジェクト名の連番を外しただけでは表の `Base-Ring.001` と一致しないため(以前は 2 回目以降の色違いの prefab で .mat が当たらず、FBX のマテリアルのままだった)。連番を外すと同じになる行が複数あれば(`Body` と `Body.001` が両方ある FBX)、どれか決められないので引かない。Scenes 単位の LOD の段と無効な Renderer の判定(`scene_import.parts_to_hide`)も同じ形で照合し、使われていない部品の判定は、表の名前の連番を外した形が 1 つでも一致すれば使っている側に倒す。FBX を置いただけの PrefabInstance で、中への上書きを 1 つも名前で引けないものは表に入れない。Models 単位の統合は、名前に加えてメッシュ参照の fileID でも先勝ちにする(Prefab Variant で名前を変えた行と、後の prefab の元の名前の行が並んだとき、パス順で先の prefab の割り当てを使うため)(Issue #71)。

**読み込む単位 Prefabs**(§3.3、Issue #47): 選んだ prefab ごとに、その prefab の表だけを当てはめてモデルを読み込む(`ImportOptions.unit` / `prefab_paths`)。同じモデルを使う prefab(色違いなど)を複数選ぶと、prefab ごとにモデルを読み直す。読み直したモデルのマテリアルのうち、組み立て済みの .mat と同じものは新しく組まずに同じ Blender マテリアルを使い回し(下記「マテリアルの共有」)、prefab の表と違うスロットだけを下記の分割で差し替える。読み込めないモデルを含む prefab は、そのモデルだけ飛ばして警告に出す。

Expand All @@ -57,7 +57,8 @@ Blender から `.unitypackage` を直接読み込み、メッシュ(アーマ
- 座標変換(`core/transform.py`): Unity のモデル空間 (x, y, z) は Blender では (-x, -z, y)。Unity でモデルのルートに掛かる行列 M は、Blender では C·M·C⁻¹ を原点に読み込んだオブジェクトの行列に左から掛ける。Unity 6 で合成 FBX を 3 通り(FBX を中に置いた prefab、空の親の下への直置き、非一様スケールで置いて中の子を上書きした展開 prefab)に置いたシーンを作り、Renderer ごとの頂点のワールド座標(スキンしたメッシュはボーン行列 × bindpose で計算)と、Blender の FBX インポーターで読んだ同じ FBX から予測した座標が、9 か所すべてで一致することを確かめた。Blender 由来の FBX では、Unity のルート直下のノードは X -90 度・スケール 100 を持つが、Blender のオブジェクトはその値を持たない。そのためノードの値を直接オブジェクトに当てず、行列で計算してから差分として当てる。
- 読み込み: 配置のルートと、その祖先の GameObject を Empty にし(行列は Unity のローカル行列を C·M·C⁻¹ で変換)、モデルの最上位のオブジェクトをルートの Empty の子にする。.meta の `globalScale` は親の逆行列として掛ける。同じモデル・同じマテリアルの割り当ての配置は、2 つ目からメッシュ・アーマチュアのデータを共有した複製(`Object.copy()`)にし、アーマチュアモディファイアとコンストレイントの参照を複製側に付け替える。割り当てが違えば読み直す(組み立て済みの .mat は、下記「マテリアルの共有」のとおり共有)。展開 prefab の中のノードが上書きで動いていれば、そのノードから逆算したルートの行列でオブジェクトを置き直す(親から順に。ワールド行列は評価を待たずに親をたどって計算する。アーマチュアで変形するものは動かさず件数を警告に出す)。非アクティブな GameObject と無効な Renderer は `hide_render` をその場で立て、`hide_set` は読み込みがすべて終わってからまとめて当てる(下記の理由で、読み込み中はビューレイヤーに無いため)。
- 読み込み中のビューレイヤー: インポーターのオペレーターは呼ぶたびにビューレイヤーの中身を評価し直すので、読み込み済みのオブジェクトが増えるほど 1 回が重くなる(3000 オブジェクトで 1 回 180 ms。Japanese Street の Day_Showcase の読み込み 16 秒の大半)。パッケージのコレクションは読み込みが終わるまで `exclude` し、モデルは空の作業用コレクション「<パッケージ名> (importing)」に読み込んでから配置先へ移す。終わったら(失敗しても)除外を戻して作業用コレクションを消す。これで Day_Showcase は 1.7 秒になった(Issue #75)。
- 使われていない部品(Issue #58): 展開した prefab・シーンの Renderer から作った配置では、Unity にあるのは表の Renderer だけなので、表に無いメッシュのオブジェクト(prefab が使っていない LOD、FBX にだけある別のノードなど)を非表示にする(削除はしない。件数を警告に出す)。FBX を直接置いた配置は対象にしない。Renderer の名前は GameObject 名ではなくメッシュ参照(MeshFilter / SkinnedMeshRenderer の `m_Mesh` の fileID)から決める。展開後に GameObject の名前を変えていると、名前の照合では使っている部品まで隠してしまうため。モデルの .meta の表(`fileIDToRecycleName`、古い番号を引き継いだ `internalIDToNameTable`)で名前を引き、表の無い新しい形式では fileID = xxHash64("Type:Mesh-><名前>0")(`core/unity_ids.py`。Unity 6 の prefab と Japanese Street の 100 か所で一致)を Blender のオブジェクト名で計算して照合する。
- 使われていない部品(Issue #58): 展開した prefab・シーンの Renderer から作った配置では、Unity にあるのは表の Renderer だけなので、表に無いメッシュのオブジェクト(prefab が使っていない LOD、FBX にだけある別のノードなど)は Unity に無い。既定ではこれを読み込まず(ダイアログの「Unused Parts」、`ImportOptions.skip_unused_parts`。Issue #152)、オプションを切ると読み込んだうえで非表示にする(件数はどちらも警告に出す)。FBX を直接置いた配置は対象にしない。読み込む単位 Prefabs でも、prefab がモデルを丸ごと置いていなければ(モデルの PrefabInstance の配置が無ければ。`prefab.models_placed_whole`)、表に無いメッシュを同じように隠す(Issue #151。以前は Prefabs 単位にこの処理が無く、部品をまとめた FBX のメッシュを 1 つだけ指す prefab で、FBX の全部品が表示されていた)。Renderer の名前は GameObject 名ではなくメッシュ参照(MeshFilter / SkinnedMeshRenderer の `m_Mesh` の fileID)から決める。展開後に GameObject の名前を変えていると、名前の照合では使っている部品まで隠してしまうため。モデルの .meta の表(`fileIDToRecycleName`、古い番号を引き継いだ `internalIDToNameTable`)で名前を引き、表の無い新しい形式では fileID = xxHash64("Type:Mesh-><名前>0")(`core/unity_ids.py`。Unity 6 の prefab と Japanese Street の 100 か所で一致)を Blender のオブジェクト名で計算して照合する。
- 読み込まないときの消し方(Issue #152): シーンでは配置ごとに FBX のテンプレートを複製するので、最初に読み込んだモデルから使われていない部品を消し、残りからテンプレートを作る。以後の配置では必要な分だけを複製する(部品を 1 つの FBX にまとめ、prefab がそのうち 1〜3 個だけを指す Japanese Train Station PBR Pack の DemoScene では、オブジェクトが 31,290 から 1,167 に、読み込みが 34 秒から 3.4 秒になった)。消す部品はテンプレートを使い回す条件で決まるよう、使い回しのキーに Renderer から作った配置かとメッシュ参照の fileID を加える。消すものは `scene_import.parts_to_remove` が決める。使われていないメッシュでも、子孫に残すもの(使う部品)があれば親子関係と位置を保つため残して隠し、子がすべて消えた Empty・アーマチュアは消す。消したオブジェクトだけが使っていたメッシュ・アーマチュアのデータも消す。マテリアルは同じ回の別のモデルと共有するので(#77)消さない。Prefabs 単位では prefab ごとに読み込んだモデルから同じように消す。
- LOD(Issue #104): Unity の LOD は prefab・シーンの `LODGroup`(クラス ID 205)が持ち、`m_LODs` の段ごとに Renderer の fileID が並ぶ。段ごとの GameObject はどれも Renderer を持つので、そのままでは全段が重なって表示される。`parse_asset` で段を読み、`RendererInfo.lod_level`(0 = もっとも細かい LOD0)に付けて、読み込み側が LOD1 以降を非表示にする(削除はしない。段は `unity_lod` カスタムプロパティに残し、件数を警告に出す。ダイアログの「LODs」トグルで切れる)。LODGroup はシーンから prefab インスタンスの中の Renderer を指すことがあるので、段の解決は PrefabInstance の差し込みが終わった後に、stripped ドキュメントの別名を通して行う。1 つの LODGroup の中で同じ Renderer が複数の段にあればもっとも細かい段を採り、内側の prefab で付いた段は外側で上書きしない(内側で遠景用だったものは隠れたままにする)。`m_Enabled: 0` の LODGroup は Unity でも全段が描かれるので段を付けない。適用するのは Scenes と Prefabs 単位(Models 単位の表は prefab をまたいで統合したものなので対象にしない)。外側から LODGroup 自体を無効化・削除する上書きは反映しない。`_LOD1` のような命名規則からの推測はしない(LODGroup の無いモデルは今までどおり全部表示する)。
- Renderer をまとめるモデルのルート(Issue #60): 以前は Transform を直接持つアセットでの最上位の祖先に固定していたため、シーンの共通の親(部屋など)の下に同じ小物を複製して並べると、名前が同じ Renderer が 1 つだけ残り、しかも親の原点に置かれていた。モデルの .meta の表(`fileIDToRecycleName` / `internalIDToNameTable`)にある、ルート以外の GameObject 名を FBX のノード名として、次のように決める(`core/hierarchy.py` の `placements`)。
- 1 メッシュの FBX(表の GameObject はルートだけで、メッシュも 1 つ): Renderer の GameObject 自身。この形の FBX では、Unity はノードを `//RootNode` に畳んでその変換をモデルのルートの Transform に載せ、メッシュの頂点はノード空間のままにするので、メッシュを直接指す Renderer にはノードの変換が掛からない。そこで、下のノード自身をルートにした配置と同じく `node_transforms`(名前は表のメッシュ名)でそのノードを単位行列にする。これをしないと、ノードが原点から離れた FBX(街並みアセットの歩道など)がノードの移動ぶんずれる(Issue #98。Unity 6000.6.0f1 で確認)。
Expand Down
Loading
Loading