# Geometry Introduction

## Geometry Class Libraries

The main geometry libraries in Houdini are:

Geometry Class Hierarchy

There are additional geometry libraries which may be of use for more specialized uses:

## Geometry Structures

The main classes for Houdini geometry are:

Geometry Structure

This diagram shows the geometry structure for a simple mesh of triangle primitives.

The Detail (GU_Detail) container contains a list of points (GEO_Point) and a list of primitives (GEO_Primitive).

Each primitive has 3 vertices (GEO_Vertex) which refers to one of the shared points (GEO_Point). In this example point 0 and point 3 are shared between the two polygons. All attribute data associated with point 0 and point 3 will be shared between the two polygons. Any attribute on the vertex objects will not be shared.

## Introduction To Geometry Attributes

An geometry attribute (GB_Attribute )is defined by:
• name
The name of the attribute (i.e. `P` or `N` or `temperature`)
• type
The type of the attribute (GB_AttribType). For example GB_ATTRIB_FLOAT or GB_ATTRIB_INT.
• size
The size of the attribute data

The list of attributes are maintained by the GU_Detail. Because the attributes are maintained at the global level, attributes are uniform across element types. That is, if one GEO_Point object has the `Cd` attribute, then all GEO_Point's in the detail will have the `Cd` attribute.

There are separate lists maintained for each geometric element: GEO_Point, GEO_Vertex, GEO_Primitive and even GU_Detail. Each element type has it's own set of attributes. This means that it's possible to have a primitive and point attribute of the same name, type and size. For example, it's possible to have the `float Cd[3]` attribute on both primitive and the point objects. It's up to the caller to determine the precedence, though the informal standard is to prefer finer grained attributes over coarser grained attributes (i.e. GEO_Vertex before GEO_Point before GEO_Primitive before GU_Detail).

### Example Code

• SOP/SOP_DetailAttribC
Create (or modify) a floating point detail attribute
• SOP/SOP_CopRasterC
Add a color attribute to points and assign the value from an image.
• SOP/SOP_SParticleC
Use the `v` and `life` attributes to control birth/death of a simple particle system
• SOP/SOP_TimeCompareC
Compares a point attribute on inputs at two different times, storing the delta in a new attribute
• standalone/geo2voxelC
An example which reads and writes string attributes (GB_ATTRIB_INDEX) for name attributes.

### Attribute Types

The enum GB_AttribType lists the different types of attribute data. At the current time, these types are:
• GB_ATTRIB_FLOAT
Floating point data. The attribute stores single precision (32-bit) floating point data.
• GB_ATTRIB_VECTOR
This attribute is stored as 3 single precision floats. The difference between this and a GB_ATTRIB_FLOAT of 3 floats is that a GB_ATTRIB_VECTOR attribute will be transformed as a normal when the geometry has a transform applied.
• GB_ATTRIB_INT
Integer data. The attribute stores 32-bit integer data.
Note:
When storing integer data, it is usually unclear how interpolation of the data should be done. So for example, with an integer point attribute, it isn't clear how the attribute would be interpolated over a NURBS surface.
• GB_ATTRIB_MIXED
Blind data. Attributes of this type are not saved/loaded to disk, and will not typically be visible to users. It's possible to store a "struct" of information in a mixed attribute.
• GB_ATTRIB_STRING
Do not use this. This attribute is a fixed 32 byte character string. It has been deprecated in favour of GB_ATTRIB_INDEX.
• GB_ATTRIB_INDEX
An integer value is stored on each geometric element. The index refers to an index in a table of strings. More information follows in the next section.

### String Attributes

Ignore the GB_ATTRIB_STRING enum.

The preferred method for storing string attribute data is to use GB_ATTRIB_INDEX. With an index attribute, each element stores an integer value which is used to look up into a table of strings stored on the attribute.

