Filedialog設定で変わる!? FBX Export挙動の話

おはこんばんちわ。TAチームリーダーの山本です。
今回は久しぶりの執筆になります。

この記事はMayaアドベントカレンダー2023の7日目の記事です。
https://qiita.com/advent-calendar/2023/maya

はじめに

ゲーム開発におけるパイプライン整備なんかをしていると、FBXファイル周りの取り扱いは避けて通れないお仕事かと思います。
ほとんどの開発パイプラインで、中間ファイルとしてFBXを出力し、各種ゲームエンジンや実装環境へインポートする…といった流れです。

そんな中、MayaのfileDialogの設定を変更するとFBXファイルのエクスポート周りの挙動に差異が生じる…という現象に遭遇し、少々苦しめられました。

今回は、その時得られた知見と教訓を以下にまとめて共有したいと思います。

なお、検証に使用したMayaバージョンは Maya2024.1です。

結果を先に

まずは、「起こった問題」と「その解決策」について、簡単に説明します。

発生した問題

  • MayaのfileDialogをOSネイティブ設定にすると、FBXファイルのエクスポートの挙動が変わる
    • fileコマンドで、FBX Exportに関するファイルタイプが「FBX export」ではなく「FBX」になる。
    • そのため、例えばスクリプト上であらかじめファイルタイプを「FBX export」などと明示的に指定してあると、処理が走った際に「そんなファイルタイプは存在しねぇぜ!」とか言われて怒られる…(エラーで止まる)
    • fileDialogがOSネイティブでも、ファイルタイプを「FBX」で指定しておけば処理は走るが、FBXのインポート・エクスポートが実行される直前で必ず「FBXの設定ウィンドウ」が開かれて、FBXのインポート・エクスポートの完了まで実行されない。
  • ただし、fileコマンド以降のスクリプトはそのまま実行されるため、スクリプトやToolの設計次第では想定外の不具合につながることが考えられる。
  • なお、FBX Importも挙動が変わる。
    • Export同様に、直前で設定UIが開いて処理が止まってしまう
    • そのため、fileコマンドの返り値で「新しいノードのリスト」などを返すようにスクリプト上で設計していた場合、返り値は何も帰ってこないため、処理の下流で不具合になる可能性が考えられる。
    • Maya画面へのドラッグ&ドロップ操作での挙動も変わる。同じように設定ウィンドウが開いて止まるように。

解決策

