ファイル・データ
3ds Max Python チートシート の章一覧
ファイルまわりは Python の標準ライブラリが一番強い ところです。
パス操作は os.path / pathlib、データの読み書きは json / csv を使い、3ds Max 固有のこと(シーンの保存・読み込み・書き出し)だけを rt で行います。
よく使うパスを取る
import os
scene_dir = rt.maxFilePath # 開いているシーンのフォルダ(未保存なら "")
scene_name = rt.maxFileName # ファイル名(未保存なら "")
full = os.path.join(scene_dir, scene_name) # フルパス
temp_dir = rt.getDir(rt.Name("temp")) # MXS: getDir #temp
export_dir = rt.getDir(rt.Name("export")) # 書き出しの既定フォルダ
render_dir = rt.getDir(rt.Name("renderoutput"))
project = rt.pathConfig.getCurrentProjectFolder() # プロジェクトフォルダ
getDirには#scene/#export/#import/#renderoutput/#temp/#maxroot/#userScriptsなどの Name を渡します。- 未保存のシーンでは
maxFilePathもmaxFileNameも空文字です。保存先を決める処理では、まずこれを確かめます。
pathlib でパスを組み立てる
from pathlib import Path
out_dir = Path(rt.getDir(rt.Name("temp"))) / "my_tool" / "exports"
out_dir.mkdir(parents=True, exist_ok=True) # フォルダをまとめて作る
base = Path(rt.maxFileName).stem or "untitled" # 拡張子を除いた名前
fbx_path = out_dir / (base + ".fbx")
fbx_str = str(fbx_path) # rt の関数には str にして渡す
- ⚠️
rtの関数にPathオブジェクトをそのまま渡すと受け付けないことがあります。str()にしてから渡す のが安全です。 - Python 3 の文字列は Unicode なので、日本語を含むパスもそのまま扱えます。
シーンを保存する / 開く / リセットする
import os
path = os.path.join(rt.getDir(rt.Name("temp")), "my_tool_scene.max")
rt.saveMaxFile(path, quiet=True) # MXS: saveMaxFile path quiet:true
rt.resetMaxFile(rt.Name("noPrompt")) # 新規シーン(確認なし)
rt.loadMaxFile(path, quiet=True) # MXS: loadMaxFile path quiet:true
- ⚠️
saveMaxFile/loadMaxFile/resetMaxFile(rt.Name("noPrompt"))は 確認なしで上書き・破棄 します。ユーザの作業中のシーンに対して使う前に、rt.checkForSave()(変更があれば保存を尋ねる)を呼ぶと親切です。キャンセルされるとFalseを返すので、if rt.checkForSave(): ...のように使います。 quiet=Trueを付けると、足りないプラグインや外部ファイルの警告ダイアログを出しません(バッチ処理向け)。
選んだノードだけを別ファイルに保存する
import os
path = os.path.join(rt.getDir(rt.Name("temp")), "my_tool_parts.max")
rt.saveNodes([rt.getNodeByName("Box001")], path, quiet=True) # MXS: saveNodes #($Box001) path
names = list(rt.getMAXFileObjectNames(path, quiet=True)) # シーンを開かずに、ファイル内のオブジェクト名を読む(名前順の文字列の配列)
別のシーンから取り込む(マージ)
import os
path = os.path.join(rt.getDir(rt.Name("temp")), "my_tool_parts.max")
rt.saveNodes([rt.getNodeByName("Box001")], path, quiet=True) # 準備: 取り込み元を作る
rt.mergeMAXFile(path, ["Box001"], rt.Name("autoRenameDups"), quiet=True) # 同名は自動でリネーム
# MXS: mergeMAXFile path #("Box001") #autoRenameDups quiet:true
merged = list(rt.getLastMergedNodes()) # 取り込んだノード
# MXS: getLastMergedNodes()
- 2 つ目の引数に名前のリストを渡すと、そのオブジェクトだけを取り込みます(省くと全部)。
- 重複の扱いは
#autoRenameDups(リネーム)/#skipDups(取り込まない)/#deleteOldDups(既存を消す)などの Name を位置引数で渡します。quiet=Trueのようなキーワード引数ではなく、rt.Name("...")をそのまま位置引数で並べる点に注意します。
FBX / OBJ に書き出す・読み込む
import os
rt.pluginManager.loadClass(rt.FBXEXP) # MXS: pluginManager.loadClass FBXEXP
rt.FBXExporterSetParam("Animation", False) # FBX の書き出し設定(MXS: FBXExporterSetParam "Animation" false)
rt.FBXExporterSetParam("UpAxis", "Y")
path = os.path.join(rt.getDir(rt.Name("temp")), "my_tool_export.fbx")
rt.select(rt.getNodeByName("Box001"))
ok = rt.exportFile(path, rt.Name("noPrompt"), selectedOnly=True, using=rt.FBXEXP)
# MXS: exportFile path #noPrompt selectedOnly:true using:FBXEXP
rt.resetMaxFile(rt.Name("noPrompt"))
rt.pluginManager.loadClass(rt.FBXIMP)
rt.importFile(path, rt.Name("noPrompt"), using=rt.FBXIMP) # 読み込み
FBXExporterSetParamを使うには、FBX 書き出しプラグインがメモリにロードされている必要があります。ロードされていない場合はrt.pluginManager.loadClass(rt.FBXEXP)でロードします。#noPromptを付けないと、書き出し設定のダイアログが開きます。設定は事前にFBXExporterSetParamで済ませておきます。FBXExporterSetParamで変えた設定は、UI から書き出すときにも残ることがあります。ユーザの設定を変えたくないときは、rt.FBXExporterSetParam("PushSettings")で退避してから設定し、終わったらrt.FBXExporterSetParam("PopSettings")で戻します。- 書き出し形式は拡張子で決まりますが、
using=でプラグインのクラスを明示すると確実です(OBJ ならrt.ObjExp)。
シーンの情報を JSON に書き出す / 読み戻す
import json
import os
data = []
for o in rt.objects:
p = o.pos
data.append({
"name": o.name,
"class": str(rt.classOf(o)),
"pos": [p.x, p.y, p.z],
"material": o.material.name if o.material else None,
})
path = os.path.join(rt.getDir(rt.Name("temp")), "my_tool_scene.json")
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
# 読み戻して位置を復元する
with open(path, encoding="utf-8") as f:
for item in json.load(f):
node = rt.getNodeByName(item["name"])
if node is not None:
node.pos = rt.Point3(*item["pos"])
- ⚠️
jsonがそのまま書けるのは Python の型だけです。Point3やColorは 成分を取り出して list や数値にする、クラスや Name はstr()にする、という変換が必要です。 ensure_ascii=Falseにすると日本語の名前がそのまま読める形で保存されます。
CSV に書き出す(Excel で開く)
import csv
import os
path = os.path.join(rt.getDir(rt.Name("temp")), "my_tool_report.csv")
with open(path, "w", encoding="utf-8-sig", newline="") as f: # utf-8-sig = BOM 付き
w = csv.writer(f)
w.writerow(["名前", "クラス", "三角形面数"])
for o in rt.geometry:
w.writerow([o.name, str(rt.classOf(o)), rt.getPolygonCount(o)[0]])
getPolygonCount()はノードをメッシュオブジェクトに変換した場合の面の数を返します。メッシュは三角形の面だけで構成されるので、実質的に三角形の数になります。- Excel で日本語を文字化けさせずに開くには BOM 付き UTF-8(
utf-8-sig) で書きます。 newline=""を付けないと、Windows では行の間に空行が入ります。
ファイルを選ぶダイアログ
path = rt.getSaveFileName(caption="書き出し先", types="JSON (*.json)|*.json|All (*.*)|*.*|")
# MXS: getSaveFileName caption:"書き出し先" types:"JSON (*.json)|*.json|All (*.*)|*.*|"
if path: # キャンセルなら None
print(path)
src = rt.getOpenFileName(caption="読み込むファイル", types="FBX (*.fbx)|*.fbx|")
folder = rt.getSavePath(caption="フォルダを選ぶ") # フォルダ選択
types=は"説明1|パターン1|説明2|パターン2|...|"の形式で、先頭と末尾を含めて各要素を|で区切ります。末尾の|を忘れないようにします。- キャンセルされると
Noneが返ります(MaxScript のundefined)。 - Qt の UI を作っているなら
QtWidgets.QFileDialogを使っても構いません(→「Qt UI」章)。
シーンの外部ファイルを一覧する
files = []
for i in range(1, rt.AssetManager.getNumAssets() + 1): # 番号は 1 始まり
asset = rt.AssetManager.getAssetByIndex(i)
files.append(asset.getFullFilePath())
# MXS: for i = 1 to AssetManager.GetNumAssets() do (local a = AssetManager.GetAssetByIndex i; append files a.GetFullFilePath())
- AssetManager が管理する全アセットが対象です。テクスチャ・IES・XRef だけでなく、シーン自身やレンダー出力先なども含まれます。種類を絞りたいときは
asset.getType()(#Bitmap/#XRef/#RenderOutputなど)で判定します。 - ファイル名だけでなくフルパスが欲しい場合は
asset.getFileName()ではなくasset.getFullFilePath()を使います(getFileName()はフルパスとは限りません)。 - テクスチャのファイル名だけを集めたいなら、
rt.usedMaps()の方が短く書けます。
落とし穴まとめ
- 未保存のシーンは
maxFilePath/maxFileNameが空文字。 saveMaxFile/loadMaxFile/resetMaxFile(rt.Name("noPrompt"))は確認なしで上書き・破棄する。rtの関数にはPathではなくstrを渡す。Point3などは JSON にそのまま書けない。成分に分けてから書く。- Excel 向けの CSV は
utf-8-sig、newline=""。
関連
- テクスチャのパスを付け替える: 「マテリアル・マップ」章
- ヘッドレスでまとめて書き出す: 「レンダリング・ビューポート・バッチ」章
コメント