CGA essentials

Coordinate Systems

3D coordinate systems are used to describe the position and orientation of objects in space. While CityEngine can handle many different types of (georeferenced) coordinate systems, CGA modeling works with Cartesian coordinate systems only. A coordinate system is defined by its origin and the orientation of the three orthogonal axis. While one global coordinate system would be sufficient to define each vertex of a geometry, it is more practical to use a hierarchy of coordinate systems that are defined relative to each other.

Think of a car model placed on a street segment. It makes sense to define the position of the wheels relative to the car and the car relative to the street. The origin and the orientation of the "car" system are defined with coordinates of the "street" system. This way the two coordinate systems are relative to each other and the coordinates of one system can be translated into coordinates of the other one.

Several coordinate systems are involved when working with shapes. All transformations described above operate in the system defined by the current shape's scope, the scope system. There is also a pivot system associated to each shape, and every shape defines an object coordinate system.

There are multiple different coordinate systems relative to each other in CGA:

World Coordinate System

  • Is defined per scene.
  • The world coordinate system is global, there can be only one per scene.
  • Can be georeferenced, but doesn't have to be.
  • The position of initial shapes is defined in world coordinates.

Object Coordinate System

  • Local coordinate system defined for each initial shape.
  • The origin is placed at the first point of the initial shape's first edge.
  • The axes are oriented such that the x-axis is directed along the first edge, the y-axis is directed along the first face's normal and the z-axis is perpendicular to the former two.

Pivot Coordinate System

  • The pivot system is described in object coordinates.
  • In many cases the pivot origin and orientation is identical with the object coordinates. Still it comes into play e.g. with component splits.

Scope Coordinate System

  • Think of the scope as a bounding box that is placed and oriented in the pivot system.
  • In addition to its characteristics of a coordinate system, the scope has also a size. The size is defined by a width, height and depth.
  • Using transformations you can define the position, orientation and size of the scope.
Note:

CGA shapes

CGA Shapes are the central ingredient of the CGA shape grammar. A Shape consists of a geometry in an oriented bounding box, the scope . The scope is placed relative to the pivot.

The translation and size of the scope, applied in the pivot coordinate system defines the position, size and orientation of the geometry.

Note:

Initial shapes serve as a starting CGA shape for CGA rules, see Shapes

A Shape has the following components:

Shape Symbol

The name of the shape. Used to find the matching rule that is used to generate the successive shapes.

Pivot

The pivot describes the shape's coordinate system and is defined by:

  • a position vector p (pivot.px, pivot.py pivot.pz)
  • an orientation vector o (pivot.ox, pivot.oy and pivot.oz.

The pivot is given in object coordinates, relative to the initial shape's origin; see Coordinate Systems.

Scope

The scope represents the oriented bounding box for the shape in space relative to the pivot and is defined by three vectors:

  • Translation vector t (scope.tx, scope.ty and scope.tz)
  • Rotation vector r (scope.rx, scope.ry and scope.rz)
  • Size vector s (scope.sx, scope.sy and scope.sz)

Geometry

The geometry contains the information about the corners edges and faces that make up the "form" of the shape. The geometry can be any sort of polygonal mesh. In addition, information about color, material and textures (shader attributes) are stored in the geometry as well.

Parameters

Each shape can have an associated parameter list. The ordered parameter list is implicitly defined in the rule which creates the shape. Three parameter types are supported:

  • Boolean
  • Numeric (internally represented with double-precision float)
  • String

Rule application

The basic idea of a rule is to replace a shape with a certain shape symbol with a number of new shapes. Formally:

PredecessorShape --> Successor

Here is a simple example:

A --> B

On application on a specific shape with symbol A, the rule above creates a copy of the shape and sets its shape symbol to B. The A shape is now considered done and not processed anymore. If there is no rule matching symbol B the generation process is finished. The resulting structure is called the shape tree and looks like this:

In the shape tree above, A is the root shape and B is a leaf shape. Leaves are very important because the sum of all leaves represents the generated model. Inner nodes are not visible in the final model.

In this simple example, we assume shape A's geometry, scope and pivot are set up such that the shape represents a unit cube in the origin; because B is a copy of A, B looks exactly the same (see the picture above).

A rule can have more complex successors, e.g., the right side of the rule can consist of multiple shape symbols and shape operations:

A --> B t(3, 0, 0) C

This successor is now executed from the left to the right. Again, B is an identical copy of A. Then the current shape is translated by 3 units in x-direction (i.e., the scope.t is manipulated) and a new shape C is created. The shape tree now has two leaves:

The two leaves B and C make up the final 3D model:

If we add this rule for C:

C --> D s(2, 0.5, 1.75) E

The generation process will add two children, D and E to shape C. Shape D is an exact copy of shape C, but shape E will have a different sized scope (because of the s() shape operation). The shape tree and the associated model look like this:

Note:

Now, the leaves (B, D, E) are not on the same level (i.e. have different distances to the root shape) but they are all part of the model.

Another shape operation is the insert operation i():

E --> i("cylinder.obj") F

After starting the generation again, shape E is not a leaf anymore but now has a child shape F.

The geometry of shape F is not a cube anymore but was replaced with the mesh read from file "cylinder.obj".

Note:

The size (i.e. the scope.s vector) of shape F is still the same as the one of shape E.

Terminal Shapes

In the E rule above, F is a so-called terminal shape: because no rule F is defined, the generation is stopped at this point. However, the CGA editor will issue a "Undefined Rule" warning. This can be suppressed by adding a period after F, thus explicitly marking F as a terminal shape:

E --> i("cylinder.obj") F.

Anonymous Leaf Shapes

For convenience, rules like the E rule above can be truncated:

E --> i("cylinder.obj")

In this case (i.e. E has no children), the rule interpreter silently inserts an anonymous leaf with the same name as the rule itself. The shape tree after applying the E rule above looks like this: