🍰

【逆引きメモ】Pixyz のスクリプトでUnityに取り込む3Dデータを自動処理する

2023/03/18に公開

はじめに

Pixyz Studio、Pixyz Scenario ProcessorではPythonスクリプトを使って一連の処理をバッチ化することができます。
でも、PixyzのAPIリファレンスの情報だけだと理解が苦しかったので、後から見直せるように理解した内容をメモとして残します(随時更新します)。

前提とは言いませんが、Pythonでバッチ処理を記述するのでPhytonの知識があった方が良いです。
きっと、Pythonがわかる人にとっては理解は容易なのだろうと想像します。
私はPython初心者なのでAPIを読んでも良くつまづきます。

環境

  • Pixyz Studio 2022.1 Update 1 r4 (2023年3月時点で最新リリース)

目的別のScriptの書き方

1. APIリファレンスの読み方

AIPリファレンスを開くとまず左ペインにカテゴリが表示されます。
CoreGUI、Core、Geomといった名前領域を選択すると、所属する公開APIが表示されます。
Pixyz StudioのUIメニューと関連する名前もあるので、どの名前領域でどのAPIがあるかをなんとなく推測できますが、正直規則がよくわかってないです。
私がよく使うのはSceneとalgoです。

2. オカレンスの種類

Pixyzでは3Dデータ表現のことをオカレンス(Occurrence)と呼びます。
3D CADだとパーツ、アセンブリのことを指し、Unityに取り込んでからはGameObjectと呼ばれる存在です。
パーツ、アセンブリと表現したことから気づくかもしれませんが、オカレンスにも種類が存在します。
ドキュメントを参考にすると以下の通りです。

  • Part occurrence :ジオメトリ情報(BrepやMesh)を持つ存在。パーツです。
  • Assembly occurrence :Part occurrenceを束ねる存在。アセンブリ/フォルダです。
  • Empty occurrence :何かしらの目的で作成されていますが、子階層にPart occurrenceを持たない、表現には影響がない存在。
  • Light occurrence :照明情報を持つ存在。
3. Typeの理解

APIのfunctionを見るとParametersやReturnsの情報があり、Typeの記述があります。
例えばgetChildren()の場合にはOccurrenceを引数に取り、OccurrenceListを返却します。

実際にこのOccurrenceOccurrenceListにどういう値を入れるのか、ですがScene typesに書いてある説明を読んでも具体的に理解するのが私には難しかったです。。。
これまでの経験から、Occurrenceの持つOccurrence PropertiesにあるIdで良いのかな、と理解してます。
次にOccurrenceListは何かというと、これはそのままPythonのリストで前述のOccurrenceの持つIdをリスト形式でまとめたものです。
例えば[1668,20,31]の様な形。
このOccurrenceとOccurrenceListが理解できるとPixyzのAPIが理解しやすくなります。

4. 3D CADのデータをUnityで使うためにマージする。

3D CADは設計データなので製造をするために必要なレベルで情報が定義されています。
つまり非常に細かい情報の集合で、ボルト、ワッシャー、バネといったレベルまで独立したパーツになっています。
目的によりますが、CADのデータをそのまま使うことはなく必要ないレベルはマージした方がデータは扱いやすくなります。
APIにscene.mergeParts()があるのでそれを使うのですが、何が対象になるのかをOccurrenceListとして渡してあげる必要があります。
その絞り込みに使うのがscene.getFilteredOccurrences()です。
getFilteredOccurrencesは第一引数にFilterExpression、省略可能な第二引数にOccurrenceを指定します。
ここで第二引数を省略したら全量が対象になりますので、全Occurrenceに対して条件に該当するOccurrenceだけを絞り込むのに便利なfunctionです。

Part Occurrenceで、かつまだMeshが貼られていないOccurrenceの一覧を取得

filter = "(Component(\"Part\") AND (Component(\"Part\").Mesh().TriangleCount().ToNumeric() < \"1\".ToNumeric()))"
filteredOccurrences = scene.getFilteredOccurrences(filter)

名前にキーワード(以下ではCaliper)を含まないOccurrenceの子階層のOccurrenceの一覧を取得

