新しいインポータを作成する

imports 拡張機能を使用すると、指定したファイル タイプの新しいアセットの読み込みプロセスをプラグインで処理することができます。ユーザが指定されたタイプのファイルを読み込むたびに、エンジンはプラグインを呼び出してこのファイルを読み取り、これを標準の Stingray アセット(.texture ファイルなど)に変換します。

インポータを配置すると、ユーザは Stingray エディタでサポートされている次の読み込み方法のいずれかを使用して、カスタム ファイル タイプを読み込めるようになります。

環境設定

次のように、.stingray_plugin 記述ファイル内で imports 拡張機能を設定します。

// The editor/plugins/asset_browser/asset-browser.stingray_plugin file defines different importers:
imports = [
    {
        types = ["fbx", "bsi"]
        label = "Scenes"
        regroup = true
        priority = 1 // Low priority because there is a dialog involved.
        options = {
            types = ["types/material_import_options", "types/scene_import_options"]
            validate = {
                type = "js"
                module = "asset-browser-actions"
                function_name = "validateSceneImportSettings"
            }
        }
        do = "import-fbx"
    }

    {
        types = ["dds", "tga", "png", "jpg", "jpeg", "tif", "tiff"]
        label = "Textures"
        regroup = true
        do = "import-core-assets"
    }

    {
        types = ["srz", "zip"]
        label = "Packages"
        regroup = true

        do = [
            {
                type = "js"
                module = "asset-browser-actions"
                function_name = "importPackages"
            }
        ]
    }
]

types

プラグインで読み込むことができるファイル拡張子のリストです。必須

label

ファイル選択ダイアログ ボックスで types をグループ化する説明ラベルです。必須

regroup

複数のアセットを 1 回のバッチ操作で読み込む必要があるのか、それとも順番に実行される独立したアクションとして実行する必要があるのかを決定するブール値です。この値を true に設定すると、do 設定で指定した各アクションが、ユーザが読み込むよう要求したすべてのファイルのリストに渡されます。この値を false に設定すると、do アクションは一度に 1 つのアセットに渡され、すべてのアセットが読み込まれるまで次のアセットを使用して再度呼び出されます。オプション

do

ユーザがこのインポータで処理されるタイプのファイルを読み込むときに、エディタが実行するアクション、またはアクションの配列です。「アクションを登録する」を参照してください。必須

priority

同じタイプのファイルを処理する複数のインポータが登録されている場合、この値は相対的な優先順位を定義します。この値により、エディタがインポータを実行する順番が決まります。優先順位が最小のプラグインが最初に実行されます。オプション

options

読み込み順に従ってエディタが各 do アクションに渡すカスタムのユーザ定義 SJSON データです。たとえば、上記の .fbx 読み込み拡張機能には、一般的な読み込みダイアログ ボックスで使用される .type ファイルの仕様、および検証関数が含まれます。詳細は、以下を参照してください。オプション

FBX インポータ

Stingray Asset Browser プラグインには、複数のファイル タイプの読み込み拡張機能が含まれています。すべての定義は editor/plugins/asset_browser/asset-browser.stingray_plugin に格納されています。

.fbx ファイル インポータを参照して、その仕組みを確認してみましょう。次に、この拡張機能の環境設定を示します。

{
    types = ["fbx", "bsi"]
    label = "Scenes"
    regroup = true
    priority = 1 // Low priority because there is a dialog involved.
    options = {
        // Specifies how to populate the Generic Import Dialog
        types = ["types/material_import_options", "types/scene_import_options"]

        // Function used to validate importation settings while the user is in the Generic Import Dialog.
        validate = {
            type = "js"
            module = "asset-browser-actions"
            function_name = "validateSceneImportSettings"
        }
    }

    do = {
        name = "import-fbx"
        type = "js"
        module = "asset-browser-actions"
        function_name = "importFbx"
    }
}

importFbx 関数は極めて複雑です。読み込み設定ダイアログ ボックスの表示、ダイアログ オプションとオプションの既定値の結合、重要なプロセス自体のトリガを処理します。

