generator

A generator is a symbol that recursively generates a sequence of vertices. It is a procedural generation tool that is completely separate from the shader paradigm, that is instead based on production rules of formal grammars.

A generator's syntax is similar to a function, but its body is different:

generator <vertex type> <name>(<arg1, arg2, ...>) {
    <local variable declarations (optional)>
    <vertex data sequence>;
}

Keep in mind that although similar in syntax, generators are not functions and no control structures such as if statements or loops can be present. There is no clear control flow from top to bottom, instead, a generator should be thought of as a grammar's production rule, with added arguments to make the generation process unambiguous.

Other generators, as well as functions may be called inside. When a sequence of values (separated by commas) is outputted, it will be automatically divided into vertices based on the size of the vertex type. For example, if MyVertex contains a vec3 coordinate and a vec3 normal, a generator that generates a single triangle may look like this:

generator MyVertex triangle(vec3 a, vec3 b, vec3 c) {
    vec3 normal = normalize(cross(b-a, c-a)); // local variable
    a, normal, // vertex 1
    b, normal, // vertex 2
    c, normal; // vertex 3
}

The main strength of generators however, is recursion. Calls to different generators or to itself can be used to subdivide the geometry into a hierarchy or a fractal structure. Because of the strong focus on recursion, generator is the only construct in Shadron that can be used before declaration.

Specialization

In addition to function-like argument overloading, generators may be specialized using explicit argument values. This is required in order to be able to stop the recursion, since conditional statements are not available.

When a constant value is specified in place of an argument type and name, the generator is a specialization of a previously defined generator. It will be invoked if the argument's value matches this constant value. If more than one argument is specialized, all values must match. If there are multiple matching specializations, the one declared last shall be called. Specialized generators must always be declared after the general one. It is not recommended to specialize floating point arguments, since they may not produce an exact match due to floating point errors.

Consider the following example, which generates a circular arc from line segments:

generator vec2 arc(vec2 coord, vec2 direction, float angle, int steps) {
    vec2 nextCoord = coord+direction;
    coord, nextCoord,
    arc(nextCoord, rotate(direction, angle), angle, steps-1);
}

generator vec2 arc(vec2, vec2, float, 0) { }

The second generator is a specialization of the first one. The first generator will be recursively called until steps reaches zero. Then, the specialization is invoked instead, ending the recursion. If it was omitted, the recursion would continue with steps = -1, -2, etc.

A generator with no arguments can become a root generator. A root generator can be used in the vertex specification of a model or a particle system, and then its generated vertices will serve as the input of the vertex shader.

Example:

For a minimal but comprehensive example, see triangle-generator.shadron in the examples directory.