2.6. The Geometry Node

The Geometry node describes the shape of the geometry and any additional data that is assigned to the surface of an object.

Verse has a single primitive when it comes to representing geometry: Catmull/Clark subdivision surfaces. Subdivision allows a simple polygonal mesh to be refined, by letting the algorithm generate more polygons so that the model finally becomes smooth. Rendering clients in Verse are free to apply subdivision to the meshes stored, the intent is that they should in order to approach the ideal created by the artist but they do not have to. Verse's subdivision surfaces support creases with which one can control the degree of subdivision allowed for each vertex and edge.

The data held by a geometry node can be divided into vertex and polygon definitions. Vertex data describe positions in a local 3D space, and polygons are created by referencing the vertices. All polygons have exactly three or four corners (vertex references), there is no support for general N-sided polygons. There is always storage reserved for four corners for a given polygon; if the three first references are valid and the fourth unused, the polygon is a triangle. If also the fourth reference is to a valid vertex, it is a quadrillion. All polygons are two-sided, and the front side references its vertices in a clockwise manner.

2.6.1. Layers

The geometry data is stored in layers, where each layer has a well-defined type. There are layer types for storing data per vertex, per polygon corner, and per polygon face. Table 2-1 is a table that summarizes the available layer formats.

Table 2-1. Geometry Node Layer Types

NameEnum ValueValue TypeValue Size, bitsValues per Index
VN_G_LAYER_VERTEX_XYZ0Floating Point643
VN_G_LAYER_VERTEX_UINT321Unsigned Integer321
VN_G_LAYER_VERTEX_REAL2Floating Point641
VN_G_LAYER_POLYGON_CORNER_UINT32128Unsigned Integer324
VN_G_LAYER_POLYGON_CORNER_REAL129Floating Point644
VN_G_LAYER_POLYGON_FACE_UINT8130Unsigned Integer81
VN_G_LAYER_POLYGON_FACE_UINT32131Unsigned Integer321
VN_G_LAYER_POLYGON_FACE_REAL132Floating Point641

The first layer, named "vertex" but sometimes referred to as the "base layer", is always of type VN_G_LAYER_VERTEX_XYZ. The second layer is always used to define the polygons of the subdivision mesh, and must have type VN_G_LAYER_POLYGON_CORNER_UINT32 and be named "polygon". These two layers are rigidly defined since a host must be able to perform validity checking on them, and thus must be able to uniquely identify which layer is the base vertex layer and which defines the polygons. They are always present in every geometry node.

Any other layers can have whatever type is required by the application. More layers than the two basic ones are often required, for instance to create a texture-mapped surface layers are added to hold the U and V components of the texture coordinates that define the mapping.

The REAL-typed layers can be read and written using either 32 or 64 bits by clients, but always occupy 64 bits in the host memory. By allowing 32-bit subscriptions, clients that do not need the full precision can reduce their bandwidth demands.