importFbx を参照して、読み込み中にエンジンがこの関数に渡すパラメータを確認してみましょう。

// options: corresponds to the options objects in the import extension descriptor above.
// previousResult: if there was a previous importer in the chain of importation, these are all the results (compilation results, asset created).
    // previousResult[0]: compilation information
    // previousResult[1]: an object packaging all created/modified/removed assets during importation
// assets: the list of files to import
// directory: the destination directory where the importation is to happen, relative to the project.
// flags: a set of flags passed by the editor for importation. Currently, the flag.reImport value indicates that an asset needs to be reimported.
importFbx: function (options, previousResult, assets, directory, flags) {
    let sceneImport = new SceneImport(options, flags, processFlags, showDialog, null, options.validate);

    return sceneImport.doImport();
}

次に示す asset-browser-actions.js からの抜粋は、sceneImport.DoImport 呼び出し中に発生する動作を示します。

importFbx: function (options, previousResult, assets, directory, flags) {
    let sceneImport = new SceneImport(options, flags, processFlags, showDialog, null, options.validate);
    return sceneImport.doImport();
}

// From SceneImport class:
doImport() {
    return projectUtils.getSettings(fbxImportSettings)
        // Lots of chaining of processing to ensure default Fbx importation options are properly saved/merged with user defined importation options:
        .then(this.processInfo.bind(this))
        .then(this.processImportOptions.bind(this))
        .then(this.importDefault.bind(this))
        .then(function (categories) {
            if (!projectService.showCreateExtraRoot) {
                let mesh = _.find(categories, {'id': 'mesh'});
                if (mesh) {
                    mesh.definitions.fields.CreateExtraRoot.editor.showLabel = false;
                    mesh.definitions.fields.CreateExtraRoot.editor.showValue = false;
                }
            }

            // Importation without a dialog: import fbx directly
            if (this.hideDialog)
                return importService.importFbx(this.importAssets, this.defaultSettings, this.hideDialog);

            let title = 'Import FBX';
            if (this.flags && this.flags.reImport)
                title = 'Re-Import FBX';

            // Trigger the importation dialog. This will evenetually called the importservice to trigger fbx importation.
            return this.showDialog(categories, this.importAssets, title, this.validation);
        }.bind(this));
}

// Show dialog for Fbx import:
var showDialog = function (categories, assets, title, validation) {
    if (!title)
        title = 'Import';

    return assetUtils.trackFileSystemChanges(function () {
        return hostService.openDialog(title,
                [{ assets: assets, categories: categories, plugin_name: 'asset-browser', validate: validation }],
                require.toUrl('core/views/import-dialog'),
                { width: 430, height: 760 })
            .then(function (result) {
                if (result.accepted) {
                    var data = result.data[0];
                    projectUtils.setSettings(fbxImportSettings, data.options);
                    return Promise.all([
                        waitForImportCompletedPromise(),

                        // calls to the importService to effectively do Fbx import.
                        importService.importFbx(data.assets, data.options)
                    ]);
                }
            });
    });
};

登録済みファイルの読み込みを拡張する

各ファイル タイプは複数の異なる読み込み拡張機能で処理することができます。エンジンは優先順位に従ってすべてのインポータを呼び出します。これにより、他のプラグインで既に処理されているファイル タイプを読み込むときの動作を拡張することができます。たとえば、ファイルから追加情報を抽出したり、前の読み込みシーケンス中に作成されたアセットを微調整したりできます。

次に、読み込み中に作成されたすべてのアセットをコンソールに表示するように、Asset Browser の既定の .fbx インポータを拡張するインポータの例を示します。

次に、.stingray_plugin ファイルの拡張機能の定義を示します。

// From asset-browser.stingray_plugin
{
    types = ["fbx"] // Another importer for Fbx
    label = "Fbx Log"
    regroup = true
    priority = 1000 // Will run AFTER normal Fbx importation
    do = {

         type = "js"
         module = "asset-browser-actions"
         function_name = "fbxImportLog"
     }
}

