HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Houdini Geometry Tesselation (GT)

GT Overview

GT is a library for geometry tesselation in Houdini. In the library, there are four primary classes

  • GT_DataArray
    This class is used to represent arrays of data. The arrays have a storage type (i.e. int32, fpreal16, string), a tuple size, optional type qualifiers (i.e. vector, normal, position, color), and of course the data for the array.
  • GT_AttributeMap
    This is a simple class that simply stores a bi-directional map between attribute names and unique, sequential integers.
  • GT_AttributeList
    This class takes a GT_AttributeMap and stores a GT_DataArray for each attribute. Attributes can be indexed either by name or by index.

    Each attribute list may contain multiple segments. In this case, the list will hold multiple data arrays for each attribute.

  • GT_Primitive
    A primitive in GT is the representation of the geometry. An important feature of primitives is that they implement a refine() method which will split the primitive into simpler primitives.

The GT library is used several places in Houdini. Currently, it's used in viewport rendering, Alembic export, and access is also availble through a procedural in Mantra.

Most objects are stored using smart pointers (i.e. GT_AttributeListHandle, GT_PrimitiveHandle and GT_DataArrayHandle). Since objects are shared, the general philosophy is to treat the objects as const and create new data to make modifications.

GT Data Arrays

All data arrays in GT are implementations of an abstract data array class. The virtual interface for a data array consists of:

  • const char *className(): Return a name for the class for debugging.
  • GT_Storage getStorage(): Return the storage type for the data in the array.
  • GT_Size entries(): Return the number of items in the array For example, an array of four normals would have entries()==4 and getTupleSize()==3
  • GT_Size getTupleSize(): Return the number of elements per item in the array. For example, an array of four normals would have entries()==4 and getTupleSize()==3
  • int64 getMemoryUsage(): Return how much memory is used by the array.
  • GT_DataArrayHandle harden(): Ensure data has no external references. For example, a sorted data array may store an original data array with a separate array to implement indirect referencing of the original data:
    class SortedArray : public GA_DataArray {
    GT_DataArrayHandle myUnsoredData;
    GT_DataArrayHandle myIndirectReference;
    virtual uint8 getU8(GA_Offset off, int idx=0) const
    {
    off = myIndirectReference->getI64(off);
    return myUnsortedData->getU8(off, idx);
    }
    ...
    In this case, the sorted array needs to "harden" its data to remove the indirect references. The default behaviour is to create either a GT_DANumeric or a GT_DAIndexedString array, copying over the source data into the resulting array.
  • Data Accessors
    There are various methods (getF32, getF16Array, import(), fillArray). By far, the most efficient methods to get the data out in the form you want is to call the getF32Array() method. In this case, if the underlying array can return you a naked pointer to its data, it will, otherwise, it will extract the data into the storage buffer passed in, and return a naked pointer to that data.

Common GT Data Array Types

  • GT_DANumeric provides a simple class to store arrays of numeric data. Access to the raw data is available using the data() method. There are specializations:
    • GT_Unsigned8Array
    • GT_UInt8Array
    • GT_Int32Array
    • GT_Int64Array
    • GT_Real16Array
    • GT_Real32Array
    • GT_Real64Array
  • GT_DAIndexedString provides a simple class to store an indexed array of shared strings.
  • GT_DAConstantValue A constant value, repeated for all entries in the array. There are two specializations (GT_IntConstant and GT_RealConstant). For example, if defining a polygon mesh of all triangles, you might use GT_IntConstant(num_triangles, 3)
  • GT_DARange Similar to a range object in Python.

GT Attributes

The typical way to construct a GT_AttributeList is to first, create a GT_AttributeMap, then construct a new list, and then populate the list with GT_DataArray handles.

There are methods to add attributes to the list. However, since GT_AttributeList objects can often be shared, the method addAttribute returns a new GT_AttributeList object instead of modifying the existing object. For simple cases, this is can be sufficient, but for optimized code, you may want to create a GT_AttributeMap and populate (which more efficient, but requires more code).

GT Primitives

GT_Primitive is an abstract base class for objects which represent geometry. One key feature of a GT_Primitive object is that there's an interface to refine the primitive into simpler primitives. For example, a GT_PrimPolygonMesh has a refine method which breaks the mesh into individual GT_PrimPolygon objects.

Some of the key methods in the virtual interface include:

  • bool refine(GT_Refine &refiner, const GT_RefineParms *parms): This method is intended to split a complicated primitive into simpler primitives. The simpler primitives can then be refined to even simpler primitives. For example the GT_GEODetail primitive is a single GT primitive which represents all the geometry in a GU_Detail, and its refine() method will generate new GT_Primitive objects representing the primitives in the GU_Detail. As an example of what a refine method might looks like, consider the GT_PrimCollect (which simply stores a list of GT_PrimitiveHandle objects).
    bool
    {
    for (exint i = 0; i < myList.entries(); ++i)
    // Return true if we added primitives to the refiner
    return myList.entries() > 0;
    }
    An example of how you might process geometry using refinement could be something like this:
    class MyRefiner : public GT_Refine
    {
    public:
    void processPolygonMesh(const GT_PrimitiveHandle &prim);
    virtual void addPrimitive(const GT_PrimitiveHandle &prim)
    {
    if (!prim)
    return;
    if (prim->getPrimitiveType() == GT_PRIM_POLYGON_MESH)
    {
    processPolygonMesh(prim);
    return;
    }
    // Refine the primitive to generate polygon meshes.
    prim->refine(this, NULL);
    }
    };
    void
    processPolygons(const GT_PrimitiveHandle &prim)
    {
    MyRefiner ref(...);
    prim->refine(&ref, NULL);
    }
  • const char *className(): Return a descriptive name for the class for debugging.
  • createPrimitiveTypeId(): Each primitive class may have a unique, run-time type id associated with it. There are several pre-defined types (see GT_PrimitiveType.h), but it's possible to assign a unique integer to a custom primitive type.
  • enlargeBounds(UT_BoundingBox boxes[], int nsegments): Create bounding boxes for each motion segment defined on the primitive.
  • enlargeRenderBounds(): Similar to enlargeBounds but takes rendering bounds into account. For example, a point or curve mesh should consider the "width" attribute. At the current time, this method is only called by mantra and may be deprecated in the future.
  • getPointAttributes(), getVertexAttributes(), getUniformAttributes(), getDetailAttributes(): These methods are intended to return attributes which roughly correspond to the attribute classes for Houdini attributes. For example, a polygon mesh would return its shared points for point attributes, it's unique point attributes for vertex attributes, any per-face attributes for uniform etc. It's possible that NULL pointers can be returned if there aren't attributes of a given type.

Some common primitive types are

GT Converting From Houdini Geometry

The GT_GEODetail class is a GT_Primitive which is used to represent an entire GU_Detail. There are several static methods on the class which allow you to construct a GT_Primitive given a GU_Detail.

GT_GEODetail keeps references to the source geometry and builds very light-weight data structures which reference the underlying geometry. So, for example, the attribute data on the GU_Detail isn't copied into buffers, instead, there's an abstract GT_DataArray class which extracts the data from the attributes on demand.

Since GT_GEODetail and the data array classes hold onto references to the underlying geometry, it's imperative that the geometry remain in existence while the GT geometry is live. If this isn't possible, you can call the harden() method on the primitive which will extract all the data from the GU_Detail and create corresponding GT data structures representing the geometry.

The refine() method for GT_GEODetail needs to process all the primitives in the geometry. If you've created a custom GU primitive, or a custom packed primitive, you may want to specialize the GT processing of your primitive.

Each GU primitive type (including packed primitives) has an associated GT_GEOPrimCollect class (as a singleton). When the detail hits the first primitive associated with a collector, it will call the collector's beginCollecting method. For each primitive encountered in the detail, it will call the collectors collect method. The collect method may choose to return a GT_PrimitiveHandle to represent the GEO_Primitive, or it may return an empty GT_PrimitiveHandle. After all primitives have been processed, the endCollecting method is called. An example of this can be seen in the tetprim HDK example.

GT Converting To Houdini Geometry

Given a GT primitive, the GT_Util::makeGEO method can be used to create GU_Detail's from the GT primitive. This method will refine primitives until either the method can generate Houdini geometry, or until the refinement fails. The method is able to handle many different GT primitives (see GT_Util::isSimpleGEO()).

It's possible that different GT primitives have different attribute sets. Because of this, multiple GU_Detail objects may be created to accurately represent the original GT geometry.