• `int GB_Attribute::addIndex(const char *str)`
Look up or create a new entry in the string table.
• `void GB_Attribute::renameIndex(int idx, const char *str)`
Change the string associated with the given `index`
• `int GB_Attribute::destroyIndex(const char *str)`
Delete the string from the table.
Warning:
This will change the index values for some strings without modifying the attribute data. Please use with caution.
• `void GB_Attribute::clearIndex()`
Delete all strings from the table.
Warning:
This does not change any attribute data. Please use with caution.
• `void GB_Attribute::removeRedundantIndex()`
Remove duplicate entries in the string table.
• `int GB_Attribute::getIndexSize()`
Returns the number of entries in the string table. This allows you to iterate over all the strings without having to evaluate the attribute value on each element.
Note:
The string table may hold strings which are no longer referenced by any elements.
• `int GB_Attribute::getIndex()`
Given a string, look up the index (returns -1 if the string doesn't exist)
• `const char * GB_Attribute::getIndex()`
Lookup the string associated with a given index. This may return `NULL` if no string is associated with a given index.

### SOP Local Variables

Users may note that some attributes magically create local variables in SOPs. This is not so magical.

GEO_Detail::addVariableName() creates a mapping between an attribute and a variable name. This mapping is kept as a detail string attribute. You can also call GEO_Detail::removeVariableName() to remove a mapping, or GEO_Detail::getVariableNameMap() to retrieve the list of all mappings in place. An example of this can be found in SOP/SOP_BrushHairLenC.

You can use GEO_Detail::getVariableNameMap() and parse the results to perform local variable mapping, or, you can let the methods on SOP_Node do the work for you. SOP/SOP_FlattenC makes use of the SOP_Node::setVariableOrder(), SOP_Node::setCurGdh() and SOP_Node::setupLocalVars(), SOP_Node::resetLocalVarRefs() methods to automatically bind attributes to their local variables.

### Attribute Evaluation

There are various methods in the geometry libraries which perform evaluation of attributes. Many of these have three different signatures for evaluation. For example, `GEO_Primitive::evaluatePoint()` has three different signatures each which stores the result in a different fashion:
• `evaluatePoint(GEO_Vertex &result, GEO_AttributeHandleList &h, ...)`
Using GEO_AttributeHandleList is the preferred method for evaluating attributes. This is discussed further after this list.
• `evaluatePoint(UT_Vector4 &result, ...)`
A method which evaluates only the `P` attribute.
• `evaluatePointWAttrib(UT_Vector4 &result, GB_AttributeData &adata, ...)`
A method which evaluates the `P` attribute in the `result` parameter, and all attributes in the `adata` data. The `adata` parameter must be allocated and deleted by the caller and must have the correct amount of storage allocated
See also:
GB_AttributeDict::getAlloc()
The preferred method for evaluation is to use GEO_AttributeHandleList. This allows evaluation of a selection of attributes, doesn't require allocation of storage, and most importantly, handles evaluating both point and vertex attributes at once.

The difficulty with using the attribute handle list is that the results need to be stored in a GEO_Vertex. However, it is easy to create temporary GEO_Vertex objects using the GEO_WorkVertexBuffer class.

Here's an example of how you might evaluate interior points of polygon

```    void
samplePoint(GU_Detail &gdp, int prim_number)
{
// Create a temporary vertex buffer.
GEO_WorkVertexBuffer     vbuffer(&gdp);
GEO_Vertex              *tmp;

// Create an attribute handle list and bind all the floating
// point attributes
GEO_AttributeHandleList hlist;

hlist.bindDetail(&gdp);
// Add vertex attributes first (higher priority)
hlist.appendFloatAttributes(GEO_VERTEX_DICT);
// Add point attributes next
hlist.appendFloatAttributes(GEO_POINT_DICT);

// Get vertex out of the work-vertex buffer.  By passing in a
// null pointer for the point, a temporary point will be
// created (to hold the point attributes).
tmp = vbuffer.appendVertex(NULL);

// Evaluate the point
gdp.primitives()(prim_number)->evaluateInteriorPoint(*tmp, hlist, .5, .5);
for (int i = 0; i < hlist.entries(); i++)
processAttribute(tmp, hlist[i]);

// The temporary vertex will be released when the vertex
// buffer is destructed.
}
```

### Attribute Caveats

• At the current time, attributes are keyed in the lists by the tuple of `(name, type, size)`. This means that it's possible to have two attributes that have the same name but differ in `type` or `size`. This is non-intuitive and can lead to confusion.
• When referring to the size of attributes, the geometry libraries usually expect the size in bytes, not tuple size.
```        GB_AttributeRef attrib;
float           one[3] = {1,1,1};
// Add "float Cd[3]" attribute
attrib = GU_Detail::addPointAttrib("Cd", sizeof(float)*3,
GB_ATTRIB_FLOAT, one);
```
• At the current time, the same attribute cannot exist on GEO_Point and GEO_Vertex elements simultaneously. For example, if there is a point attribute `float Cd[3]` and you try to add a vertex attribute `float Cd[3]`, then, the point attribute will be converted/promoted to a vertex attribute
• The GB_ATTRIB_VECTOR GB_AttribType is a misnomer. This type indicates that the attribute should be transformed as a normal.

## Geometry Primitives

There are several sub-classes of GEO_Primitive. Each primitive has one or more GEO_Vertex objects.

### Face Primitives

Face primitives store their vertices in a 1D list. If the face is closed, the last vertex has is periodic and will connect the last vertex in the list to the first vertex.

There are three main sub-classes of the GEO_Face base class. GEO_PrimPoly is a sub-class which uses an implicit linear basis to represent polygons. GEO_Curve gets further refined to GEO_PrimNURBCurve (NURBS curve) and GEO_PrimRBezCurve (rational Bezier curve).

### Patch Primitives

Patch primitives store their vertices in a 2D matrix. There are two closed flags indicating whether the primitive is closed in the `u` or `v` directions. For example, a grid primitive would be open in both `u` and `v` while a tube primitive would be closed in one of the directions and a torus would be closed in both parametric directions.

There are three main sub-classes of the GEO_Hull base class (mirroring Face Primitives). GEO_PrimMesh represents a linear mesh, while GEO_PrimNURBSurf (NURBS surface) and GEO_PrimRBezSurf (rational Bezier surface) can be used to represent higher order surfaces.

Parametric coordinates are well defined for these primitives.

### Quadric Primitives

Quadric primitives have a single GEO_Vertex representing the center of the primitive. All primitives also store a 3x3 rotation/scale matrix (the center of the primitive is determined by the GEO_Vertex).

The sub-classes are

The tube primitive has two additional intrinsic parameters:

• taper
The taper is used to change the radius over the height of the tube. The radius is determined by:
```        radius = 1 + (taper - 1)*v
```
Where v is the parametric `v` coordinate. So, with a taper of 1, the tube is a cylinder. With a taper of 1, the tube becomes a cone (with the apex at `v == 1 `).

Of course, this is the radius prior to transformation by the rotation/scale matrix.

See also:
GEO_PrimTube::getTaper(), GEO_PrimTube::setTaper(), Matrix Classes
• endcaps
This flag specifies that the tube should have implicit end-caps. With end-caps, the parametric coordinates of the tube are split into thirds in `v`. The first third for the bottom end-cap, the last third for the top end cap.
See also:
GEO_PrimTube::endcaps(), GEO_PrimTube::setEndCaps()

### Volume Primitives

The volume primitive is like a quadric primitive in that it has a single GEO_Vertex and a rotation/scale matrix. The volume primitive also has a scalar voxel field which stores values over the volume.

The volume before the rotation/scale matrix occupies the bounding box (-1, -1, -1) to (1, 1, 1).

More detail on volumes can be found in Volumes in Houdini.

### Particle Primitives

The GEO_PrimPart class stores a linked list of GEO_ParticleVertex vertices. This is primarily so that killing particles is a less expensive operation.

Because vertices are stored in a linked list, traversing vertices can be extremely expensive. Consider the loop:

```    int         i, nvtx;
nvtx = prim->getVertexCount();
for (i = 0; i < nvtx; i++)
vtx = prim->getVertex(i);
```
Since the vertices are stored in a linked list, it is `O(N2)` since to find the `Nth` vertex requires traversal of the entire list. The particle primitive keeps a one level cache of the last vertex queried and uses this to optimize. But, random queries into primitive vertices should be avoided if possible.

Each GU_Detail may have multiple particle primitives (See: Creating Custom POPs)

Each particle primitive also contains a GEO_PartRender object that specifies how the particles from that primitive should be rendered.

For example, to build a particle system with 4 particles rendered as motion blurred rounded tubes:

```    GU_Detail            gdp;
GU_PrimParticle     *partsys;
GEO_ParticleVertex  *part;
float                one = 1;

if (partsys = GU_PrimParticle::build(&gdp, 4))
{
part = partsys->iterateInit();
do
{
// Initially all particles spring from (3,1,1):
part->getPos().assign(3, 1, 1);
}
while (part = partsys->iterateFastNext(part));

partsys->getRenderAttribs().setType(GEO_PARTICLE_TUBES);
partsys->getRenderAttribs().setMotionBlur(1)
partsys->getRenderAttribs().setBlurTime(0.03);
partsys->getRenderAttribs().setSize(0.05);
}
```

### Metaball Primitives

The Houdini geometry format supports quadric (GEO_PrimMetaBall) and superquadric (GEO_PrimMetaSQuad) metaballs. Meta-primitives are subclassed from GEO_PrimMeta (as well as GEO_Primitive). Like Quadric Primitives, metaballs have a single vertex and a transform matrix. However, metaballs also have two intrinsic parameters
• kernel
The kernel is a function of the metaball radius. The kernel is one at the center and zero at the outside radius of the metaball. The shape of the interpolation curve is different for each kernel type. The factory metball kernel functions are:

In all metaball functions, `r` is the radius squared:

```        fpreal wyvill(fpreal r) { return -4/9*r^3 + 17/9*r^2 - 22/9*r + 1 }
fpreal elendt(fpreal r) { return -4/3*r^2 + 34/9*r + 1 }
fpreal blinn(fpreal r) {
const fpreal        blinn_radius = 9;
const fpreal        blinn_min = SYSexp(-blinn_radius);
const fpreal        blinn_scale = 1/(1 - blinn_min);
return (SYSexp(-blinn_radius*r) - blinn_min)*blinn_scale;
}
fpreal links(fpreal r) {
if (r < 1/9)
return 1 - 3*r;

fpreal      r2 = SYSsqrt(r);
return 3/2 * (1-r2)*(1-r2);
}
fpreal hart(fpreal r) {
// Provided by John Hart
fpreal      r2 = SYSsqrt(r);
return -6*r^7 + 15*r^5 - 10*r^3 + 1;
}
fpreal prman(fpreal r) {
return -r^3 + 3*r^2 - 3*r + 1;
}
```

See also:
GEO_MetaPrim::setMetaKernel(), GEO_MetaPrim::getMetaKernel(), Custom Metaball Kernels
• weight
The weight of the metaball is a scale on the kernel function's density. This scale may be negative (a metaball which takes away from the surface).
See also:
GEO_MetaPrim::setWeight(), GEO_MetaPrim::getWeight()

Superquadrics (http://en.wikipedia.org/wiki/Superquadrics) have two additional intrinsic parameters to control the shape of the superquadric. The XY and Z exponents are used to change the shape of the superquadric.

See also:
GEO_PrimMetaSQuad::setXYexp(), GEO_PrimMetaSQuad::setZexp()

### Metaball Expressions

By default, all metaballs in the GU_Detail will blend together by adding their density fields. However, it's possible to make an expression which defines how the metaprimitives are supposed to blend.

The expression uses a simply grammar which is defined http://www.sidefx.com/docs/current/nodes/sop/metaball, but in brief, has 2 primitive types and 3 functions defined:

• integers represent primitive numbers of metaball primitives
• strings refer to primitive groups (GB_PrimitiveGroup)
• The `min()` function returns the minimum density of the arguments
• The `max()` function returns the maximum density of the arguments
• The `sum()` function returns the sum of the arguments

For example: `sum(max("group1", "group2"), max("group3", "group4"), 0, 1)`

Internally, the metaball expression is represented using the TS_MetaExpression class. This is an example of how to evaluate the expression:

```    void
dumpExpression(const TS_MetaExpression *expr)
{
const char              *function = NULL;
const TS_ExpressionList *kids = expr->getAllKids();
const TS_MetaPrimitive  *mprim;
const GEO_Primitive     *gprim;
int                      i;

if (expr->isSum())
printf("sum(");
else if (expr->isMin())
printf("min(");
else if (expr->isMax())
printf("max(");

for (i = 0; i < kids.entries(); i++)
{
if (i > 0)
printf(",");

// Get the Nth child of the expression
mprim = kids->operator()(i)->isPrimitive();
if (mprim)
{
// Each TS_Expression has a back-pointer to the GEO_Primitive
gprim = (const GEO_Primitive *)mprim->getGeoPrimPtr();
// Print the number of the primitive
printf("%d", gprim->getNum());
}
else
{
// Recuse into a new expression
dumpExpression(kids->operator()(i));
}
}
printf(")");
}
```

The expression for a GU_Detail is stored on a detail attribute `string metaExpression`. You can access this directly:

```        TS_MetaExpression       *expr;

expr = GEO_MetaExpression::getExpression(gdp, NULL, NULL, NULL);
expr->ref();    // Reference count the expression
dumpExpression(expr);
expr->unref();
```

Alternatively, you can use the GEO_MetaExpression class. With a GEO_MetaExpression, you can easily evaluate attributes for the blended surface:

```        GEO_MetaExpression      expr;

expr.preCompute(gdp);
dumpExpression(expr.getTSExpression());
expr.computeAttributes(P, result, handles);
```

### Adding a Primitive

For simple primitives such as polygons, adding a primitive to the detail is usually accomplished by calling a GEO_Detail::appendPrimitive() function. Note that it can either copy a primitive from another primitive, or add an empty primitive of the specified type, which then has to be filled in with data. An example below illustrates an addition of a simple polygon to an existing detail:

```void addPolygon(GU_Detail* gdp)
{
int point_idx;
const int num_points = 3;
UT_Vector3 positions[num_points];
GEO_PrimPoly *prim_poly_ptr;
GEO_Point* added_points[num_points];

// Set positions for the points of the polygon
positions[0].assign(0, 0, 0);
positions[1].assign(10, 0, 0);
positions[2].assign(0, 10, 0);

// Add new points to the gdp. This is optional - you can also
// reuse existing points and add them to the polygon instead.
for(point_idx = 0; point_idx < num_points; point_idx++)
{
// Append a new point
added_points[point_idx] = gdp->appendPoint();
// Set its coordinates
added_points[point_idx]->getPos().assign(positions[point_idx][0],positions[point_idx][1],
positions[point_idx][2], 1.0);
}

// Append a new polygon primitive. Primitive types are defined in GEO_PrimType.h
prim_poly_ptr = dynamic_cast<GEO_PrimPoly *>(gdp->appendPrimitive(GEOPRIMPOLY));
prim_poly_ptr->setSize(0);

// Append vertices that form this primitive
for(point_idx = 0; point_idx < num_points; point_idx++)
prim_poly_ptr->appendVertex(added_points[point_idx]);

// Close the primitive to make it a polygon instead of a polyline.
prim_poly_ptr->close();
}
```

For more complex primtive types, it is often more convenient to use a number of helper builder functions provided in the same GU class which corresponds to the primitive type you would like to build, such as GU_PrimPoly::build(), GU_PrimMesh::build() or GU_PrimSphere::build().

For example, to append a NURBS primitive you can use GU_PrimNURBSurf::build() function which builds the primitive for you, adhering to the specified parameters, and returns a pointer to the new NURB surface. You still need to fill in the points and the knot vector, however:

```    GU_PrimNURBSurf *nurb = GU_PrimNURBSurf::build(gdp, num_rows, num_columns,
u_order, v_order, do_wrap_u, do_wrap_v,
do_interp_u, do_interp_v, GEO_PATCH_QUADS, do_append_points);

// The knot vectors can be accessed as follows:
GB_Basis* curr_basis = nurb->getUBasis();
int knot_vector_len = curr_basis->getLength();
float* knot_vector = curr_basis->getData();

// For the points, you can call the standard method on a detail. Please note
// that the points may not necessarily belong to the NURB you have just
// created if the gdp contains other primitives.
UT_Vector4 temp_vec;
nurb->getParent()->points()[point_index]->setPos(temp_vec);
```

### Groups

Houdini's geometry also supports groups - a named subset of items within the gdp, each marked as belonging to a group. There are several group types, including point, primitive, vertex and edge groups, all of which derive from the base group type GB_BaseGroup.

Most of the geometry manipulation functions in GU_Detail and GEO_Detail have an optional group parameter, which, if not null, will force the function to perform its operations only on items belonging to a specific group instead of working with all the items in a detail. Most of the group operations, such as creating, deleting, and modifying them, can be found in GB/GB_Detail.h.

You can also remove any groups that have become unused by calling GB_Detail::removeUnusedGroups().

An example which shows how to manipulate groups can be found below. Although it uses point groups, operating on primitive groups follows the same principles:

```void groupsExample(GU_Detail* gdp)
{
GB_PointGroup       *point_group;
GB_PointGroup       *copied_group;
GB_PointGroup       *found_group;
const char* const copied_group_name = "__copied_group__";

// Create a new point group.
point_group = gdp->newPointGroup("__test_group__", false);

// Get the first primitive in the detail (this assumes it exists).
GEO_Primitive* first_prim = gdp->primitives()[0];

// Add the points of the first primitive to the group.
first_prim->addPointRefToGroup(*point_group);

// A more conventional way to add items to groups is by
// simply calling the GB_Group::add() function:
point_group->add(gdp->points()[0]);

// Make a copy of the group and give it a new name
copied_group = gdp->copyGroup(*point_group, copied_group_name);

// Try to find the group we have just copied:
found_group = gdp->findPointGroup(copied_group_name);
UT_ASSERT(found_group);

// You can also clear the group as following:
found_group->clearEntries();

// Destroy the group. It can be destroyed either by passing in a pointer,
// or by passing in a group name.
gdp->destroyPointGroup(point_group);

// Destroy the copied group by name.
gdp->destroyPointGroup(copied_group_name);

}
```

## Edges

Edges are not explicitly represented as elements in GU_Detail and thus do not have peristent attribute data associated with them. The GB_Edge class is used by many functions to describe a directed edge. An instance of this class can be used to represent a directed segment between two points or two vertices of a primitive. For example:

```GEO_Point *pt0, *pt1;
GEO_Primitive *prim;
int i;

// ...

// a directed edge from pt0 to pt1
GB_Edge edge1(pt0, pt1);
// a directed edge along a primitive from vertex i to vertex i+1
GB_Edge edge2(prim->getVertex(i).getPt(), prim->getVertex(i + 1).getPt(), prim);
```

Note when using edge groups, instances of the GB_EdgeGroup class describe a group of directed edges.

## Copying Elements Between Details

GU_Detail's base class provides several methods for copying points and primitives between details:
• The GEO_Detail::copy() method copies all points and primitives into a detail. It will first remove all existing points and primitives from the destination detail before copying if GB_COPY_ONCE or GB_COPY_START is specified for the parameter, `method`. Multiple details can be merged by specifying GB_COPY_ADD for `method` except for the first and last detail which should use GB_COPY_START and GB_COPY_END respectively.
• The GEO_Detail::merge() method copies a group of primitives into a detail. Only the points referenced by the primitives will be copied. All points and all primitives are copied if no group is specified.
• The GEO_Detail::mergePoints() method copies a group of points into a detail. All points are copied if no group is specified.

These methods will retain all attribute values for the merged elements. Attributes from the source detail will be created in the destination for the classes of copied elements.

Generated on Mon Jan 28 00:27:57 2013 for HDK by  1.5.9