Design Automation API for 3ds Max:MAXScript レンダリング フロー

Design Automation API for 3ds Max:MAXScript と 3ds Max BatchDesign Automation API for 3ds Max:MAXScript での JSON 読込み でご案内してきた MAXScript を、Design Automation API for 3ds Max で実行出来るようにしていきます。

最初に、Design Automation API の利用手順を再確認しておきます。短く表現するなら、Design Automation API  は、クラウドで動作するカスタム処理を、クライアント(主に Web ブラウザ)からリモート操作する環境を提供します。

この時、クライアントからリクエストするカスタム処理の実行単位が WorkItem(ワークアイテム)です。WorkItem の実行前には、WorkItem が使用するファイルやカスタム処理のコマンド/スクリプトを明記/定義する Activity(アクティビティ)と、カスタム処理を実装するコンポーネントである AppBundle(アップバンドル)を事前に登録ことになります。

  • Appbundle は、アドイン/プラグインを含むパッケージバンドルを圧縮した .zip ファイルを指します。
  • WorkItem は、Activity で定義された内容(どの入力ファイル(素材)を使って、どの AppBundleで実装されたカスタム処理を実行し公開、出力ファイル(成果物)を保存するか)に沿って実行されます。
A flowchart outlining the steps for using the Design Automation API for 3ds Max, including registration of Nickname, AppPackage, Activity, and execution of WorkItem.

ここで WorkItem が実行するのは、ここまで考察してきた MAXScript ということになります。MAXScript 自体は、Step by step tutorial で説明された方法と同じく、Signed URL(署名付き URL)を使って任意に用意したクラウド ストレージから直接ダウンロード、実行します。

今回は MAXScript からの JSON 読み込み用に Newtonsoft.Json コンポーネントを使用するため、Newtonsoft.Json コンポーネントをコアエンジンに認識させる必要があります。

これを解決する方法が、AppBundle です。AppBundle は、本来、アドイン/プラグイン モジュールを WorkItem の実行環境にロードさせるために使用しますが、ここではコアエンジンに Newtonsoft.Json コンポーネントを認識させるために利用します。

下記は、 Newtonsoft.Json コンポーネント用 AppBundle の PackageContents.xml 記述と ZIP 圧縮ファイルが持つフォルダ階層の例です。

A directory structure showing a bundle named 'NewtonsoftJson.bundle' containing a 'PackageContents.xml' file and a 'Contents' folder with 'Newtonsoft.Json.dll'.
<?xml version="1.0" encoding="utf-8"?>
<ApplicationPackage
    AutodeskProduct="3ds Max"
    ProductType="Application"
    Name="Generic package"
    Description="Generic package for 3dsMax on Design Automation"
    AppVersion="1.0.0"
    UpgradeCode="{5CC054DF-72B7-46D0-BAE8-DCA6A31E26A9}"
    ProductCode="{56F41568-0851-4FF9-97A2-0707B7E9AA50}"
    SchemaVersion="1.0">
 
  <CompanyDetails
    Name="Autodesk Ltd,. Japan"
    Url="https://www.autodesk.co.jp&quot;
    Email="" />
  <RuntimeRequirements OS="Win64" Platform="3ds Max" SeriesMin="2020" SeriesMax="2023" />
    <Components Description="assemblies parts" >
        <RuntimeRequirements OS="Win64" Platform="3ds Max" SeriesMin="2020" SeriesMax="2023" />
        <ComponentEntry AppName="NewtonSoft.json.dll" Version="1.0.0" ModuleName="./Contents/Newtonsoft.Json.dll" />
    </Components>
</ApplicationPackage>

UpgradeCodeProductCode には、Visual Studio の [GUID の作成] ツールなどで作成した値を設定する必要があります。

GUID creation window displaying various format options for generating GUIDs in a software application.

もちろん、この AppBundle も WorkItem 実行前に登録しておく必要があります。

次に、MAXScript を WorkItem の作業領域にあわせて次のように変更しておきます。なお、DWG 読み込み時の形状の滑らかさの問題を回避する目的で、事前に読み込み用意したシーン ファイル(Table Fan.max)ファイルを開いて処理するようにしています。(3ds Max での DWG 読み込み時には「レイヤ、ノード階層となるブロック、マテリアルでよって分割」設定を使用して準備)