解決策としては、以下の3点が有効です。

  1. fileDialog設定にOSネイティブは使わない(←コラッ。
  2. fileコマンド経由ではなく、FBXImport/FBXExportコマンドを使うようにする
  3. 一時的にMaya既定に戻して実行し、あとで元に戻す

…なお、私の事例では、どうしてもfileコマンドを使いたかったため、3番目の解決策を採択しました。

fileDialogの設定をmaya.cmdsまたはmelから確認・変更する

  1. optionVarから「FileDialogStyle」の値を確認し、1だったらOSネイティブ。
  2. optionVarのFileDialogStyleを2に変更する。
  3. melコマンド「savePrefsChanges;」を実行して、上記の設定をMayaに保存します。

ただし、単にファイルダイアログの設定を変えるだけでは、fileコマンドのfileタイプを更新できていない点に注意が必要。
そのため、fbxプラグインのリロードをする必要があります。

以下、fileDialogの設定をMaya既定に戻し、FBXプラグインをリロードするmaya.cmdsのサンプルコードです。

from maya import cmds, mel

if mel.eval('optionVar -q "FileDialogStyle"') == 1:
        mel.eval('optionVar -iv FileDialogStyle 2 ;')
        mel.eval('savePrefsChanges;')
        cmds.unloadPlugin('fbxmaya')
        cmds.loadPlugin('fbxmaya', qt=True)

…この後で、Toolやスクリプトの提供先ユーザーのために、fileDialogの設定を戻しておいてあげるとよいと思います。

fileDialog設定をOSネイティブに変えた際のFBXプラグインの挙動

…では、現象を詳細に分析してみましょう。
まず、FBXのインポート・エクスポート周りの挙動確認のために、以下のようなスクリプトを用意しておきます。

from maya import cmds,mel
def setFBXOptions(*args,**kwargs):
    mel.eval('FBXResetExport;')
    # ・・・エキスポート設定をリセットします。
    mel.eval('FBXProperty "Export|IncludeGrp|Animation" -v 1')
    # ・・・fbxエキスポートオプションの、アニメーション設定の部分です。
    mel.eval('FBXExportBakeComplexAnimation -v 0;')
    # ・・・のコマンドは、アニメーションのベイク処理(Bake Animation)オプションに相当するスクリプトです。
    mel.eval('FBXExportShapes -v 1;')
    # ・・・シェイプを出力するかどうか
    mel.eval('FBXExportSkins -v 1;')
    # ・・・スキンをエキスポートするかどうか
    mel.eval('FBXExportSmoothMesh -v 1;')
    # ・・・スムースメッシュをエキスポートするかどうか
    mel.eval('FBXExportSmoothingGroups -v 1;')
    # ・・・スムージンググループをエキスポートするかどうか
    mel.eval('FBXExportUseSceneName -v 1;')
    # ・・・アニメーションクリップ名にシーン名を使うかどうか(使わないと、Take1に)
    mel.eval('FBXExportCameras -v 0;')
    # ・・・カメラをエキスポートするかどうか
    mel.eval('FBXExportLights -v 0;')
    # ・・・ライトをエキスポートするかどうか
    mel.eval('FBXExportInAscii -v 0;')
    # ・・・アスキー形式にするかどうか
    mel.eval('FBXExportInputConnections -v 1;')
    # ・・・コネクションを含めるかどうか
    mel.eval('FBXExportApplyConstantKeyReducer -v 1;')
    # ・・・アニメーションで単一のキーを削除するかどうか
    mel.eval('FBXExportEmbeddedTextures -v 0;')
    # ・・・テクスチャを内包するかどうか

def exportFBX(*args,**kwargs):
    if not cmds.ls(sl = True):
        return

    _filename = cmds.fileDialog2(fm = 0,ff = 'FBXfile(*.fbx);;')
    if not _filename:
        return
    setFBXOptions()
    cmds.file(_filename[0],es=True,type = 'FBX export')

def importFBX(*args,**kwargs):
    _filename = cmds.fileDialog2(fm = 1,ff = 'FBXfile(*.fbx);;')
    if not _filename:
        return
    mel.eval('FBXImportMode -v "add";')
    nodes = cmds.file(_filename[0],i=True,type = 'FBX',rnn = True)
    print(nodes)

fileDialogの設定は…

ご覧の通り、Maya既定のままです。


インポートもエクスポートも正しく実行できたのが確認できます。

ファイルタイプ FBX exportが認識されない

…それでは、試しにfileDialogの設定を「OSネイティブ」に変更して、Mayaを再起動し再実行してみます。

エラーを吐いて途中で止まる

すると、export処理を実行してみたところで、

…………!!!?

# Error: RuntimeError: file <maya console> line 44: Invalid file type specified: FBX export

このようなエラーになって途中で止まってしまいました。
FBXプラグインも別にunloadされたわけではありません。

いやいや…何言ってんの…。
ついさっきまでFBX exportで認識してたじゃないのアナタ。。
どうしたっていうの。

まるで別人よ?

ファイルタイプが「FBX」に変わっている。

そしたら、正しいファイルタイプは一体何なのか。
試しに手作業でFBX Exportしてみて、スクリプトログを見てみることにします。


いつものようにExport selectionからFBX出力すると、何やらFBXの設定ウインドウが出てきました。
…こんな設定ウインドウ、いつも出てたっけ…?

Exportボタンを押して、Export処理が実行されたのを確認し、ちゃんとExportできたことまで確認できました。

ちなみに、この時のスクリプトログを見てみると…。

file -force -options "" -typ "Fbx" -pr -es "D:/RD/adventCalendar/2023/FBXImportTest/FBX/testExp.fbx";
-typ "Fbx"

…何ィ…!?
…あぁそう。
どうやらOSネイティブにすると、ファイルタイプがFBX ExportであってもFBXになることがわかりました。

なるほどね。

Export実行直前にFBX出力処理だけが止まる

そしたら、ファイルタイプの指定を以下のように、FBXに変えて実行してみます。

def exportFBX(*args,**kwargs):
    if not cmds.ls(sl = True):
        return

    _filename = cmds.fileDialog2(fm = 0,ff = 'FBXfile(*.fbx);;')
    if not _filename:
        return
    setFBXOptions()
    # cmds.file(_filename[0],es=True,type = 'FBX export')
    cmds.file(_filename[0],es=True,type = 'FBX')

実行してみると…。
おっ。ちゃんと処理が走りました。
ファイルを指定して、保存してみます。

…ん?
なんかまたFBXの設定ウィンドウが出現してきました。

フォルダを見ると…。

出力できてないじゃん!
オイお前ー!(憤怒

…よもやfileコマンドで処理が止まっているのか?
確認のために、fileコマンド実行後にprint文を実行するようにしてみます。

def exportFBX(*args,**kwargs):
    if not cmds.ls(sl = True):
        return

    _filename = cmds.fileDialog2(fm = 0,ff = 'FBXfile(*.fbx);;')
    if not _filename:
        return
    setFBXOptions()
    # mel.eval('FBXExport -f "{}" -s;'.format(_filename[0]))
    #cmds.file(_filename[0],es=True,type = 'FBX export')
    cmds.file(_filename[0],es=True,type = 'FBX')
    print('unco') # ここですね。

こうです。

実行してみると…。。

…うむ。
よもやよもや だ。

どうやら処理自体は止まっていないものの、FBXのエクスポート部分だけが止まっているようです。
これは、Toolなどの挙動としては非常に憂慮すべき挙動と言えます。
設計次第では、エクスポートが完了した想定でその後の処理が進むわけですから、想定外の不具合につながりかねません。

こわやこわや。
季節外れの怪談かな。
ホラーは苦手だよあたしは。

設定をOSネイティブ⇒Maya既定…に変更してみる

どうやら、OSネイティブの設定はfileコマンドでのFBXファイルタイプを上書きしてしまうようです。
では、いったんfileDialogの設定をOSネイティブからMaya既定に戻して再実行してみます。

Maya既定に戻してSave。※Mayaの再起動はしません ←ここ重要

再度

cmds.file(_filename[0],es=True,type = 'FBX export')

での処理を実行してみます。
すると…。

# Error: RuntimeError: file <maya console> line 44: Invalid file type specified: FBX export

なにーっ!?

どうやらfileDialogの設定を単にMaya既定に戻すだけでは直接解決にならないようです。

この状態で手動でFBXExportをしても…。

file -force -options "" -typ "Fbx" -pr -es "D:/RD/adventCalendar/2023/FBXImportTest/FBX/testFBX.fbx";

スクリプトログは上記の通り。
FBXの設定UIは出てきませんでしたが、ファイルタイプはFbxのままです。

なお、Mayaを再起動するか、FBXプラグインをアンロード・ロードすると、ファイルタイプFBX Exportを再認識するようになりました。

FBXファイルのインポートの挙動はどうか?

以上はFBX Exportのお話でしたが、ではImportの方はどうでしょうか。
同様にOSネイティブの状態で実験してみたいと思います。

fileコマンドの返り値で「追加されたノード」をリストアップできない

前述のスクリプトで、importFBX()を実行してみます。
すると、FBXExportの時と似た形でFBXの設定ウインドウが出現するだけで、FBXのインポート処理は実行されていないような挙動を見せました。

こんなかんじ。

これも、きっとfileコマンドのところで処理が止まっているのでは?と疑いたくなります。

def importFBX(*args,**kwargs):
    _filename = cmds.fileDialog2(fm = 1,ff = 'FBXfile(*.fbx);;')
    if not _filename:
        return
    mel.eval('FBXImportMode -v "add";')
    nodes = cmds.file(_filename[0],i=True,type = 'FBX',rnn = True)
    print(nodes)

今回はfileコマンド直後にprint文があらかじめ仕込まれていますので、これを確認することにしましょう。
rnnフラグを立てたfileコマンドの帰り値をプリントするようになっているので、新規に読み込まれたノード群がきっとプリントされるはずです。

結果を見てみると…

プリント文の実行までは確認できますが、空のリストしかプリントされません。

ちなみに、Maya既定のfileDialog設定の状態で同様のスクリプトを実行すると…

このように、新規に追加されたノード群がリストでプリントされます。

どうやら、FBXImport挙動についてもExport挙動と同様に、fileコマンド以降もスクリプト処理自体は実行されるものの、FBXのImport挙動自体は止まっているようでした。

これは恐ろしい。。

ドラッグ&ドロップでの挙動は?

では、ドラッグ&ドロップでのインポート挙動は何か違いがあるのでしょうか。


試しにfileDialog設定をOSネイティブ状態にして実行してみると…。


デターーッ
FBXの設定ウインドウがぁ。

もちろんインポートは即時には実行されず、ここでImportボタンを押してあげないとデータはインポートされません。

普段fileDialogをOSネイティブ設定にしてMayaを運用しないので、ここまで挙動が違うものかと、正直なんだかおもしろくなってきました(笑)

逆にウケルゥ~☆キャハッ

ちなみに、FBXExportコマンドは問題ない。

試しにfileコマンド経由ではなく、ファイルタイプを指定しなくて済むようにFBXコマンドでExportを実行してみます。

def exportFBX(*args,**kwargs):
    if not cmds.ls(sl = True):
        return

    _filename = cmds.fileDialog2(fm = 0,ff = 'FBXfile(*.fbx);;')
    if not _filename:
        return
    setFBXOptions()

    # cmds.file(_filename[0],es=True,type = 'FBX export')
    # cmds.file(_filename[0],es=True,type = 'FBX')
    mel.eval('FBXExport -f "{}" -s;'.format(_filename[0]))
    print('unco')

すると、FBXの設定UIも開くことなく、通常通りExport処理が完了しました。

なるほど。

そもそもfileコマンド経由ではなく、FBXコマンド経由からFBXExportを実行するように設計しておけば、このような事故は回避できそうです。

解決策?

ここまで検証した結果、解決策としては…

  • もうOSネイティブは使わない!(コラーッ
  • fileコマンドを経由せず、FBXコマンドで運用する

…というのが、正直コスト安な対策だと思います。

しかし、OSネイティブは禁止ー!…というのはいささからんぼうな気がしますね(汗)
第一、Maya既定のfileDialogはたまに極端に重たくなるケースがあります。
それでいうと、OSネイティブは早いし。
実際私の周囲にもOSネイティブを好んで使う方もいますし。

共存前提でスクリプトやToolは設計したほうがみんなのためになりそうです。

一方、fileコマンド経由ではなくFBXコマンドでExport/Importを行うという施策については、そこそこ現実的かなと思います。
可能であればfileコマンドを経由せずに運用したほうが、このケースでは安全なスクリプトやToolが作成できそうです。

どうしてもfileコマンド経由でFBX出力したいケースについて

しかし、Toolの設計上、どうしてもfileコマンド経由でFBXをExport/Importしたいケースもあると思います。
例えば、fileコマンドには以下のような便利なフラフがあります。

  • relativeNamespace(rns)
    …ネームスペースを変更して出力できる。
    カットシーンのアニメーションなど、ノードにネームスペースがついていても、特殊な処理を挟まずしてネームスペースを取り除くことができて重宝。
  • returnNewNodes(rnn)
    …シーンに追加されたノードをリストアップできる。
    インポートで追加されたノード群に対して後処理をかけたい場合等に重宝。
    …などなど、fileコマンドは便利なフラグがそろっているので、捨て置くのはちょっともったいない。

以下fileコマンドの公式ドキュメントページです。
https://help.autodesk.com/cloudhelp/2024/JPN/Maya-Tech-Docs/CommandsPython/file.html

では、fileコマンドを使いたいケースでは、一体どうしたら解決できるのでしょうか。

fileDialogの設定をスクリプトで書き換える

今までの検証を振り返ってみると、要はfileDialogの設定を裏でMaya準拠に戻し、FBXプラグインをリロードすれば、元々想定していたfileコマンド経由でのFBXExport/Importの挙動になる訳です。
つまり、この操作をスクリプト化してしまえば、問題解決するのではないでしょうか。

では、そのスクリプトを調査してみましょう。
fileDialogの設定をMaya既定に変更した際のスクリプトのエコーを見てみます。

optionVar -iv FileDialogStyle 2;

なるほど。
「FileDialogStyle」というOptionVarの値を2にすれば、fileDialogの設定をMaya既定にすることができるようです。

続いて、変更後にプリファレンスを保存する操作をして、Mayaのプリファレンスウィンドウを閉じた際のエコーコマンドを調べてみると…

prefsSaveBtnにsetFocusした後で実行されているmel、

savePrefsChanges;

これで設定が保存されているようです。
念のためWhatIsコマンドから「savePrefsChanges」を調べてみると、
C:/Program Files/Autodesk/Maya2024/scripts/startup/preferencesWnd.mel
…というmelファイルにたどり着きます。

中身を調べて、savePrefsChangesプロシージャーを探してみると…

global proc savePrefsChanges ()
{
    if (canSavePrefsChanges()) {
        prefsHoldCurrentState("remove");
        savePrefs();
        closePrefsWindow();
    }
}

このようになっていました。
内部でsavePrefs()を実行し、プリファレンスウインドウを閉じる、というプロシージャーのようです。
なお、savePrefs()をドキュメントで調べてみると…

このコマンドは、プリファレンスをディスクに保存します。フラグを指定していない場合は、すべてのプリファレンス タイプを保存します。

https://help.autodesk.com/cloudhelp/2018/JPN/Maya-Tech-Docs/Commands/savePrefs.html
…といった内容になっているので、間違いなさそうです。

では、ここまでの検証と調査結果をもとに解決策をまとめてみます。
fileDialog設定がOSネイティブであっても、fileコマンドを経由して想定通りFBXImport/Exportが実行できるようにするスクリプトを考えてみたいと思います。

from maya import cmds, mel

# まず、fileDialog設定がOSネイティブなのかどうかをチェック
if mel.eval('optionVar -q "FileDialogStyle"') == 1:
        # fileDialogスタイルをMaya準拠に戻す
        mel.eval('optionVar -iv FileDialogStyle 2 ;')
        # プリファレンスを保存する
        mel.eval('savePrefsChanges;')
        # FBXプラグインをリロードする
        cmds.unloadPlugin('fbxmaya')
        cmds.loadPlugin('fbxmaya', qt=True)

このスクリプトをFBXのExport/Importの前に実行するように組んであげれば、fileDialogの設定にかかわらず安定した動作を実現することができそうです。

…以上をまとめて、解決策となるスクリプトを設計してみる

では、このスクリプトを最初のFBX Export/Import 検証用のスクリプトに組み込んで、再度検証してみます。

最終的なソースコードはこんなかんじ。

from maya import cmds,mel

def checkDialogStile(func,*args,**kwargs):
    def wrapper(*args,**kwargs):
        changeDialogType = False
        if mel.eval('optionVar -q "FileDialogStyle"') == 1:
            mel.eval('optionVar -iv FileDialogStyle 2 ;')
            mel.eval('savePrefsChanges;')
            cmds.unloadPlugin('fbxmaya')
            cmds.loadPlugin('fbxmaya', qt=True)
            changeDialogType = True
        try:
            results = func(*args,**kwargs)
        except:
            results = None
            pass
        if changeDialogType:
            mel.eval('optionVar -iv FileDialogStyle 1 ;')
            mel.eval('savePrefsChanges;')
        return results

    return wrapper

def setFBXOptions(*args,**kwargs):
    mel.eval('FBXResetExport;')
    # ・・・エキスポート設定をリセットします。 再設定する際、漏れがなく、バグ予防になります。
    mel.eval('FBXProperty "Export|IncludeGrp|Animation" -v 1')
    # ・・・fbxエキスポートオプションの、アニメーション設定の部分です。
    mel.eval('FBXExportBakeComplexAnimation -v 0;')
    # ・・・のコマンドは、アニメーションのベイク処理(Bake Animation)オプションに相当するスクリプトです。

    mel.eval('FBXExportShapes -v 1;')
    # ・・・シェイプを出力するかどうか
    mel.eval('FBXExportSkins -v 1;')
    # ・・・スキンをエキスポートするかどうか
    mel.eval('FBXExportSmoothMesh -v 1;')
    # ・・・スムースメッシュをエキスポートするかどうか
    mel.eval('FBXExportSmoothingGroups -v 1;')
    # ・・・スムージンググループをエキスポートするかどうか
    mel.eval('FBXExportUseSceneName -v 1;')
    # ・・・アニメーションクリップ名にシーン名を使うかどうか(使わないと、Take1に)
    mel.eval('FBXExportCameras -v 0;')
    # ・・・カメラをエキスポートするかどうか
    mel.eval('FBXExportLights -v 0;')
    # ・・・ライトをエキスポートするかどうか
    mel.eval('FBXExportInAscii -v 0;')
    # ・・・アスキー形式にするかどうか
    mel.eval('FBXExportInputConnections -v 1;')
    # ・・・コネクションを含めるかどうか
    mel.eval('FBXExportFileVersion FBX201400;')
    # ・・・FBXの形式
    mel.eval('FBXExportApplyConstantKeyReducer -v 1;')
    # ・・・アニメーションで単一のキーを削除するかどうか
    mel.eval('FBXExportEmbeddedTextures -v 0;')
    # ・・・テクスチャを内包するかどうか

@checkDialogStile
def exportFBX(*args,**kwargs):
    if not cmds.ls(sl = True):
        return

    _filename = cmds.fileDialog2(fm = 0,ff = 'FBXfile(*.fbx);;')
    if not _filename:
        return
    setFBXOptions()

    cmds.file(_filename[0],es=True,type = 'FBX export')

@checkDialogStile
def importFBX(*args,**kwargs):
    _filename = cmds.fileDialog2(fm = 1,ff = 'FBXfile(*.fbx);;')
    if not _filename:
        return
    mel.eval('FBXImportMode -v "add";')
    nodes = cmds.file(_filename[0],i=True,type = 'FBX',rnn = True)
    print(nodes)

この例では、fileDialogの設定の確認と一時的にMaya既定にする処理はデコレーターにしてあります。

上記を実行してみると、exportFBX()/importFBX()いずれの実行も、fileDialogの設定にかかわらず、想定通り同様の挙動を実現できることが確認できました!

ハラショー!

まとめ

今回の件で得られた知見は、個人的には…
「意外とfileDialogの設定をOSネイティブにして使っている人がいるんだな」
…ということにつきると思っています。
加えて、日々サポートすることが多いFBXExport/ImportをつかさどるパイプラインToolですが、fileDialogの設定次第では、大きく挙動が異なり、Toolの設計に大きな影響を与える…ということも大きな学びとなりました。
今まではfileDialogスタイルをOSネイティブにして使うユーザーがほぼいない環境でのお仕事が多かったためか、幸運にも今回のような現象に遭遇することはありませんでした。

しかし、今後より多くの方々の制作サポートをしていく中で、ぜひ生かしていきたい知見です。

みなさまも、お手元にFBX出力・インポートToolなどあれば、fileDialog設定をOSネイティブなどにして挙動をチェックしてみてください。

では、今回はこの辺で。

サヨナラ!
サヨナラ!
サヨナラ!!


COYOTE 3DCG STUDIO

公式HP:https://3d.crdg.jp/

COYOTE 3DCG STUDIOはクリーク・アンド・リバー社が運営するゲーム専門3DCG制作集団です。
キャラモデル、背景モデル、3Dアニメーション、テクニカルアーティストによるツール開発などを得意としています。
新規立ち上げにおけるコンサルティングから量産制作まで幅広く対応可能な体制を保有しており、出向にも柔軟に対応しております。


yamamoto tomohito

COYOTE 3DCG STUDIO モーション・リグ・パイプライン系TA。 他にやる人がいないのでチームリーダーです。 三時のヒロイン…じゃない、三児の父。 パパじゃないぞ。 おとうさんだ!

投稿者記事

  1. スクリプトでAnimCurveをフィルタリング!

    2023-12-14

  2. スクリプト練習に最適!
    カスタムファイルブラウザで仕事効率化!

    2021-12-24

  3. 1ボーンIKのフロアコンダクトリグを作る!

    2020-12-22

  4. melでのvector活用術 ~その3~ RotatePlaneIKを作ってみる③

    2020-07-30

関連記事

  1. 【PySide】QtDesignerでDrag and DropできるWidget設定

    2023-06-28

  2. melでのvector活用術 ~その2~ RotatePlane IKを作ってみる②

    2020-06-16

  3. マトリクスを使ったポーズ・モーションの左右反転

    2019-10-29

  4. melでのvector活用術 ~その3~ RotatePlaneIKを作ってみる③

    2020-07-30

スキルレーダーチャート

テクニカルアーティスト専用
スキルレーダーチャート
どなたでも無料でご利用いただけます。

ABOUT

TECH COYOTE​

テクニカルアーティストの為のまとめサイトです。​
本サイトでは、ツール開発、業務効率化等について情報発信をしていきます。

COYOTE 3DCG STUDIO

C&R Creative Studios

難易度別

RECENT TWEET

ページ上部へ戻る