🙄
Substance 3D Painter の Python API に少し手を入れる
Python API の所感
Substance 3D Painter は 7.4.0 (Released November 24, 2021) から、QML+Javascript で開発していたスクリプトをPythonから呼び出すことが出来るようになりました。
最近のリリースにも定期的な更新が入っているものの、ベイクやシェーダなどのモジュールがバックポートされず、使うには難しい印象でした。
js モジュールをラップ
そこでpythonicに呼び出せるようにラップしてみました。こういう用途で使う
メタプログラミングで引数をうまく動的にbindできなかったので、結果的にコードが長いです。
環境
- Substance 3D Painter、バージョン 8.2.0 ビルド 1987 - 772e8c568b0b05753504c17acf6c5f950b1c5093
- Windows10 Pro
下のスクリプトを
%PROGRAMFILES%\Adobe\Adobe Substance 3D Painter\resources\python\startup
に置きます。
patch.py
import substance_painter
import functools
import inspect
import types
class Baking:
@staticmethod
def bake(textureSetName : str):
return substance_painter.js.evaluate(f"alg.baking.{inspect.currentframe().f_code.co_name}('{textureSetName}')")
@staticmethod
def setCommonBakingParameters(parameters : object):
return substance_painter.js.evaluate(f"alg.baking.{inspect.currentframe().f_code.co_name}({parameters})")
@staticmethod
def setCurvatureMethod(method : str = "FromMesh"):
return substance_painter.js.evaluate(f"alg.baking.{inspect.currentframe().f_code.co_name}('{method}')")
@staticmethod
def setTextureSetBakingParameters(textureSetName : str, parameters : object):
return substance_painter.js.evaluate(f"alg.baking.{inspect.currentframe().f_code.co_name}('{textureSetName}',{parameters})")
@staticmethod
def textureSetBakingParameters(textureSetName : str):
return substance_painter.js.evaluate(f"alg.baking.{inspect.currentframe().f_code.co_name}('{textureSetName}')")
class Shaders:
@staticmethod
def groups(shaderId : int = 0):
return substance_painter.js.evaluate(f"alg.shaders.{inspect.currentframe().f_code.co_name}({shaderId})")
@staticmethod
def materials(shaderId : int = 0):
return substance_painter.js.evaluate(f"alg.shaders.{inspect.currentframe().f_code.co_name}({shaderId})")
@staticmethod
def parameter(shaderId : int, identifier : str):
return substance_painter.js.evaluate(f"alg.shaders.{inspect.currentframe().f_code.co_name}({shaderId},'{identifier}')")
@staticmethod
def parameters(shaderId : int = 0, group : str = ""):
return substance_painter.js.evaluate(f"alg.shaders.{inspect.currentframe().f_code.co_name}({shaderId},'{group}')")
@staticmethod
def setParameters(shaderId : int, parameters : object):
return substance_painter.js.evaluate(f"alg.shaders.{inspect.currentframe().f_code.co_name}({shaderId, parameters})")
@staticmethod
def shaderInstancesFromObject(jsObject : object):
return substance_painter.js.evaluate(f"alg.shaders.{inspect.currentframe().f_code.co_name}({jsObject})")
@staticmethod
def updateShaderInstance(shaderId : int = 0, shaderUrl : str = ""):
return substance_painter.js.evaluate(f"alg.shaders.{inspect.currentframe().f_code.co_name}({shaderId},'{shaderUrl}')")
def start_plugin():
"""Plugin interface: called to start the plugin."""
substance_painter.baking = types.ModuleType("baking", "alg.baking\nManage baking of the opened project")
for obj in "commonBakingParameters", "curvatureMethod", "selectCageMesh", "selectHighDefinitionMeshes":
setattr(substance_painter.baking, obj, functools.partial(substance_painter.js.evaluate, f"alg.baking.{obj}()"))
for obj in inspect.getmembers(Baking, inspect.isfunction):
setattr(substance_painter.baking, obj[0], getattr(Baking, obj[0]))
substance_painter.shaders = types.ModuleType("shaders", "alg.shaders\nControl shader instances of the currently opened project")
for obj in "instances", "shaderInstancesToObject":
setattr(substance_painter.shaders, obj, functools.partial(substance_painter.js.evaluate, f"alg.shaders.{obj}()"))
for obj in inspect.getmembers(Shaders, inspect.isfunction):
setattr(substance_painter.shaders, obj[0], getattr(Shaders, obj[0]))
if __name__ == "__main__":
start_plugin()
module をインポートした段階で baking モジュールと shaders モジュールが、自動的にパッケージに追加されます。
まとめ
数年前の「このAPIでどうやって開発するの...?」状態からメッシュのリロードができるようになったり、リモートデバッグができるようになったりで、ゆっくり着実に進歩しています。
レイヤー周りにapiの変更が加われば使いやすくなるので、その日を気長に待ちたいと思います。
Discussion