次に、結果をログに記録する fbxImportLog 関数を示します。

fbxImportLog: function (options, previousResult, assets, directory, flags) {
    let fbxImportReport = previousResult[1];

    console.log('Fbx Importation Log:');
    console.log('New asset created', fbxImportReport.added);
    console.log('Existing assets modified', fbxImportReport.changed);
    console.log('Assets remove', fbxImportReport.removed);
}

フォントの読み込みと一般的な読み込みダイアログ ボックス

Stingray は一般的な読み込みダイアログ ボックスを利用するフォント インポータもサポートしています。

// From font-importer.stingray_plugin:
imports = [
    {
        types = ["ttf" "ttc" "otf" "otc" "cff" "woff" "fnt" "pfa" "pfb" "pfr"]
        label = "Font"
        options = {
            types = ["font-import-options"]
            validate = {
                type = "js"
                module = "font-importer-module"
                function_name = "validateImportOptions"
            }
        }
        do = [

            // Step 1 - Open dialog with the import options
            {
                type = "js"
                module = "font-importer-module"
                function_name = "fontImporterDialog"
            }

            // Step 2 - Import actual asset as a Stingray resource
            {
                type = "js"
                module = "font-importer-module"
                function_name = "importFontFile"
            }
        ]
    }
]

読み込みダイアログ ボックスの設定方法を見てみましょう。

// From font-importer-module.js
function fontImporterDialog (importOptions, previousResult, file, destination, flags) {
    if (!_.isArray(file)) file = [{ file: file, directory: destination }];

    // Loop over the different type files spcified in importOptions.
    let types = importOptions.types[0].types;
    let categories = [];

    // Create a list of categories with all the different properties specified in the type descriptor:
    let categoryNames = Object.keys(types);
    _.each(categoryNames, function (categoryName) {
      let category = {};
      category.id = categoryName;
      category.options = types[categoryName].editor;
      category.definitions = {
          type: types[categoryName].type,
          fields: types[categoryName].fields
      };

      category.default_values = dataTypeService.createDefaultValue(types[categoryName]);
      category.default_values.Name = fsUtils.getFileName(file[0].file, true);
      categories.push(category);
    });

    // Pop the Generic Import Dialog passing it the properties to show:
    return hostService.openDialog('Distance Field Font Importer',
          [{ assets: file, categories: categories, plugin_name: 'font-importer', validate: importOptions.validate }],
          require.toUrl('core/views/import-dialog'),
          { width: 510, height: 430 });
}

importFont では、フォント ファイルからデータを抽出して、テクスチャ アセットを記述する一般的なツールの実行方法を処理します。

