Parser module

JSON serialization/deserialization, the darts plugin factory, and scene parser.

JSON

JSON is a human-readible data interchange format for expressing attribute-value pairs. You can read more about it here:

Darts relies on the great nlohmann-json C++ library for parsing and manipulating JSON data.

In darts, we will use JSON for two purposes:

  1. As a generic way to pass named parameters to functions
  2. As a way to specify and load text-based scene files

Darts already supports serializing all the basis darts data-types (such as floats, strings, Vec, Color3, etc.) to and from a JSON object. Additionally, most of the constructors for ray tracing classes in darts take a single JSON object as a parameter. This allows us to have a uniform interface for creating these structures while allowing the constructors to retrieve the necessary values from the JSON object. This dramatically simplifies our code for writing a parser for reading scene files from disk.

darts_tutorial1.cpp provides a gentle introduction to this functionality and nlohmann-json website provides extensive documentation.

Plugins and factories

Darts relies on a Factory pattern a automatically register new plugins that extend its functionality. Dart plugins are simply classes that extend a few abstract base classes within darts, such as Material, Surface, and later we will also introduce Texture, Integrator, Sampler, and more.

Registering plugins

Whenever you are adding a new plugin to darts, you will call the macro #DARTS_REGISTER_CLASS_IN_FACTORY, which creates some code that informs a global #DartsFactory how to create objects defined by your plugin. For instance, if we were adding a hypothetical SuperAwesome Material to darts, we would include:

DARTS_REGISTER_CLASS_IN_FACTORY(Material, SuperAwesome, "super-awesome")

at the bottom of our implementation file for the plugin. This informs darts about a new #Material of type SuperAwesome, and associates the keyword super-awesome with it. This allows us to create such a Material in other parts of darts (or from a file on disc) without ever having to #include a header for that plugin. We can simply call the function #DartsFactory<Material>::create(). This function accepts a #json object as a parameter, and returns a shared_ptr to a Material. It determines the specific Material class to create by looking for a "type" field in the json object. If the value is "super-awesome" it will create our SuperAwesome Material for us.

More generally we will use #DartsFactory<Type>::create() with different classes for the Type, such as Surface, Texture, etc.

Registering instances

It is often necessary to have one element in a scene refer to another one by name (for instance, if we'd like to load a texture and use it to texture more than one Material). Each JSON plugin specification can therefore contain an optional "name": field. Latter parts of the JSON file can refer to that instance by this name. To facilitate this, #DartsFactory::register_instance() allows storing previously parsed/created instances into a registry. These can later be retrieved by name using #DartsFactory::find(). The darts basecode already has an example of how to accomplish this for Materials in the #Scene::parse() function, but you may want to add similar functional for other types of plugins as you extend darts.

An example JSON specification

In addition to the "type": and "name": fields, plugins may require any number of additional parameters which can be specified using additional fields in the #json object. A full specification of our hypothetical SuperAwesome Material might look as follows in JSON:

"materials":
[
    {
        "type": "super-awesome",
        "name": "my awesome material",
        "fuzziness": 1.5,
        "color": [1, .2, .7],
        "awesomeness-factor": 1000
    }
]

The constructor for SuperAwesome would then be responsible for retrieving the values from the #json object and appropriately initialize the material.

The darts scene parser

You will be implementing the top-level darts parser in Scene::parse(). It takes a json object which contains the contents of a json file loaded from a disc.