Maya Game Exporter Hax

I've been having some fun using the GameExporter that comes bundled with Maya. It's really nice to have a well featured exporter that you don't have to write yourself.

There is one thing that really bugs me about it though- I just can't find the button to suppress the "Replace files that already exist?" and "Success!" messages. Sure, I like the validation, but when I need to export 100+ files through a batch process it can get a bit tedious.

It doesn't feel as rewarding the 80th time...

Sooo... rather than doing anything fancy like finding the topmost window and deleting it or whatever, what if I just made it so the GameExporter just, you know, wouldn't do that, and just log to the script editor all nice like.

Enter global proc redefinition! Probably very familiar to anyone who uses MEL more than I do, you can redefine a gobal proc at any time, stomping it's previous behavior. (it's a bit more nuanced than that, but for my purposes, STOMP STOMP GOOD)

Anyway, you can probably see where this is going. I don't like the dialogue windows, and I would prefer them to just print to the log. Deep inside <InstallDir>\Maya2018\scripts\others are a bunch of scripts tellingly named "gameFbxExporter........mel".

Taking a browse through these I found three procedures that I happily made my own versions of:

global proc int gameExp_OverwriteExistingFile(string $path)
    print("Force overwriting files.\n");
    return 1;

global proc int gameExp_OverwriteExistingFiles(string $fileNameList[], int $overwriteListLimit)
    print("Force overwriting files.\n");
    return 1;

global proc gameExp_ShowMessage(string $message, int $msgType)
    print $message;
    print "\n";

Executing that through the script editor now logs my super helpful messages, but without the messy dialogue prompts.

Now, you might be thinking, it would be less destructive to have taken the whole original function and maybe, you know, added an option for suppressing the dialogue, and you would be right.


Calling Python from Substance Painter

Here is a quick code snippet for calling a Python script from Substance Painter and parsing the results.

The in/out is very simple, but serves as an example of using the alg.subprocess.check_output function to bridge the JS api and your own Python scripts.