function importFontFile (importOptions, dialogResult, file, destination/*, flags*/) {
  if (!dialogResult.accepted) {
      return console.info('Font import has been cancelled.');
  }

  var importOptions = dialogResult.data[0].options;
  if (!importOptions)
      throw new Error('Nothing to import');

  return projectService.relativePathToAbsolute(destination).then(function (absoluteDestination) {
    absoluteDestination = fsUtils.cleanPath(absoluteDestination);
    return projectService.absolutePathToRelative(absoluteDestination+"/"+importOptions.Name).then(function (assetpath){
      return locatorService.getToolChainDirectory().then(function (tccDir) {

         // Start a command line tool to generate an atlas from a font:

          var cmd_Args = ["-generateAtlas", file, "-size", `${importOptions.CharWidth}`, `${importOptions.CharHeight}`, "-pxrange",`${ importOptions.PixelRange}`, "-autoframe",
                          "-atlasRange", `${importOptions.UnicodeRangeStart}`, `${importOptions.UnicodeRangeEnd}`, "-atlasItemsPerRow", `${importOptions.ItemsPerRow}`,
                          "-o", `${absoluteDestination}/${importOptions.Name}.dds`, "-outputFontInfo", `${absoluteDestination}/${importOptions.Name}.font`];
          if(importOptions.BlackBorder)
            cmd_Args.push("-blackBorder");

          if(!importOptions.Autoscale) {
            cmd_Args.push("-scale");
            cmd_Args.push(`${importOptions.Scale}`);
          }

          // hostService allows to start external process and wait for their execution to be done:
          var msdfgenExe = fsUtils.join(tccDir, "tools", "msdfgen.exe");
          return hostService.startProcess(msdfgenExe, cmd_Args, {
            UseShellExecute: true,
            CreateNoWindow: true,
            WorkingDirectory: absoluteDestination,
            WaitForExit: true
          });
      }).then(function (/*processDescriptor*/) {
          // Write a material file in the folder where we started the font importation:
          // fileSystemService allows to read/write files. There are even function to write Json files directly.
          return fileSystemService.writeJSON(`${absoluteDestination}/${importOptions.Name}.material`, {
              shader: "gui:DEPTH_TEST_ENABLED:DIFFUSE_MAP:MEDIAN_BIT_ALPHA",
              textures: {
                diffuse_map: `${assetpath}`
              }
          });
      }).then(function () {

          // Writes a texture file for the newly generated atlas.
          return fs.writeJSON(`${absoluteDestination}/${importOptions.Name}.texture`, {
              common: {
                input: {
                  filename: `${assetpath}`
                },
                output: {
                  apply_processing: true,
                  category: "",
                  cut_alpha_threshold: 0.5,
                  enable_cut_alpha_threshold: false,
                  format: "R8G8B8A8",
                  mipmap_filter: "box",
                  mipmap_filter_wrap_mode: "mirror",
                  mipmap_keep_original: false,
                  mipmap_num_largest_steps_to_discard: 0,
                  mipmap_num_smallest_steps_to_discard: 0,
                  resident_mips: 0,
                  srgb: true,
                  streamable: false
                }
              }
          });
      }).then(function() {
        return fs.copy(file, absoluteDestination+"/"+fsUtils.getFileName(file));
      });
    });
  });
}

一般的な読み込みダイアログ ボックスを使用する

一般的な読み込み ダイアログ ボックスではタイプ記述子を使用して、プロパティ エディタ ウィジェットを含むダイアログ ボックスに自動的に入力しますこのダイアログ ボックスでは、ユーザが入力ダイアログ ボックスで設定できるデータの構造を定義する .type ファイルを指定します。このデータが表示されるように、プロパティ エディタ ウィジェットが設定されます。

例として、次に font-import-options.type ファイルからの抜粋を示します。

export = "#fonts"
types = {
    fonts = {
        type = ":struct"
        editor = {
            label = "Distance Field Options"
        }
        fields = {
            AdvancedOptions = {
                type = ":bool"
                default = false
                editor = {
                    label = "Distance Field Options"
                    description = "Options for generating distance field font resources"
                    showLabel = false
                    showValue = false
                    enable_category = true
                }
            }
            Name = {
                type = ":string"
                default = ""
                editor = {
                    label = "Name"
                    description = "Name of the font asset"
                }
            }
            CharWidth = {
                type = ":number"
                default = 32
                min = 1
                editor = {
                    label = "Character Width"
                    description = "Image width of each character in pixels"
                    step = 1
                }
            }
            CharHeight = {
                type = ":number"
                default = 32
                min = 1
                editor = {
                    label = "Character Height"
                    description = "Image height of each character in pixels"
                    step = 1
                }
            }
        }
    }
}

基本的に、これはデータの特定の部分を解釈して編集する方法を指定する SJSON ファイルです。Stingray プロパティ エディタは .type ファイルを使用してデータを自動入力し、JSON データ モデルを編集します。

.type ファイルの詳細については、「Stingray タイプ システム」を参照してください。タイプを使用してプロパティ エディタの表示内容をコントロールする方法の詳細については、「組み込みメタデータのプロパティ」を参照してください。