Skip to content
Ben Heasly edited this page Aug 21, 2015 · 26 revisions

RenderToolbox3 has a Recipe API which facilitates working with recipes. It gathers all the files and configuration for a recipe into a single Matlab struct, and provides utilities for making a new recipe, executing a recipe, packing up a recipe into a portable archive file, and unpacking recipe archives for renderings.

For a quick, practical introduction to the RecipAPI, see the MaterialSpherePortable Example Scene. It creates a recipe and packs up configuration, resources, and pre-generated scene files into a zip archive. Then it unpacks the recipe and picks up where it left off, rendering the pre-generated scene files. Since the recipe archive is complete and portable, the packing up and unpacking can happen on different days or different machines.

Overview

The Recipe API contains several utilities that help with making new recipes, executing them, packing them up into archives and unpacking them, and logging progress and errors.

The Recipe API also maintains a current recipe, which can be accessed and modified from m-functions and plain old Matlab scripts.

These utilities are outlined below. See the Matlab help() output for each utility or the low level Function Reference for details.

Making a Recipe

To start with, you can create a new recipe using NewRecipe(). This accepts as arguments all of the usual parts of a recipe, including the parent scene file, conditions file, mappings file, and executive script or scripts. It also accepts a hints (as returned from GetDefaultHints) which holds recipe-specific configuration.

NewRecipe() also accepts the name of a configuration script to be invoked automatically before executing the recipe. This can provide a consistently configured environment for the recipe to run in, and can prevent configuration from "leaking" unexpectedly between recipes.

The Recipe API adds the new ability to have multiple executive scripts or m-functions that contribute to the same recipe. These are provided as a list of script or function names passed to NewRecipe(). This allows the recipe to be executed in stages. For example, scene files could be generated in one stage, then rendered in another stage.

Executive scripts that are plain old Matlab functions can communicate with each other via the Recipe API's current recipe (see below). Executive m-functions must accept the current recipe struct as the first argument.

The Recipe API provides some "canned" executive m-functions for common tasks like generating scene files, rendering scenes, and processing images. Respectively, these are MakeRecipeSceneFiles(), MakeRecipeRenderings(), and MakeRecipeMontage(). These utilities are wrappers around the usual batch rendering utilities that accept a recipe as their first argument, and automatically log their progress and errors in the recipe log (see below).

NewRecipe(), automatically parses your recipe's conditions and mappings files into structs that are saved within your recipe struct. This allows your executive scripts to access conditions and mappings values without having to worry about parsing text files. Internally, NewRecipe() calls ReadRecipeConditions() and ReadRecipeMappings(). You can also call these functions directly.

Executing a Recipe

After you create a recipe, you can execute it with ExecuteRecipe(). This invokes each of your executive scripts in sequence. It also automatically calls your recipe's configuration script using ConfigureForRecipe() and moves to your recipe's working folder using ChangeToRecipeFolder().

By default, ExecuteRecipe() invokes all of your executive scripts. You can also pass in a list of indices to run a subset of scripts, or to run them out of order. This allows you to execute the recipe in stages.

Execution results like generated scene descriptions and output data file names are added to your recipe struct automatically. This makes it easier for your executive scripts to access these results. You can get clear out of all the execution results and start with a fresh recipe struct using CleanRecipe().

Packing Up and Unpacking a Recipe

The Recipe API adds the new ability to "pack up" a recipe and save it in an archive file, and then later "unpack" the same recipe. This would allow you to execute a recipe in stages, with different stages happening on different machines. It would also allow you to make a recipe, pack it up, and send it to a collaborator. It would also facilitate back-ups and documentation of recipes and projects.

PackUpRecipe() takes your recipe struct and writes it to a new zip-file. The same zip-file also includes input files and resources that are part of your recipe, such as the parent scene, texture images, and spectrum definitions. By default, PackUpRecipe() archives all resources and output files that it finds in the recipe's working folder. It can optionally exclude certain files, such as boring temporary files.

UnpackRecipe() takes a zip-file recipe archive and returns to you the recipe struct. It also extracts archived input files, resources, and output files into a new working folder. Once you've unpacked a recipe, you can execute it like any other recipe using ExecuteRecipe().

Logging and Errors

The Recipe API adds a new logging feature for RenderToolbox3. A recipe's log is a struct array, where each element describes some progress or error that happened while executing a recipe. Many of the Recipe API utilities make entries in your recipe's log automatically. This should make it easier to debug things when they go wrong, because the log gives you a trace through your recipe's execution.

Your executive scripts can add custom log entries using AppendRecipeLog().

PrintRecipeLog() prints a summary of your recipe's log to the Matlab command window. For example:

executed: MakeRecipeSceneFiles (index: 1)
at: 06-Jun-2014 15:03:15
comment: run automatically by ExecuteRecipe
with error: none

executed: MakeRecipeRenderings (index: 2)
at: 06-Jun-2014 16:37:52
comment: run automatically by ExecuteRecipe
with error: none

executed: MakeRecipeMontage (index: 3)
at: 06-Jun-2014 16:37:55
comment: run automatically by ExecuteRecipe
with error: none

WriteRecipeLog() will write the same kind of summary to a file.

The recipe log may contain execution errors as well as progress. GetFirstRecipeError() prints a summary of the first error found in the log. It can also re-throw the error, in order to print a handy Matlab stack trace in the command window. This should facilitate debugging, since it would be painful to have to dig through the recipe log struct array manually.

The Current Recipe

The Recipe API supports multiple executive scripts or m-functions that contribute to a recipe's execution. This raises the question, "How do all the executive scripts communicate?" Executive m-functions must accept a recipe struct as the first argument, so they are able to communicate by accessing and modifying the recipe struct.

But what about plain old Matlab scripts that can't accept any arguments? These must use the Recipe API current recipe, which is a recipe struct that's persistent in Matlab memory. Scripts can access the current recipe as the output of CurrentRecipe(). Scripts can modify the current recipe by passing in a recipe struct as the first argument to CurrentRecipe(). This gives plain old Matlab scripts a way to communicate in a way that's similar to executive m-functions.

ExecuteRecipe() automatically sets the current recipe before invoking each plain old Matlab script. It also updates the current recipe immediately afterwards. This allows executive m-functions and plain old Matlab scripts to be interleaved in the same recipe, and still all communicate with each other.

Clone this wiki locally