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.

Classes

template<typename Object, typename... Args>
class Factory
Abstract factory used to construct objects by name.
template<typename T>
class DartsFactory
A specialization of the Factory for darts objects read from json.

Typedefs

using json = nlohmann::json
Bring nlohmann::json into scope.

Functions

template<class T>
void from_json(const json& j, mat<T, 4, 4>& m)
parse a Mat44<T> from json
template<class T, int N>
void from_json(const json& j, vec<T, N>& v)
parse a Vec<N,T> from json
template<class T>
void to_json(json& j, const mat<T, 4, 4>& v)
Serialize a Mat44<T> to json.
template<class T, int N>
void to_json(json& j, const vec<T, N>& v)
Serialize a Vec3<N,T> to json.
void from_json(const json& j, Transform& v)
Parse a Transform from json.
void to_json(json& j, const Transform& t)
Serialize a Transform to json.

Defines

#define DARTS_REGISTER_CLASS_IN_FACTORY(T, cls, name)
Macro for registering an object constructor with a DartsFactory.

Typedef documentation

using json = nlohmann::json

Bring nlohmann::json into scope.

Function documentation

template<class T>
void from_json(const json& j, mat<T, 4, 4>& m)

parse a Mat44<T> from json

template<class T, int N>
void from_json(const json& j, vec<T, N>& v)

parse a Vec<N,T> from json

template<class T>
void to_json(json& j, const mat<T, 4, 4>& v)

Serialize a Mat44<T> to json.

template<class T, int N>
void to_json(json& j, const vec<T, N>& v)

Serialize a Vec3<N,T> to json.

void from_json(const json& j, Transform& v)

Parse a Transform from json.

void to_json(json& j, const Transform& t)

Serialize a Transform to json.

Define documentation

#define DARTS_REGISTER_CLASS_IN_FACTORY(T, cls, name)

Macro for registering an object constructor with a DartsFactory.

Parameters
T The object type to register
cls The name of the class to create instances of
name Associate the keyword type: "name" for creating this type of object from a json object