filter = 'Component(\"Part\") AND Parent().Property("Name").Matches("^(?!.*Caliper).*")'
thisOcc = scene.getFilteredOccurrences(filter)

名前にキーワード(以下ではBrake)を含むOccurrenceの子階層のOccurrenceの一覧を取得

filter = 'Component(\"Part\") AND Parent().Property("Name").Matches(".*Brake.*")'
thisOcc = scene.getFilteredOccurrences(filter)

名前にキーワード(以下ではCaliper)を含むOccurrenceの子階層のデータをMergedPartという名称のOccurrenceにマージし、不要になったカラのAssembly Occurrenceを削除

filteredOccurrences = scene.getFilteredOccurrences('Component(\"Part\") AND Parent().Property("Name").Matches(".*Caliper.*")')
core.setProperty(scene.mergeParts(filteredOccurrences)[0],"Name","MergedPart")
scene.deleteEmptyOccurrences()

Caliperを対象にMergedPartsを作成した際の様子。

5. Importした3Dモデルの情報にアクセスする

Pixyz Studioを使っているとViewerの左上にPartOccurrencesやTrianglesといった便利情報が表示されています。
Pixyzのスクリプトでそれらの情報を取得する方法について以下に記述します。
PartOccurrencesの数を取得

list = scene.getPartOccurrences(0)
print(len(list))
6. Guided Importで実行している処理

PixyzのImportにはRaw ImportとGuided Importがあります。
Guided Importだとよろしくデータを整えてくれますが、実際には以下の順番で処理を実行しています。
Preserve Original UVsとRemove Duplicated Meshesを有効。

process.guidedImport(これが代表で、個別の処理は以下)
	1. scene.deleteEmptyAnimation (API referenceにはない)
	2. scene.applyTransformation (Occurrenceの位置情報をUnityのTransform形式にする)
	3. algo.deleteFreeVertices (使われてない頂点を除去)
	4. scene.cleanUnusedMaterials (使われてないマテリアルを除去)
	5. scene.mergeMaterials (全マテリアルで同じ表現のマテリアルをマージする)
	6. material.makeMaterialNamesUnique (マテリアル名をユニークにする)
	7. scene.deleteEmptyOccurrences (空のアセンブリ オカレンスを除去)
	8. algo.repairCAD (形状の修復、重複した面の除去)
	9. algo.tessellateRelativelyToAABB (maxSagを判断しながらCADにメッシュを貼る)
	10. scene.getDuplicatedParts (ボリューム、ポリゴン数、範囲の合致率から重複オカレンスを探す)
	11. algo.createInstancesBySimilarity (インスタンスを作成して同じ形状のメッシュの作成を防ぐ)
	12. scene.removeUselessInstances (不要なインスタンスを除去)
	13. algo.repairMesh (つながっていない、または乱れているメッシュを修復)
	14. algo.createNormals (メッシュにノーマルを作成)
	15. algo.mapUvOnAABB (AABBに基づいてテクスチャーマップを作製)
	16. algo.orientNormals (ポリゴンの向きにあわせてノーマルの向きを合わせる)
	17. algo.createTangents (何をしているのかわかってません)
	18. scene.removeSymmetryMatrices (何をしているのかわかってません)
7. Outputウインドウへの情報出力を減らす

Pixyz StudioでUIもしくはスクリプトから処理を実行した場合、その情報がOutputウインドウに出力されます。
自分の書いたスクリプトの実行結果や、標準メニューから実行した処理の内容がOutputに出力されるので便利だと思います。

ただ、実行が安定していたら毎度大量の情報を出力する必要はないと思います。
そのような時はconfigureInteraceLogger()を使うと出力する情報量を調整できます。
core.configureInterfaceLogger(bool1,bool2,bool3)
すべてFalseにすると情報は全く出力されません。
第一パラメータをTrueにするとFunction名が出力されます。
第二パラメーターは第一パラメータとセットでTrueにしますが、Functionのパラメータが出力されます。
第三パラメーターをTrueにするとFunctionの実行時間が出力されます。

個人的な感想としては、第二パラメーターをFalseにするだけでOutputの出力がすっきりするので処理を追いかけやすくなると思います。

Discussion