今回は前回記事の関連事項となりますが Jsonデータを使用して各ツールでのデータのやりとり関するお話です。
すでにあるデータ形式に関しては必要以上に手を加えず サポート外のデータはJsonでやりとりするのが効率が良いという本家フォーラム(英語)のほうでアドバイスを読みまして おおきくはこのような理由が挙げられているようです。
- Jsonは軽量で各種ゲームエンジン、DCCツール、WebGLなど ほとんどの開発ツールでサポートされているためやりとりに都合が良い
- データ形式ごとにコンバータを作成する場合 バージョンアップなどのたびに手を入れ続けなければいけないため作業効率が良くない。
- すでにあるデータ形式の改造を繰り返すことでツール間でデータの互換性が失われてしまうことがある。
そこで今回はサンプルとしてアンリアルエンジンで提供されているgridExport.mel を pythonにリライトし Jsonで書き出しする機能を追加してみました。連番も対応しています。
Pythonを選択する理由は、メジャーなDCCツールではほとんどでPythonに対応されているためです。元がmelで記述されているため一旦pythonでリライトします。
【Python -Maya側】
『gridExportU.py』
| # Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. pm.radioButtonGrp('myRadBtnGrp1', numberOfRadioButtons=2, label="Export Type", pm.checkBoxGrp('isCached', v1=0, l="Cached Fluid?") startFrame = int(pm.intFieldGrp('startFrame', q=1, v1=1)) else: elif sceneCurTime < startFrame: if pm.radioButtonGrp('myRadBtnGrp', q=1, select=1) == 1: if pm.radioButtonGrp('myRadBtnGrp1', q=1, select=1) == 1: else: else: filePath += ".fga" for z in range(0, res[2]): fileId.write( fileId.close() i = 0 filePath += ".json" voxCount = 0 # Grab the Grid resolution res = map(int, pm.getAttr(myfluidShape + ".res")) x = 0 for iz in range(0, res[2]): exportData = { } writeJsonFile(exportData, filePath) print "> write to json file is seeing: {0}".format(fileName) with open(fileName, "w") as jsonFile: print "Data was successfully written to {0}".format(fileName) return fileName |
GUI部分を除けばJsonのエクスポートをする部分とgetAttrを使用してシーンからデータを配列にコピーするだけなので、Maya以外に対応させるときは、使用するツールごとになんらかのエクスポーターを参考にしてJsonデータを書き出しする箇所を追記するだけで同様なスクリプトが書けると思います。
”import json” でJsonモジュールを読み込んで key[ ]とValue[ ]を別々に配列として登録した後に zip(key,value)で辞書型配列に登録するのが一般的な書き方のようなのですが、データの整列部分をはさんで今回は直接データを辞書型に変換しています。
通常の配列を辞書型の配列に変換すると要素の順番が保持されないため これを整列するためにOrderedDictモジュールをインポートしています。 これによってアルファベット順に辞書型配列内のデータを整列させて読みやすくなります ただしJsonをインポートするときには構造体のKeyを参照して読み込まれるためデータ順はどのようになっていても良いので この部分は無くてもかまいません。
OrderedDict(sorted({"resX": res[0], "resY": res[1], "resZ": res[2]}.items()))
”Python側”のexportDataが書き出しデータを整形するための配列となります。、カスタマイズしたい場合はこの配列内の記述を参考に、書き出されたデータの対応は”Python側” ”Json側"のスクリプトを参考にしてください。
python(json)での表記は { }:辞書型配列 、[ ]:リスト配列型、():タプル型 となります。
Jsonファイルに書き出しをする部分は以下のように
with open(fileName, "w") as jsonFile:
json.dump(dataToWrite, jsonFile, indent=4, separators=(',', ': '))
それぞれ 書き出しは json.dump( ) 読み込みは json.loads( ) を使用します。
参考:YoheiM.NET:[Python] JSONを扱う http://www.yoheim.net/blog.php?q=20150901
”Python側”
| exportData = { } |
”Json側"
| {
…………
], |
【C# Unity側】
『 SerializeBuffer.cs 』
| using UnityEngine; [System.Serializable] [System.Serializable] [System.Serializable] TransformCollection[] MyDataList = new TransformCollection[ MaxFileNum]; } public void ConvertData() var index = 0; foreach (FileInfo f in info) MyHeaderData = JsonUtility.FromJson texture.Apply(); int mip = 0; Color[] cols = new Color[3]; int offset = 0; for (int j = 0; j < info.Length; j++) for (int i = 0; i < maxIndex; ++i) cols[i + offset].r = MyDataList[j].DataArray[i].velocity.x; } } texture.SetPixels(cols, mip); DestroyObject(texture); Debug.Log("Textured_Buffer Exported : " + saveFilePath); Debug.Log("\n Buffer Export Completed ! "); } |
動作は単純で指定したフォルダ内にあるJsonファイルを全て読み込んでデータをテクスチャに変換するだけです。
使用方法はJsonファイルへのフォルダパスと書き出すテクスチャの名前を設定した後スペースキーを押してください Jsonファイルと同じフォルダパスにTextureBufferのPngデータが書き込まれます。
当初サブフォルダまでサーチする仕様でしたが、作業中にサブフォルダに一旦ファイルを避けておくような用途も考えてやめました。
コマ抜きやら順序入れ替えやらのオプション操作も考えましたがwindows上で必要なファイルだけを選ればいいでしょうしツールは単純な方が使い勝手が良いので必要なしと判断しました。
Jsonから読み込んだデータの内容は以下の通り
| Resolution (バッファの解像度) MyHeaderData.HeaderArray[ ].Res velocity.x (x要素) MyDataList[ ].DataArray[ ].velocity.x; |
DataArray[ ]内の要素がvelocity.X、velocity.Y、velocity.Z、でMyDataList[ ]がDataArray[ ]をJsonの数だけ格納されています。
MyHeaderDataは解像度は共通なため一つ分だけ存在します。
【テクスチャデータについて】
【TextureBuffer.png】
テクスチャへのデータのパックはUnity側はGL系なのでUV座標のV方向がフリップしています そのため見た目で下から上に向かって配置されます。 シェーダ側でDirectXを使用する場合はV方向は反転されるためテクスチャは上下が反転した見た目となります。
Velocityのデータはインポートした時すでに0~1の長さに正規化されているので、Color型配列にそのまま代入してテクスチャに書き込んでいます。
1以上の範囲を取るデータの場合はな正規化してから代入してください。詳しくは 以前の配列の転送に関する記事を参考にしてください。
【ファイルパス操作について】
セーブファイル名のパスとファイル名の連結にPathクラスを使用しています。
Path.Combine(_FilePath, _FileName);
Pathクラスの標準関数はマニュアルと同じですが次の通り
- Combine 2つのパスストリングを結合してファイルパスに変換。
- GetDirectoryName ディレクトリパスを返す.
- GetExtension ファイルの 拡張子を返す.
- GetFileName 拡張子を含めたファイルネームを返す.
- GetFileNameWithoutExtension 拡張子抜きのファイルネームを返す.
| Path.GetFileNameWithoutExtension("/Some/File/At/foo.extension")); Path.GetFileName("/Some/File/At/foo.extension")); Path.GetDirectoryName("/Path/To/A/File/foo.txt")); Path.GetExtension("/Some/File/At/foo.extension")); Path.Combine("/First/Path/To", "Some/File/At/foo.txt")); |
Pathクラスのvalue
- AltDirectorySeparatorChar ディレクトリレベルを区切るための代替文字。 (Read Only) '/' Windows 、 '/' macOS
- DirectorySeparatorChar ディレクトリレベルを区切るために使用されるデフォルトの文字。 (Read Only) '\' Windows 、 '/' macOS
【その他】
"maya-json-exporter" https://github.com/Jam3 https://github.com/Jam3/maya-json-export
Ttree.js など MayaのJsonエクスポータはいくつか公開されていますので参考にすると良いかと思います。
melとPython両方に対応されていますが、pythonがOpenMayaで記述されている箇所があり、非プログラマには若干ハードルが高いかもしれないです。 今回の解説をPython+Pymelだけで書いた理由でもあります 他プラットフォームへの移植もめんどうですし。
データが大きく 処理時間がかかりすぎるようであればOpenMayaかC++で書き換えする必要があるかもしれませんが、データをGetする以外は特に重たい処理をしているわけでもないので、わりと速度が気になるほどではないような気がします。
現在すでに手が入っている部分はそのままでよいかと思いますが ちょっとデータが必要だけどコンバータを書くのがめんどくさいとか言う場合に手軽にデータを送ることができるので便利かもしれません。
シェーダ側での配列の扱いについては、次回以降 余裕があれば、ぼちぼち書いていく 予定。
ではまた