The way to think about layers, is as two "stacks", one for vertices and one for polygons. The vertex stack always includes a "base layer" of type VN_G_LAYER_VERTEX_XYZ (with the numerical ID 0 and the name "vertex", while the polygon stack always has a VN_G_LAYER_POLYGON_CORNER_UINT32 layer (with ID 1, and name "polygon"). Other layers, when created, will end up in one of the stacks depending on whether it is a vertex or polygon layer. The size of all layers in a stack is controlled by the size of the base layer of that stack, i.e. one of the aforementioned always-present layers. As vertices and/or polygons are created, the defining data (the XYZ coordinates or the vertex references, respectively) are placed in a slot in the base layer, and all other layers are grown to provide a slot of data space of whatever type the layer has for the new addition. This slot is typically initialized with a default value, to make it well-defined until explicitly set.

2.6.2. Creases

Crease information is used to control the subdivision algorithm, by specifying how sharp a crease a vertex or edge should have it is possible to avoid getting all "smoothed out" objects. There are two possible approached supported for the two creases: either they can all be set to a single constant, or crease information can be stored in a layer and referenced that way. If a layer is used, it must be of type VN_G_LAYER_VERTEX_UINT32 for vertex creases, and VN_G_LAYER_POLYGON_CORNER_UINT32 for edges. These values are given the following interpretation: zero represents a smooth surface (no crease), while MAX_UINT32 should give the maximum amount of crease to the vertex or edge.

Vertex crease information is, as mentioned, stored in a regular VN_G_LAYER_VERTEX_UINT32 layer. This layer has room for one integer per vertex slot, which is exactly what is needed.

For polygon edges, however, perhaps a bit more explanations are in order. The crease data is stored in a layer of type VN_G_LAYER_POLYGON_CORNER_UINT32. Now, such a layer is normally used to associate values with the corners of the polygons, not with their edges. But, since the difference is small here, and since Verse doesn't provide a specialized per-edge layer, the layer is simply re-used. The way to interpret edge creases is can be stated like this: "each corner holds the crease data for the edge that starts in that corner".

2.6.3. Bones

The geometry node can also store local transformations known as bones. Bones are fairly complex in their definition, with each bone having a numerical ID, a parent ID, weight and reference layer pointers, a position, a position curve pointer, a rotation and a rotation curve pointer.

The numerical ID simply identifies each bone in the node, as with layer IDs. Bones are typically numbered from zero and upwards, and the sequence of IDs will typically be kept "tight", with as few gaps of unused IDs as possible.

The weight layer pointer is used to reference a layer that defines the influence of that particular bone on each vertex of the model. Such layers must be of type VN_G_LAYER_VERTEX_UINT32 or VN_G_LAYER_VERTEX_REAL. Integer layers are interpreted as being in the range 0..1 by dividing each value by 4,294,967,295. The value in each slot says how much the bone's transform affects the corresponding vertex.

The reference layer pointer is used to optimize space usage for large skeletons. Since the weights system above needs one layer of weights for each bone, it ends up using a lot of storage space. This is especially true since skeletons are typically local; the number of vertices influenced by each bone is typically small, and does not increase with the number of bones or the number of vertices. Having to "pay" for them anyway adds a lot of overhead, which is something to be avoided. The method used is to add another layer of indirection to the bone system. By specifying the name of a VN_G_LAYER_VERTEX_UINT32 layer in the reference field of a bone, this secondary system is triggered. The layer should contain bone IDs, and the effect is that the weight values for the bone carrying the reference are applied to the bones being referenced, rather than directly to the vertices. In this case, the weight value carried in the bone definition no longer applies to the bone itself, but to any number of bones, as defined by the reference layer.

The parent link defines hierarchies of bones, i.e. skeletons. It can be set to a non-existing bone to indicate that no parent exists, and the bone will then be at root level.

The position and rotation properties of a bone can also be animated by linking them to curve node curves. This is done by specifying the name of the desired curves directly in the bone definition. When the named curve is animated using the object node command, the bone referencing that curve will update.

Regarding the overall bone structure, the following simple "algorithm" might be helpful to illustrate how they work. To draw a bone structure:

Now you have something that looks like a bone structure, with the important (and typical) property that if you rotate any bone, all bones "downstream" of it will rotate, too.

2.6.4. Examples

Because the geometry node is fairly complex, yet used for a very central and important purpose, it is helpful to illustrate how it is used with the help of some simple examples. Such examples almost have to be very simple, since listing in full the geometry for any "realistic" scene would swamp this document and still be unreadable.

Example 2-1. Cube Geometry

One of the simplest 3D objects that is still interesting is the cube. A minimal cube consists of eight vertices and six polygons (if using quadrilaterals, which is always recommended with Verse). To be well-defined in Verse, a cube also needs its crease information set in a way that makes all edges and corners sharp. So, to summarize:

  • Eight vertices

  • Six quadrilaterals

  • Sharp creasing

That's all there is to building a cube. Let's attack these one at a time:

The vertices are stored in the standard "vertex" layer, with ID zero. They can be listed in any order, but using the first eight slots is natural and the most space-efficient way to do it. Here is a table showing one possible solution:

Layer01234567
0 ("vertex")(-1,1,-1)(1,1,-1)(1,1,1)(-1,1,1)(-1,-1,1)(1,-1,1)(1,-1,-1)(-1,-1,-1)

Here, the four first vertices outline the top quadrilateral, in clock-wise order when seen from the outside. The last four do the same for the bottom quadrilateral. Note how each cell ("slot" in the layer) contains three values, this is because the layer has type VN_G_LAYER_XYZ.

Next, we need to define the polygons. This is done in the layer with ID=1, called polygon. Here is how it could look, in tabular form:

Layer012345
1 ("polygon")0,1,2,34,5,6,73,2,5,42,1,6,51,0,7,60,3,4,7

Each slot contains four integer values, since the type of the polygon layer is G_POLYGON_CORNER_UINT32. The first two polygons are the top and bottom surfaces, respectively. They are followed by the front, right, back and left surfaces of the cube (when viewed along the negative Z-axis, with positive Y being upwards). Each quadrilateral is defined in clock-wise order, with the top left vertex first (when applicable).

Note how the vertex and polygon layers have different sizes. Although they are part of the same physical layer ID space inside the node, they really do form two separate "stacks" of layers. All vertex layers will have the same size as the base (ID=0) vertex layer, while all polygon layers will have the same size as the base (ID=1) polygon layer.

To help visualize the above, here is a figure showing a simple cube, with vertices labeled according to the order used above:

Figure 2-1. A Cube

The polygons are not marked in this figure, to reduce the clutter. The cube is viewed as described above, and the XYZ axes use red, green and blue colors, respectively.

The final point we need to take care of is the creasing. To make sure the cube is not interpreted as a smooth shape, we need to explicitly set the creasing to "sharp" for all vertices and edges. Crease information can be stored in vertex and polygon layers, when finer control is needed, but there is also a way to set the default to use for corners and edges, respectively. In this case, we need all the crease values to be the same, so we can use the defaults and set them to 0xffffffff which means "maximum sharpness everywhere".