Home

Awesome

Generator - A procedural geometry generation library.

Examples

The purpose of this library is to easily generate procedural meshes of geometric primitives such as spheres, boxes, cones, cylinders etc.

Generator is not a graphics library. It will only produce data for any graphics library to use.

Compiling

To compile you will need a C++11 compatible compiler (tested with gcc 4.8) and cmake (tested with cmake 2.8).

Generator needs a vector math library to work. It will use GML. There are no other dependencies.

Compile with:

cmake .
make

If GML is not in the include path, you can give it's path with: -DGML_INCLUDE_DIR command line argument.

cmake . -DGML_INCLUDE_DIR=path/to/gml
make

The library libgenerator will be in the lib directory.

In images directory there is a program called generate that can generate svg test images.

You can generate documentation with Doxygen. Run generate in the images directory before doxygen as the documentation uses images generated by it.

Usage

Make sure that that Generator and the GML math library are in the include path.

To include all primitives use:

#include <generator/generator.hpp>

To include individual primitives include a header file that has the same name as the primitive class.

#include <generator/SphereMesh.hpp>
#include <generator/HelixPath.hpp>
#include <generator/CircleShape.hpp>

If you'd prefer to use GLM, a similar (but more heavyweight) library, build with the #define GENERATOR_USE_GLM.

Everything in the library is under the namespace generator. The examples in this document will omit this namespace.

You will also need to link with libgenerator.

Coordinate systems

Generator uses a right handed coordinate system, just like OpenGL does.

All angles are in radians positive, with the direction being counterclockwise when looking towards the axis.

Vertices in triangles are in counterclockwise order.

All calculations are done double precision.

Concepts

There are two main concepts in the library: "primitives" and "generators".

Primitives produce generators. Generators generate values such as vertex coordinates.

Primitives

There are three types of primitives: "shapes", "paths" and "meshes". All primitive class names end with their type e.g. SphereMesh, HelixPath or CircleShape.

Shapes

Shapes are a set of ShapeVertexes on the 2d xy-plane and a set of Edges connecting them.

class ShapeVertex {
public:
	gml::dvec2 position;
	gml::dvec2 tangent;
	gml::dvec2 normal() const noexcept;
	double texCoord;
};
class Edge {
public:
	gml::uvec2 vertices;
};

All shapes have these methods:

EdgeGenerator edges() const noexcept;
VertexGenerator vertices() const noexcept;

Where EdgeGenerator and VertexGenerator can be a arbitrary types. The shape must outlive the generators it produces. If the shape is mutated the generators are invalidated. Shapes can be mutated only via assignment. The are no setter methods.

Available shapes are:

Paths

Paths are a set of PathVertexes in 3d space and a set of Edges connecting them.

class PathVertex {
public:
	gml::dvec3 position;
	gml::dvec3 tangent;
	gml::dvec3 normal;
	gml::dvec3 binormal() const noexcept;
	double texCoord;
};

All paths have these methods:

EdgeGenerator edges() const noexcept;
VertexGenerator vertices() const noexcept;

EdgeGenerator and VertexGenerator are arbitrary types.

Available paths are:

Meshes

Meshes are a set of MeshVertexes in 3d space and a set of Triangles connecting them.

class MeshVertex {
public:
	gml::dvec3 position;
	gml::dvec3 normal;
	gml::dvec2 texCoord;
};
class Triangle {
public:
	gml::uvec3 vertices;
};

All meshes have these methods:

TriangleGenerator triangles() const noexcept;
VertexGenerator vertices() const noexcept;

Again, TriangleGenerator and VertexGenerator can be any type.

Available meshes are:

Generators

Except for cached values (which should be considered an implementation detail), primitives don't store any data (vertices etc). All data is generated on the fly with generators.

Generators are made by calling edges(), triangles() or vertices() on the primitive. Generators are lightweight objects analogous to iterators. They typically only contain a pointer back to the Primitive and state info for iteration. Any number of generators for the same primitive can exist at the same time.

Generators can have arbitrary types. Use the keyword auto to avoid the need to explicitly specify them.

SphereMesh sphere{};
auto vertices = sphere.vertices();

For cases where auto cannot be used, the helper class templates EdgeGeneratorType, TriangleGeneratorType and VertexGeneratorType are provided in util.hpp.

typename VertexGeneratorType<SphereMesh>::Type vertices = sphere.vertices();

If the generator type is known only at runtime, the type-erasing class template AnyGenerator can be used (in AnyGenerator.hpp).

AnyGenerator<MeshVertex> vertices = sphere.vertices();

If the generator's primitive type is known at compile time, the AnyShape, AnyPath, and AnyMesh types may be used for respectively storing shapes, path and meshes.

All generators have the following methods:

bool done() const noexcept;
Value generate() const;
void next();

Where Value can be Edge, Triangle, ShapeVertex, PathVertex or MeshVertex.

These methods can be used to iterate over all values in the generators.

SphereMesh sphere{};
auto vertices = sphere.vertices();
while (!vertices.done()) {
	MeshVertex vertex = vertices.generate();
	// Do something with vertex
	vertices.next();
}

Once done() returns true, calling next() or generate() will throw std::out_of_bounds. There is no way to rewind the generator.

If the vertex/edge/triangle count needs to be known before iteration use the helper function count from util.hpp.

count(sphere.vertices());

Iterators

It is possible to use an iterator to drive a generator. Use free functions begin and end on the generator to get the iterators. If the generator is deleted the iterators are invalid.

SphereMesh sphere{};
auto vertices = sphere.vertices();
std::for_each(begin(vertices), end(vertices), [] (const MeshVertex& vertex) {
	// do something with vertex
});

for loops will also work.

SphereMesh sphere{};
for (const MeshVertex& vertex : sphere.vertices()) {
	// do something with vertex
}

NOTE: Be aware of a lifetime issue with a temporary primitive and the for loop.

for (const MeshVertex& vertex : SphereMesh{}.vertices()) {
	// Error! Undefined behaviour!
}

Modifiers

Some primitives such as TranslateMesh do not generate data of their own. Instead they modify data generated by another primitives. They are called "modifiers".

Modifiers store the primitive they modify.

TranslateMesh<SphereMesh> translatedSphere{SphereMesh{}, {1.0f, 0.0f, 0.0f}};
for (const auto& vertex : translatedSphere.vertices()) { }

Multiple modifiers can be nested.

Each modifier also has a function to make one. The function form has the same name as the class but starts with a lower case letter. translateMesh instead of TranslateMesh etc. This makes template argument deduction possible.

auto result = translateMesh(
	rotateMesh(
		scaleMesh(TorusMesh{}, {0.5f, 1.0f, 1.0}),
		gml::radians(90.0), Axis::Y
	),
	{1.0f, 0.0f, 0.0f}
);
for (const MeshVertex& vertex : result.vertices()) { }

Available modifiers for shapes are:

Available modifiers for paths are:

Available modifiers for meshes are:

Preview and debug

For preview and debug purposes generator has class SvgWriter that can create SVG images of primitives.

It also has class ObjWriter that can write OBJ files of meshes that can be viewed with most 3d programs such as Blender.