Hints and Tips

Shotgun – Bootstrapping and Deadline

By | Blog, Hints and Tips, Shotgun | No Comments

Following the release of Shotgun’s new descriptor workflow along with its Bootstrap API I’ve finally got round to updating our pipeline to take advantage of these new features. It has been a big improvement over the old Toolkit pipeline configuration deployment approach and has definitely aided our development and deployment processes.

There has been one hurdle we’ve had to overcome; when bootstrapping in to a Shotgun environment on rendernodes with multiple slaves, it appears that the bootstrap process fails if there are concurrent threads all trying to copy the pipeline config to a shared location.

Thankfully there was a simple solution which involves setting up a custom which appears to have resolved the issue.

  1. Create a file called and save it to your deadline repos /custom/plugins directory. This file will be run before every job that’s loaded by each slave.
  2. Copy the following code in to the file :

What this script does is set a slave-specific SHOTGUN_HOME location(in this case c:\your_studio\shotgun\cache\slaveName ensuring that each slave can bootstrap in to its own folder. Problem solved!


Shotgun Tutorial – Using hooks to configure your renderfarm.

By | Blog, Hints and Tips, Shotgun, Tutorials | No Comments
Note : This post is specifically for a Windows based Shotgun installation, but it should be fairly easy to adapt to other environments.

One of the tricky things about getting Shotgun up and running smoothly, is getting your renderfarm environment configured correctly.

The Shotgun docs offer two approaches that are worth trying here and here but as these are specific to Nuke they may not do everything you need to get all your DCCs configured correctly.

Another approach to try is something that we have implemented here at HaloVFX; using Shotgun hooks to build/set your environments from scratch. This approach was developed largely to avoid the need to use 3rd party tools or scripts to set and update environment variables across the studio and renderfarm.

There are a number of benefits to this approach

  • your environment is set entirely within the Shotgun ecosystem; there is no need to launch Shotgun from a custom in-house launcher.
  • you can take a new workstation, install Shotgun and your DCCs, and without any further configuration or setting of a single environment variable, you can open Shotgun.
  • the studio environment can be configured per-project.
  • workstations/rendernodes require no environment configuration.
  • your entire environment is included in the pipeline configuration for any given project facilitating easy backup and retrieval.
  • your environments are entirely isolated per-project with no risk of changes causing un-expected problems across other projects.

In principle, this approach requires three steps.

  1. set studio-wide environment variables inside your core hook
  2. set project specific environment variables in your hook.
  3. update your deadline submissions scripts to include all required environment variables in the job file.

Update Shotgun ‘’

In your project pipeline config ‘core/hooks’ folder, make a copy of the ‘’ file from your core ‘install/core/hooks’ folder. (whether shared or localized)

You can add project-wide environment variables here by adding entries to the EngineInit ‘execute’ method.

class EngineInit(Hook):
    def execute(self, engine, **kwargs):
        os.environ['NUKE_PATH'] = some_path
        os.environ['NUKE_FONT_PATH'] = some_path
        os.environ['NUKE_LUT_PATH'] = some_path

Update Shotgun ‘’

In your project pipeline config ‘hooks’ folder, make a copy of the ‘’ file from your ‘config/hooks’ folder.

To help identify environment variables we want to keep track of, they are all prefixed with “HV_”. We will use this later in the deadline submission script to choose which environment variables to pass on to our rendernodes.

It’s up to you want variables you need to define for your pipeline, but here are some paths I found useful to have to use in other tools and apps.

import sys, os

def execute(self, app_path, app_args, version, **kwargs):
    self.parent.log_info("\n\n######## #########")

    system = sys.platform
    self.path_sep = os.pathsep
    engine = self.parent.get_setting("engine")

    # Set some utility global environment vars
    os.environ['HV_PROJECT'] = self.parent.tank.project_path
    os.environ['HV_PROJECT_PATH'] = self.parent.tank.project_path
    os.environ['HV_PROJECTCODE'] = os.path.split(self.parent.tank.project_path)[1]

You can define environment variables for specific applications by testing for the engine being called :

if engine in ["tk-houdini"]:
    arnold_path = r'C:/solidangle_houdini/htoa-1.14.2_r8c78c1d_houdini-{0}'.format(version)
    arnold_bin_path = r'C:/solidangle_houdini/htoa-1.14.2_r8c78c1d_houdini-{0}/scripts/bin'.format(version)

For Houdini, you’ll want to set a number of paths including the following.

os.environ['HOUDINI_PATH'] = some_path
os.environ['HOUDINI_OTLSCAN_PATH'] = some_path

And if you need to add paths to your PYTHONPATH you can add the following :

os.environ['PYTHONPATH'] = some_other_path + os.pathsep + os.environ['PYTHONPATH']

Update Deadline submission scripts –

To pass your environment variables through to deadline, you need to edit the submission script and insert something like the following after the jobInfoFile has been opened and assigned to the fileHandle variable;

after :

fileHandle = open( jobInfoFile, "wb" )
fileHandle.write( EncodeAsUTF16String( "Plugin=Nuke\n" ) )

add something like this:

env_vars = ['an_environment_variable','another_environment_variable','etc'] # CREATE A LIST OF ENVIRONMENT VARIABLES YOU WANT TO PASS ON WITH YOUR JOB
            EncodeAsUTF16String("EnvironmentKeyValue{0}={1}={2!s}\n".format(env_index, env_var, os.environ.get(env_var)))

of course, this example is specific to the deadline submission script for Nuke, but you should be able to adapt this code fairly easily for the submission scripts in other applications.

Once you have this implemented successfully, you should see in your ‘job details’ window of your Deadline monitor, the environment variables you defined. When the rendernode launches the job plugin, the environment of the rendernode will be updated with these environment variables.

It’s up to you how far you push this approach; you can theoretically recreate the entire environment of the machine/session submitting the job without having to worry about how the rendernode has been configured.


When implementing this approach. I half expected there to be  a number of unexpected and unwanted consequences, but I’m glad to report that in production this has proven to be very robust. As an added bonus it also provided me with excellent information with which to debug problematic jobs on the farm!

Thanks for reading, and I’ll look forward to hearing your thoughts on how this approach can be further developed. If you have taken another approach to this then I’d love to hear about it!


Soft Instancing and expressions

By | Hints and Tips, Maxscript, Mission Control | No Comments

In advance of more detailed documentation being added to the website, I thought I would share a walkthrough that demonstrates some of the more novel features of Mission Control that I hope some of  you will find to be a huge time-saver. By following these instructions, you will be able to:

  • Quickly ‘instance’ individual parameters between objects (so called soft-instancing)
  • Perform calculations in-cell using values and/or object parameters.
  • Create expression controllers on the fly.


Before you start, please bear in mind that the “Enter” key does not work for ending the editing of cells. You need  to click on another cell or double tap the “tab” key to finish the edit.

  • Create two omni lights; omni001 and omni002.
  • Add the multiplier parameter column to the spreadsheet.
  • Select a float value.
  • Click to start editing the value.
  • Type “=2+2” and click on another cell to end the data entry (double tapping tab also works).
  • The value should now reflect the calculation.
  • You can also submit object parameters eg “$omni002.multiplier” as part of the calculation.
  • In the multiplier for omni001, type “=(omni002.multiplier *2) + 0.5)”
  • You should now see a value that reflects this calculation.
This approach is for single hit operations; eg there is no linking.
Lets try something else :-
  • In the multiplier for omni001, type “==omni002.multiplier” (Notice the double equals sign).
  • Now if you middle-click and adjust the multiplier of omni002, the multiplier for omni001 should also update.
  • You will also notice a blue indicator on the cell. This indicates that the cell now has an animation controller.
  • What has happened is that when entering “==” you are creating a scripted controller with the script entered following this prefix.
  • You can edit this expression in two ways.
    • double click the cell to open the expression editor.
  • or
    • click the status-bar to open the util bar.
    • Select the “Animation” tab.
    • Select the animated cell.
    • you should now see the expression in the ‘Expression’ box below.
    • You can also add notes to the “Notes box” which will be stored in the script controller.
    • You can now copy and paste this scripted cell to other cells as instances to quickly link the multipliers of many lights.

The camel that got the hump, or how I learnt to respect existing Names

By | Blog, Hints and Tips, Maxscript | No Comments

Whilst developing Mission Control for 3ds Max, I came across an unusual bug where a name I was using “#vrayproperties” was becoming camel-cased to “#vrayProperties” and causing my script to fail. I had previously called the name #vrayProperties, so I thought I hadn’t managed to change all the instances to non-camelCased, but after doing a string search and replace, there were definitely no cases of the camelCased version in my script.

I though that maybe the value was being retained in a global variable, but even restarting max didn’t fix the problem. Then I thought it might be a weird dotnet persistance issue, so I restarted windows… but still no joy! Then it clicked, perhaps the name is being defined somewhere else before my script is being executed? So, to test this I replaced #vrayProperties with #vrproperties, and lo and behold the problem was solved.

Lesson learnt, be careful with name naming!

Skip to toolbar