modoのサブディビジョンを、PSub(Catmull-Clark)に変換するスクリプトをAIに書いてもらった。

modoで作ったモデルを他の3DソフトにFBX出力する場合に、サブディビジョンのクリースを出力したい場合はCatmull-Clarkを使用する必要があります。
「情報と統計」ウィンドウから手動で「サブディビジョン」を選択して「Catmull-Clark」に変換することもできるのですが、アイテムを選択するのが面倒だったのでシーン内の全てのメッシュのサブディビジョンをCatmull-Clarkに一括変換するスクリプトをAIに書いてもらいました。
@_kai_ConvertPSub.py
#!/usr/bin/env python
import modo
import lx
import time
import traceback
def log_msg(*args):
"""lx.out に出力。失敗時はダイアログで通知(フォールバック)。"""
try:
lx.out(" ".join([str(a) for a in args]))
except Exception:
# フォールバック: ダイアログで通知
try:
msg = " ".join([str(a) for a in args])
lx.eval('dialog.setup info {%s}' % msg.replace('}', '\\}'))
lx.eval('dialog.open')
except Exception:
# 最終フォールバックは print
print("LOG:", args)
def safe_lx_eval(cmd):
"""lx.eval を安全に呼ぶ(例外をキャッチしてログ出力)"""
try:
return lx.eval(cmd)
except Exception as e:
log_msg("[WARN] コマンド実行失敗:", cmd, "->", e)
return None
def main():
start_time = time.time()
# コンソールログを有効化
lx.eval('log.toConsole true')
lx.eval('log.toConsoleRolling true')
log_msg('------------------------------------------------------------')
log_msg('[実行開始] Subdiv -> PSubD 変換スクリプト')
processed_items = 0
try:
# シーン内のアイテム数を取得
try:
n_items = int(lx.eval('query sceneservice item.N ?'))
except Exception:
try:
n_items = int(lx.eval('query sceneservice item.n ?'))
except Exception:
log_msg("[ERROR] シーン内のアイテム数を取得できません。中止します。")
return
log_msg("[ステップ1] シーン内アイテム数:", n_items)
# 全アイテムをループ処理
for idx in range(n_items):
try:
# アイテムタイプを取得
item_type = lx.eval('query sceneservice item.type ? %d' % idx)
except Exception:
log_msg("[WARN] インデックス", idx, "のアイテムタイプ取得失敗 — スキップ")
continue
# Mesh以外はスキップ
if item_type.lower() != 'mesh':
continue
# アイテムIDを取得
try:
item_id = lx.eval('query sceneservice item.id ? %d' % idx)
except Exception:
log_msg("[WARN] インデックス", idx, "のアイテムID取得失敗 — スキップ")
continue
log_msg("[ステップ2] 処理中: インデックス=%d ID=%s" % (idx, item_id))
# アイテムを選択
sel_cmd = 'select.item {%s} set' % item_id
safe_lx_eval(sel_cmd)
# サブディビジョン型ポリゴンを選択(!でダイアログ抑制)
safe_lx_eval('!select.polygon add type subdiv')
# サブディビジョンを解除
safe_lx_eval('poly.convert face subpatch true')
# PSubD に変換
safe_lx_eval('poly.convert face psubdiv true')
# ポリゴン選択を解除
safe_lx_eval('select.drop polygon')
processed_items += 1
except Exception as e_main:
log_msg("[FATAL] メインループでエラー発生:", e_main)
log_msg(traceback.format_exc())
elapsed = time.time() - start_time
log_msg("[完了サマリー] 処理アイテム数=%d" % processed_items)
log_msg("[スクリプト終了] 実行時間=%.3f秒" % elapsed)
log_msg('------------------------------------------------------------')
# スクリプト実行
try:
main()
except Exception as e:
log_msg("[FATAL] main() でエラー発生:", e)
log_msg(traceback.format_exc())
modoのサブディビジョンに統一したい場合もあるかも知れないので、modoのサブディビジョンに統一するスクリプトも置いておきます。選択部分のコマンドを入れ替えただけです。
@_kai_ConvertModoSub.py
#!/usr/bin/env python
import modo
import lx
import time
import traceback
def log_msg(*args):
"""lx.out に出力。失敗時はダイアログで通知(フォールバック)。"""
try:
lx.out(" ".join([str(a) for a in args]))
except Exception:
# フォールバック: ダイアログで通知
try:
msg = " ".join([str(a) for a in args])
lx.eval('dialog.setup info {%s}' % msg.replace('}', '\\}'))
lx.eval('dialog.open')
except Exception:
# 最終フォールバックは print
print("LOG:", args)
def safe_lx_eval(cmd):
"""lx.eval を安全に呼ぶ(例外をキャッチしてログ出力)"""
try:
return lx.eval(cmd)
except Exception as e:
log_msg("[WARN] コマンド実行失敗:", cmd, "->", e)
return None
def main():
start_time = time.time()
# コンソールログを有効化
lx.eval('log.toConsole true')
lx.eval('log.toConsoleRolling true')
log_msg('------------------------------------------------------------')
log_msg('[実行開始] PSubD -> Subdiv 変換スクリプト')
processed_items = 0
try:
# シーン内のアイテム数を取得
try:
n_items = int(lx.eval('query sceneservice item.N ?'))
except Exception:
try:
n_items = int(lx.eval('query sceneservice item.n ?'))
except Exception:
log_msg("[ERROR] シーン内のアイテム数を取得できません。中止します。")
return
log_msg("[ステップ1] シーン内アイテム数:", n_items)
# 全アイテムをループ処理
for idx in range(n_items):
try:
# アイテムタイプを取得
item_type = lx.eval('query sceneservice item.type ? %d' % idx)
except Exception:
log_msg("[WARN] インデックス", idx, "のアイテムタイプ取得失敗 — スキップ")
continue
# Mesh以外はスキップ
if item_type.lower() != 'mesh':
continue
# アイテムIDを取得
try:
item_id = lx.eval('query sceneservice item.id ? %d' % idx)
except Exception:
log_msg("[WARN] インデックス", idx, "のアイテムID取得失敗 — スキップ")
continue
log_msg("[ステップ2] 処理中: インデックス=%d ID=%s" % (idx, item_id))
# アイテムを選択
sel_cmd = 'select.item {%s} set' % item_id
safe_lx_eval(sel_cmd)
# サブディビジョン型ポリゴンを選択(!でダイアログ抑制)
safe_lx_eval('!select.polygon add type psubdiv')
# サブディビジョンを解除
safe_lx_eval('poly.convert face psubdiv true')
# PSubD に変換
safe_lx_eval('poly.convert face subpatch true')
# ポリゴン選択を解除
safe_lx_eval('select.drop polygon')
processed_items += 1
except Exception as e_main:
log_msg("[FATAL] メインループでエラー発生:", e_main)
log_msg(traceback.format_exc())
elapsed = time.time() - start_time
log_msg("[完了サマリー] 処理アイテム数=%d" % processed_items)
log_msg("[スクリプト終了] 実行時間=%.3f秒" % elapsed)
log_msg('------------------------------------------------------------')
# スクリプト実行
try:
main()
except Exception as e:
log_msg("[FATAL] main() でエラー発生:", e)
log_msg(traceback.format_exc())
普段はサブディビジョンのクリースは使わないのですが、ちょっと楽するために使いたい場合があったのでスクリプトにしてみました。
後で気づきましたが、特定のアイテムタイプだけ選択したい場合は select.itemType コマンドを使用すると便利です。ライトだけ別コマンド必要っぽい?
- select.itemType mesh
- select.itemType camera
- select.itemPattern *Light*
- select.itemType replicator
- select.itemType meshInst
- select.itemType advancedMaterial
このコマンドがあればスクリプトじゃなくてマクロの方がシンプルでよかったですね。modoのコマンド強力すぎてスクリプト無くてもだいたいどうにでもなっちゃうの凄い。
#LXMacro#
select.itemType mesh
select.polygon add type subdiv
poly.convert face psubdiv true
poly.convert face psubdiv true
select.drop polygon