// This can be called from the QML UI, or elsewhere in the plugin code. 
function GetAllFilesInDirectory(root)
  if (root == undefined)

  // I put my scripts in a relative path to keep my plugin tidy. 
  var script_path = "Scripts/";

  // Gathering files
  var ret = "NOSTRING";
      // The arguments are used as parameter inputs to the Python script. This requires some planning
      // and well communicated conventions, but works well enough. 
      ret = alg.subprocess.check_output(
          pypath, // Absolute path to interpreter.
          script_path, // Relative path to the py script.
          "log_files_of_type", // sys.argv[1], in this case the python method I'm calling.
          root, // sys.argv[2], used as a parameter for my method, in this case, the root directory to find files in.
          "spp" // sys.argv[3], used as a parameter for my method, in this case the file type extension to look for.

  // Iterating the returned string 
  var file_urls = [];
  var all_files = ret.split(/\r?\n/);
  for (x = 0; x < all_files.length; x++) 
      var local_file = all_files[x];
      if(local_file.length == 0)

      // Convert the file names to the native substance path URL format. 
      var project_url = alg.fileIO.localFileToUrl(local_file);
  return file_urls;

The Python script is also quite simple:

function GetAllFilesInDirectory(root)
#!/usr/bin/env python3

import glob
import sys

def log_files_of_type(root_dir, ext):
    Returns a list of all the files with a given extension in the named directory. 
    @param root_dir : the directory to parse. 
    @param ext : the extension to match. 
    for filename in glob.iglob(root_dir + '**/*.{0}'.format(ext), recursive=True):

if __name__ == "__main__":
    # Select the method to run based off of the arguments.
    # args are [0] script name, [1] method name. Subsequent args are arbitrary based on method called. 
    method_name = str(sys.argv[1])

    if method_name == "log_files_of_type":
        log_files_of_type(sys.argv[2], sys.argv[3])

I find that any script I write that passes information back and forth between two different platforms/interpreters usually comes with it's own set of headaches.

This kind of string parsing should look familiar to anyone who has done work with Photoshop bridging tools, or who has chosen to wrap the P4 command line interface themselves.

(PS... syntax highlighting borked... should fix that one of these days...)

Making A Hello World Substance Painter Plugin

Substance Painter plugins use the QT Meta Language, or QML files to build their interface. From Wikipedia:

It is a JSON-like declarative language for designing user interface–centric applications. Inline JavaScript code handles imperative aspects. It is part of Qt Quick, the UI creation kit developed by Nokia within the Qt framework.

Using this information, we can start building a window with a button to execute our script.

Visual Studio Code

For this tutorial I’m going to be using Visual Studio Code, a lightweight IDE from Microsoft. It’s not the same thing as Visual Studio, but it’s a nice scripting editor that’s available on Mac, Linux and PC. Other text editors like Sublime will do the job quite well.

Installing QML Syntax Highlighting

  • By default, QML files will appear as ordinary text files in Visual Studio Code. While this won’t stop you from being able to write the plugin, adding some QML support will make reading, organizing and editing our script easier. 
  • To do this, I’m going to install a QML extension in Visual Studio Code for Syntax highlighting. 
  • Use the View->Command Palette and type in Extensions: Install Extensions

  • Type in “QML” into the search bar and install the “QML language support for Visual Studio Code” extension. 
  • Once it is installed, restart visual studio code- your QML files will now have syntax highlighting. 

Starting our Plugin

  • This is some documentation online for substance painter scripting, but your installation also comes with some example plugins.
  • Plugins for Substance Painter live in 
    • Windows : C:\Users\*username*\Documents\Allegorithmic\Substance Painter\plugins 
    • Mac OS : /Users/<username>/Documents/Allegorithmic/Substance Painter/plugins 
    • Linux : /home/*username*/Documents/Allegorithmic/Substance Painter/plugins 
  • Note that for this tutorial I am using Substance Painter 2017x - the plugin directory changed since version 2.
  • This is where we will be creating our plugin- be sure to look through the other plugins there to see how they work. 
  • Valid plugins here will automatically be detected by Substance Painter when it is first opened, and become available via the Plugins menu option. 

Making a Hello World Plugin

  • For a plugin to be valid, it needs to have a definition, and a main entry point qml file. 
  • Navigate to the plugins directory for your system. 
  • Create a folder here called “HelloPlugin” 
  • Inside the plugin, create two files- 
    • plugin.json 
    • main.qml 

Filling in the main file:

  • The main.qml file is the entry point to your plugin. 
  • When the plugin is first loaded, this file will be used to initialize any additional data or properties the plugin needs, like adding extensions to the main toolbar. 
  • For now, we are going to make a really simple main function, which will log “Hello world!” to the console when the plugin is loaded. 
Ship it!

Filling in the JSON file:

  • The JSON file contains a manifest of metadata about your plugin. 
  • When you use a plugin’s “About” menu in Substance painter, the data you see there is populated from the plugin.json file of the relevant plugin. 
  • For example, the resources-updater plugin ‘about’ window looks like this: 

  • And looking at the plugin.json file of the resources-updater plugin, we can see how this data is defined: 

  • In this case, our JSON file is defining key : value pairs which are read by Substance Painter when it loads the plugin. 
  • We can use the same structure in our HelloPlugin to have a simple about window available. 

What we get so far:

When we load the plugin...

Our very informative "about" window. 
  • Right now we can see the plugin load, but it’s not particularly useful. 
  • We also get an about window courtesy of the plugin.json file. 
  • Let’s add a window- later we can use this window to add buttons and other functionality. 

Adding a window to the plugin

  • Create a new file in our HelloPlugin directory. 
  • Name it HelloWorldWindow.qml
  • This is going to where we define our window object.
  • Inside the file, we are going to add just enough code to define an extremely simple window. 
  • A few things to note: 
    • The window class is imported using the import AlgWidgets 1.0 call. 
    • The properties are like variables on the object type.
    • Assigning a specific id to an object is useful, as it allows us to reference this object elsewhere in our plugin. 
    • Likewise, properties like the visibility can be accessed via the object id.  
  • We now have enough code to build a simple window, but before we can see our window, we need to instantiate it in the main.qml file. 
  • Open the main.qml file, and at the top, add the following code: 

  • The code above creates an instance of a HelloWorldWindow and assigns it the id “window”. 
  • As the window is first instantiated when the plugin is loaded, in order to see it you will need to disable and re-enable the plugin. We will fix this later. 

Creating a button:

  • Rather than logging to the console when the plugin is loaded, let’s make a button to do that. 
  • Open the “HelloWorldWindow.qml” file. 
  • We are going to add three things- 
    • A series of layout elements. 
    • A label. 
    • A button. 

  • QML windows are created using a series of nested layout objects- for our purposes we are going to use a column, a rectangle and a row. 
  • We need to add additional import statements to access these object types- QtQuick and QtQuick.Layouts.
  • The column represents the overall layout- elements will be stacked within this shape in the order that they are added. 
  • The rectangle allows us to fill a partition of this column with a child layout. 
  • Finally the row layout allows us to add elements that will be rendered from left to right in the order in which they were added. 
  • Adding an AlgLabel and AlgButton in the row layout adds two new elements to our window. 
  • Finally defining the “onClicked” event for the button replaces where we were logging in main.qml on startup. 

  • I also commented out our log in the main.qml file. 

Our button is very chatty. 

Reloading your script:

  • At this point it’s good to know you can reload your script on the fly using the Plugins->HelloPlugin->Reload menu in painter. This is going to be super useful as we add more complexity. 

Adding our plugin to our toolbar:

  • Right now if you close the plugin window, it’s gone until you restart the plugin. 
  • To solve this issue, let’s add a button to the toolbar. 
  • The button in the toolbar is going to be pretty simple- all it’s going to do is toggle the visibility of our plugin window when it is pressed. 
  • First, we are going to make our HelloPluginWindow start invisible. 
  • We do this by changing the ‘visible’ property from ‘true’ to ‘false’ in the HelloPluginWindow.qml file. 
  • Now we are going to make a new file called ‘toolbar.qml’ 
  • It’s pretty simple, and similar to what we have done before- it’s just a row, with a button. 
  • Something important- the property variable “windowReference” will be filled in by our plugin when it is loaded. 
  • Because locally the windowReference starts as null, we wrap the calls to it later inside a try/catch block. 
  • This will stop terrible things from happening, like a crash, if something elsewhere in our script stops us from being able to make the window- instead of a crash we can log information to the console. 

catch(err), catch(err) not a belly scratch(err)...
  • Finally, in our “main.qml” file, we are going to add an new toolbar widget, which will instantiate our “toolbar.qml” as a button on the toolbar. 
  • We also assign our HelloWorldWindow instance to the windowReference variable in our toolbar button, using it’s id. 

  • The end result is a giant blot on the toolbar that we can click to turn our window on and off: 
Now thats UI...

Adding an icon

  • Finally, even with as good as our giant white square looks, we can add an icon.svg file to our plugin and add it to the toolbar.qml script to make it more appealing. 
  • This icon will be used in the substance toolbar to show us which button is linked to our window. 
  • Wikimedia commons has some good free svg files. For our purposes I am going to use this one
  • To add the icon to our toolbar, we need to add an Image block. 
  • This is what we are going to end up with: 

Hi! What a friendly little widget.
  • To make the icon to render correctly, we need to assign it a rectangle area, and make an image widget which is the child of that area. 
  • Using the Rectangle area gives us control over the hover state colors, and the anchors. 
  • The Rectangle is a child of the Button widget, and will inherit the button’s size information by using the “anchors.fill: parent” hint. 
  • We make sure the image also inherits these size settings by using the same hint in the child Image widget. 
  • This is the code we end up with: 

  • Beside giving us a nice little icon, there is something interesting going on where we define the color of the rectangle based off the hover state. 
This is called a ternary operator, defined by the ? symbol. 

  • In this instance the rect.hovered boolean allows us to change the assignment of a variable based on the state of a boolean. 
  • The hovered state can also let us add some other cool things, like animations, to our widgets. 

Making our icon animate:

  • As a fun little exercise, we are going to animate the hand using qml animation sequences
  • Let’s make the hand wave at us. 

  • Using this code, our hand will wave at us twice each time we hover over it. 


  • Note that the sequence is actually made out of a number of nested animations. These will run top to bottom, and in the cast of the nested sequence, each animation nested in that sequence will play before moving to the next animation in the parent sequence. 
  • Using this technique, you are able to add some complex behaviors to your widgets. 

So that's it for adding a basic plugin to Substance Painter! I'm going to follow this up with another post where we actually make it do something useful...



One line Unreal Engine 4 (mac os) Review:
Doing Ue4 development on a Mac is not a great experience.

Update: Just ran into this one... cleaning your project in XCode nukes the actual editor. Case in point.


Probably familiar to any TA or Engineer who deals with their graphics pipeline, RenderDoc is a tool I have found incredibly useful for debugging graphics in Unreal.

Formerly a Crytek internal graphics debugging tool, it was released to the public in 2014. The tool is developed by Baldur Karlsson, and it boggles my mind that a tool this gosh-darn useful has been given to the public for free. Kudos to Baldur and to Crytek.

Previously I was using NVIDIA Nsight to view and debug runtime atlased textures, and it was quite slow and somewhat unintuitive to sift through the outputs to find what I was after.

RenderDoc on the other hand is not only fairly straight forward to get running, the turn around time to get meaningful output for frame-debugging is extremely rapid.

For Ue4 integration I'm using Temaran's RenderDoc Ue4 plugin, which makes frame captures in game as simple as entering a command on the command line- RenderDoc.CaptureFrame

Check it out if you need to know more about your graphics pipeline.

Substance Designer foray

I'm pretty late to the whole substance craze, having spent three years in a mobile development environment where textures larger than 512x512 caused eyebrows to raise within the engineering team.

But I kept hearing things about it being amazing, so I thought I'd give it a shot. Here is my attempt at a "hand painted" look, based off of similar processes I'd use in Photoshop.

The texture is generated using an exported sbsar material and... well, thats it. I saw a nice example online of using a switch to swap out gradient maps in a material which allowed me to create a bunch of looks in the one material.

This tool makes me happy.

A Quick Checkin- Clion, Unreal and Mac

Super quick check-in. Hello world! For the last year or so, I've been working with Echtra Inc. on a great project as a Tools Engineer/Technical Artist guy.

The project is using the Unreal Engine, which I like more and more every day. On Windows, C++ plus Visual Studio Pro and Visual Assist is a great combo, and I happily churn through my daily tasks without fighting the tools too much.

Not so on my Mac at home. Programming in Unity on a Mac is great! Mono Develop isn't amazing, but it isn't terrible. But Unreal on a Mac. I want it to be fun, I want it to be possible, but I just can't get myself to like, let alone enjoy, XCode.

On that, for anyone thinking "well, you could just use blueprints..." etc, I feel it's too much of a shackle to not be able to just dive into the guts of it. C++ or bust.

So anyway, I recently adopted PyCharm at work and really enjoyed using it for my Python tools. I noticed that JetBrains also made an IDE called CLion, and they also had recently got it running with Unreal, so I thought, what the hell, why not.

Turns out their documentation is missing a couple of important notes, that maybe they take for granted, but after a couple of forum dives I managed to actually get it compiling, and running, my little test project.

So what was missing?

Something isn't set...

Once I run through their setup scripts, these were the things I needed to double check.

  • Make sure Mono is installed and up to date, and that the mono command is available in the terminal.
  • Once you generate your CLion project via the editor (make sure to follow the instructions here) you need to update the generated configs with paths to the editor executable. 
  • eg: /Users/Shared/Epic Games/UE_4.15/Engine/Binaries/Mac/
  • Finally, now that it's pointing to the editor correctly, add an absolute path to your project's .uproject file in the project arguments. 
If everything went well, you should now be able to build and run your project from within CLion. Bye bye XCode. I'll probably post some time in the future about how I'm finding CLion. Well, it compiles, and thats a start...