Skip to content
Ben Heasly edited this page Oct 21, 2013 · 6 revisions

It would be useful to have a layer of utility functions for working with RenderToolbox3 recipes. This Recipe API would sit on top of existing utilities, facilitating their use and making it easier to treat a rendering recipe as a coherent whole, rather than a collection of files.

Existing examples and utility functions should not need to be changed. These could continue to work in the “old style”, or they could be modified to use the Recipe API as desired.

What is a Recipe?

A RenderToolbox3 recipe is a set of files that makes up a complete set of rendering instructions. These in include:

  • the Collada parent scene file
  • the mappings file
  • the conditions file
  • the executive script
  • any auxiliary files like spectrum definitions and textures

As of RenderToolbox3 1.1, it is up to users to manage these files separately and to combine them into a cohesive set of rendering instructions. The Recipe API would provide data structures and utility functions that make it easy to create, execute, and archive a recipe as an intuitive unit.

Recipe Data Struct

The Recipe API should be centered around a Recipe Struct, which would contain references to individual recipe files and other useful data.

Several basic data fields would be required when creating a Recipe Struct. Many of these fileds correspond to variables that are declared at the top of a typical example scene executive script:

  • executiveScriptFile
  • parentSceneFile
  • conditionsFile
  • mappingsFile
  • auxiliaryFiles
  • hints
  • versionInfo
  • executiveScriptUserData

The Recipe Struct should also contain derived data fields and rendering data that are generated from the basic data fields above. Including derived data fields would provide convenient access to these fields and allow field data to persist across various functions. Some of these would be the results of parsing individual recipe files. Others would be actual rendering results:

  • conditions (name-value pairs, as from ParseConditions())
  • mappings (struct of mappings objects, as from ParseMappings())
  • nativeScenes (cell array of scene descriptions, as from MakeSceneFiles())
  • renderings (cell array of renderings, as from BatchRender())
  • images (cell array of images, as from MakeSensorImages())
  • montages (cell array of images, as from MakeMontage())
  • errorData (cell array of errors or exceptions from Recipe API functions)

Creating a Recipe

It should be easy to create a new recipe from scratch and start configuring it. The simplest recipe would be a struct with all the expected fields, and none of them filled in. It should also be easy to supply several basic data fields when making a new recipe.

  • recipe = NewRecipe(executiveScript, parentSceneFile, conditionsFile, mappingsFile, hints)

RenderToolbox3 versionInfo should be filled in automatically when the recipe is created. If conditionsFile or mappingsFile is provided, it should be parsed right away the results should be added to the appropriate derived data field. If hints is provided, it should be added to the hints field. If not, GetDefaultHints() hints should be added to the hints field.

Archiving a Recipe

The recipe API should have functions to archive an entire recipe to disk, and also unarchive it from disk. The archive should contain all files and data necessary for rendering the recipe. It should be possible to archive a recipe on one machine, send it to another machine, and immediately render there recipe on the new machine.

The archive itself should be a single file, created with using Matlab’s zip(). The zip archive should contain a mat-file that contains the Recipe Struct itself. The zip archive should also contain copies of any recipe files and auxiliary files referenced from the Recipe Struct. The archived Recipe Struct should have its file references changed so that they point to file copies in the archive, not to original files on the local machine.

By default, the Recipe Struct should be saved with only basic data fields, and derived fields should be cleared out. There should be an option to also include derived data fields in the archive:

  • zipFileName = ArchiveRecipe(recipe, zipFileName, saveDerivedData)
  • recipe = UnarchiveRecipe(zipFileName)

Recipe API Utilities

Once a user has a new or unarchived recipe, it should be easy to execute the recipe and invoke other Recipe API utilities. Generally, these utilities would process data in the recipe and fill in derived data fields of the Recipe Struct.

Some utilities would depend on other utilities. For example, a utility like MakeRecipeSceneFiles(), would require that ReadRecipeMappings() and ReadRecipeConditions() had already been invoked. Recipe API utilities should check for known preconditions and attempt to satisfy them automatically by invoking other Recipe API utilities.

Each Recipe API utility should attempt to trap errors and exceptions, save them, and append them to the Recipe Struct's errorData cell array. Recipe API utilities should also attempt to print warnings instead of throwing errors or crashing. This style of error handling should facilitate debugging for large, unattended batch jobs.

Here are several Recipe API utilities that we would want:

  • recipe = ReadRecipeMappings(recipe) -- invoke ParseMappings() and save results in the Recipe Struct
  • recipe = ReadRecipeConditions(recipe) -- invoke ParseConditions() and save results in the Recipe Struct
  • recipe = MakeRecipeSceneFiles(recipe) -- invoke MakeSceneFiles() and save results in the Recipe Struct
  • recipe = RenderRecipe(recipe) -- invoke BatchRender() and save results in the Recipe Struct
  • recipe = MakeRecipeMontage(recipe, etc.) -- invoke MakeMontage() and save results in the Recipe Struct
  • recipe = MakeRecipeSensorImages(recipe, etc.) -- invoke MakeSensorImages() and save results in the Recipe Struct

Executing a Whole Recipe

An additional top-level Recipe API utility would invoke the recipe’s executive script. This would be the expected entry point for executing recipes. The executive script would be written by users, and might refer to the Recipe Struct's executiveScriptUserData, which would also be supplied by users. The user's executive script could invoke "old style" RenderToolbox3 utilities like BatchRender(), and/or invoke the newer Recipe API utilities. The ExecuteRecipe utility would not have any preconditions to satisfy. It would attempt to trap and save errors and exceptions:

  • recipe = ExecuteRecipe(recipe)

Using a Recipe for Analysis

Analysis scripts should have easy access to the basic and derived data fields of a recipe.

A few Recipe API utilities should dig out numerical data rendering and image data from a recipe. By default these return a cell array of data from all conditions. It should also be possible to dig out data for a subset of conditions, as specified by an array of condition indices, or a cell array of condition names.

In the case of condition names, the utilities should examine the Recipe Struct conditions field in order to match condition names to conditions indices, then return data using those indices. If the Recipe Struct hints specifies a custom value for whichConditions, it may be necessary to modify indices using the custom condition numbers in whichConditions.

Here are some Recipe API analysis utilities that we would want:

  • [data, metadata] = getRecipeRenderingData(recipe, indexesOrImageNames) -- data should be a cell array of numeric matrices that contains a rendered radiance image. metadata should be a cell array of structs of metadata, which BatchRender() saved along with the radiance image.
  • images = getRecipeImageData(recipe, indexesOrImageNames) -- images should be a cell array of numeric image matrices
  • montages = getRecipeImageData(recipe, indexesOrImageNames) -- montages should be a cell array of numeric montage image matrices

It's likely that we will discover additional analysis utilities that we want to add to the Recipe API.

Clone this wiki locally