また、上記 AppBundle の効果によって、以前記述していた Newtonsoft.Json アセンブリ読み込み (json = dotNet.loadAssembly “Newtonsoft.Json.dll”)が不要になっている点にもご注意ください。

    /* JSON 読み込み関数
     参照:https://forums.cgsociety.org/t/json-and-maxscript/1552038/11
    */
    fn getJsonFileAsString filePath=(
        local  jsonString = ""
        fs=openFile filePath
        while not eof fs do(
            jsonString += readchar fs
        )
        close fs
        return jsonString
    )

    /* JSON 読み込み */
    paramsFilePath = "params.json"
    jsonString = getJsonFileAsString paramsFilePath
    myJObject = dotNetObject "Newtonsoft.Json.Linq.JObject"
    myJObject = myJObject.parse jsonString

    /* パラメータ取得 */
    global col = myJObject.item["color"].value as integer
    global leaf = myJObject.item["leaf"].value as booleanClass

    /* ColorX 画層オフ */
    global lay
    for i = 1 to 6 do
    (
        if col != i then (
            lay = LayerManager.getLayerFromName ( "Color" + i as string )
            lay.on = false
        )
    )

    /* Leaf 画層オン/オフ */
    lay = LayerManager.getLayerFromName ( "Leaf" )
    lay.on = leaf

    /* 既定パース ビューポート */
    viewport.activeviewport = 4
    viewport.SetRenderLevel #smoothhighlights
    viewport.setLayout #layout_4

    /* ART レンダラー設定 */
    global art = ART_Renderer()
    art.quality_db = 20
    art.render_method = 1
    art.anti_aliasing_filter_diameter = 2.0
    art.enable_noise_filter = true
    art.noise_filter_strength = 1
    renderers.current = art

    /* レンダリング & 生成画像保存 */
    global fname = sysInfo.currentdir + "/result.jpg"
    undisplay ( render outputfile:fname )

この MAXScript(myscript.ms)の処理を Design Automation API for 3ds Max で実行させるための Activity 記述は、次のようになります。

    "id": 'TableFanConfiguratorRender',
    "commandLine": ['$(engine.path)\\3dsmaxbatch.exe -sceneFile "$(args[InputMaxScene].path)" "$(args[MaxscriptToExecute].path)"'],
    "parameters": {
        "InputMaxScene": {
            "zip": false,
            "ondemand": false,
            "verb": "get",
            "description": "Input 3ds Max scene",
            "required": true,
            "localName": "Table Fan.max"
        },
        "Params": {
            "zip": false,
            "ondemand": false,
            "verb": "get",
            "description": "Input parameters to specify behavior",
            "required": true,
            "localName": "params.json"
        },
        "MaxscriptToExecute": {
            "zip": false,
            "verb": "get",
            "description": "MAXScript to render scene on 3dsmaxbatch.exe",
            "ondemand": false,
            "required": true,
            "localName": "myscript.ms"
        },
        "ImageOutput": {
            "zip": false,
            "ondemand": false,
            "verb": "put",
            "description": "output rendering image",
            "required": true,
            "localName": "result.jpg"
        }
    },
    "engine": 'Autodesk.3dsMax+2022',
    "appbundles": ['<your nicknme>.TableFanConfiguratorRender+dev'],
    "description": "Create rendering image"

この Activity を起動する WorkItem ペイロードは次のようなかたちです。

    // Create WorkItem
    var payload =
    {
        "activityId": DA4M_FQ_ID,
        "arguments": {
            "InputMaxScene": {
                "url": signedURLforInput,
                "headers": {
                    "Authorization": "Bearer " + credentials.access_token,
                    "Content-type": "application/octet-stream"
                },
                "verb": "get"
            },
            "Params": {
                "url": "data:application/json," + paramsJSON
            },
            "MaxscriptToExecute": {
                "url": signedURLforInput2,
                "headers": {
                    "Authorization": "Bearer " + credentials.access_token,
                    "Content-type": "text/plain"
                },
                "verb": "get"
            },
            "ImageOutput": {
                "url": signedURLforOutput,
                "headers": {
                    "Authorization": "Bearer " + credentials.access_token,
                    "Content-Type": "application/octet-stream"
                },
                "verb": 'put'
            }
        }
    };

    var uri = "https://developer.api.autodesk.com/da/us-east/v3/workitems&quot;;
    request.post({
        url: uri,
        headers: {
            'content-type': 'application/json',
            'authorization': 'Bearer ' + credentials.access_token
        },
        body: JSON.stringify(payload)
    }, function (error, workitemres, body) {
        …

ここまでの実装で、クライアント(Web ブラウザ)の指定によって変化するレンダリング画像を得ることが出来るようになります。

次の例では、Forge Viewer 上で指定した「羽根」の色と「葉っぱ」有無を、JSON パラメータとして Design Automation API for 3ds Max に渡し、クラウド上のコアエンジンと MAXScript でレンダリング画像を作成してから、クライアントの Web ページの <img> タグ領域に署名付き URL(Signed URL)を使って表示する例です。(Forge Viewer カンバスと同じサイズでオーバーレイ表示)

3Dモデルのテーブルファンコンフィギュレーターのインターフェース。ファンの色を選ぶカラーパレットと、レンダーボタン、数量指定フィールドが表示されている。背景は暗色。

この WorkItem の成果物であるレンダリング画像は、AutoCAD の Table Fan.dwg を 3ds Max で読み込んで用意した Table Fan.max の第 4 ビューポートの既定ビュー(カメラ視点)になっています。

3ds Maxでテーブルファンのモデリングを行う画面。正面ビューと側面ビューに加えて、シーンのプレビューが表示されている。

次回は、Forge Viewer 上に表示しているビュー(カメラ)を 3ds Max シーンに反映して、レンダリング画像を得る方法を考察してみます。

By Toshiaki Isezaki

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading