HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Rendering with the GR_GeoRender Classes

Introduction to GR

The viewport has two types of GR objects that it uses to render geometry - GR_Primitive and GR_GeoRender objects. A GR_Primitive is mainly responsible for directing the render. It handles such tasks as:

  • updating the geometry
  • creating GR_GeoRender or RE_Geometry objects to handle the actual drawing
  • managing display modes
  • displaying decorations

By keeping drawing separate from the higher level GR objects that represent Houdini primitives, these GR_GeoRender objects can be reused by many GR_Primitive types. For example, GR_PolySurfaceGL3 is used for displaying such primitives as polygons, tessellated NURBS surfaces, and primitive spheres. A GR_Primitive can use more than one GR_GeoRender, such as how a NURBS surface uses a GR_PolySurfaceGL3 object to render its surface, and two GR_PolyCurveGL3 objects to render its isoparms and hull.

Each GR_GeoRender object has a GL3, a GL4 or a VK suffix. A GL3 suffix indicates that the object will render with OpenGL 3 compatible hardware, and given Houdini's minimum requirement is GL3.3, this will work in all cases. A GL4 suffix requires OpenGL 4 hardware. A VK suffix is for Vulkan.

These classes have the advantage of understanding all of Houdini's viewport states, and can properly display selections (point, prim, vertex). Using these classes in a render hook can save a substantial amount of work.

The GR_GeoRender classes take specific GT_Primitive types. Some GT types are not directly supported by a GR_GeoRender class, and instead must be refined down to a type that is supported. For example, a GT_PrimSphere is not directly supported, but refining it will produce a GT_PrimPolygonMesh, which can be rendered by a GR_PolySurfaceGL3 object.

GR_GeoRender GL3 Classes

All GL3 classes support instancing. There are also VK versions of these files for Vulkan.

Using GR_GeoRender classes

A GR_Primitive object is generally GL-renderer-agnostic. While it's possible to restrict a hook'ed GR_Primitive to a subset of GL renderers, it's also very easy to support multiple renderers when using GR_GeoRender objects. For example, when creating a polygon surface for rendering in a GR_Primitive::update() method, the GL3 and GL4 objects can be used interchangeably:

void
GR_PrimBlobSurface::update(RE_RenderContext rc,
const GT_PrimitiveHandle &primh,
const GR_UpdateParms &p)
{
if(!myPolyGeo)
{
if(rc.isVulkan())
myPolyGeo = new GR_PolySurfaceVK(myInfo);
else if(rc->hasGL4(0))
myPolyGeo = new GR_PolySurfaceGL4(myInfo, true); // note: doesn't exist, example only!
else
myPolyGeo = new GR_PolySurfaceGL3(myInfo, true);
}
// refine the GT_Primitive in primh to a gt_poly_mesh_prim_handle, or
// create a GT_PrimPolygonMesh manually from the GT/GEO primitive(s)
myPolyGeo->update(rc, gt_poly_mesh_prim_handle, p, getCacheName());
}

Rendering the primitive is fairly easy as well:

void
GR_RenderMode render_mode,
GR_RenderFlags render_flags,
GR_DrawParms draw_parms)
{
if(myPolyGeo)
myPolyGeo->draw(r, render_mode, render_flags, draw_parms);
}

In some instances, the tessellated surface may not be suitable for displaying wire-over-shaded directly, or may support certain rendering modes in a slightly different way. For example, a NURBS surface shouldn't display its tessellated polygons as flat-shaded, nor their outlines as the wire overlay. In this case, it's possible to strip off these behaviours from the render_flags before rendering the GR_GeoObject:

myPolyGeo->draw(r, render_mode, alt_flags, draw_parms);

Decorations can also be set up to work on a GR_GeoRender. Not all decorations need to be supported by a GR_Primitive. In some cases when a GR_Primitive is made up of more than one GR_GeoRender object, some decorations might apply to one, and a different set to the other. This can be specified in update() when setting up the decorations:

// The subset of decorations that this GR_GeoRender object will support
const GR_Decoration hdecs[] = { GR_POINT_MARKER,
GR_NO_DECORATION }; // delimiter
// All GR_GeoRender objects have a decoration renderer object attached to
// them. Calling setupFromDisplayOptions() will add any required attributes
// to the p.needed_attribs list. 'GR_POINT_ATTRIB' specifies that any
// custom point attributes will be displayed on this object.
GR_UpdateParms hullp(p);
myDecorRender->setupFromDisplayOptions(hullp.dopts, hullp.required_dec,
hullp, hdecs,
// Create the hull's GL vertex arrays and connectivty
myHullGeo->update(r, hullPrimHandle, hullp, hullCacheName);
// Build the decorations from the attribute data constructed in update()
RE_Geometry *hgeo = myHullGeo->getGeometry();
if(hgeo)
for(int i=0; hdecs[i] != GR_NO_DECORATION; i++)
updateDecoration(r, hdecs[i], hullp, hullPrimHandle, hgeo);

Decorations are then rendered via the GR_Primitive::renderDecoration() virtual, which basically directs which GR_GeoRender object handles which decorations, and how these are drawn. For most builtin decorations, this is just a matter of calling drawDecoration().

void
GR_PrimBlobSurface::renderDecoration(RE_RenderContext r,
{
if(myHullGeo) // is a GR_GeoRender subclass
{
if(decor == GR_PRIM_HULL)
{
renderDecorWireframe(r, myHullGeo, p.opts->common().crtAuxColor(),
p.opts, p.instances);
}
else if(decor == GR_POINT_MARKER ||
decor == GR_POINT_NUMBER ||
decor == GR_POINT_POSITION ||
{
drawDecoration(r, myHullGeo, decor, p.opts, p.overlay,
p.override_vis, p.instances);
return;
}
else if(decor == GR_VISUALIZER_MARKER)
{
drawVisualizer(r, myHullGeo, p.visualizer, p.opts, p.render_flags,
return;
}
}
// handle any other GR_GeoRender objects...
}

GR_GeoRender objects should be freed when the GR_Primitive is deleted; there is no advantage to stashing them for future use.

Creating GT objects for GR_GeoRender Classes

In some cases you may need to create your own geometry to pass to a GR_GeoRender subclass, instead of using GT primitives generated from Houdini. This section describes what each GR_GeoRender subclass expects in terms of attributes, array sizes and general layout.

Each attribute can be represented by a GA_DataArray, the most useful of which is a GT_DANumeric<> class. This allows you to create templated data arrays for attributes.

const fpreal32 *raw_pos = posdata->data(); // array_len x 3 elements
const fpreal16 *raw_nml = nmldata->data(); // array_len x 3 elements
// fill raw_pos and raw_nml
"N", nmldata);

Attribute lists combine all the attributes of the same class (vertex, shared, uniform, or detail). They must all have the same length, but the data format and tuple size may change.

A GT_Primitive may have all four attribute class types, or a subset of them. A GT_PrimPointMesh only has point and uniform attribute lists, for example. This indicates that it supports varying attributes per point, but also attributes that are constant over the entire point cloud. The more complicated GT_PrimPolygonMesh has detail attributes, which are constant for all polygons; uniform attributes, which are per-polygon values; shared (point) attributes, which are per-point in the mesh; and vertex attributes, which vary the attribute for every vertex of a polygon, even if it shares points with a neighboring polygon.

Some GT_Primitive subclasses also require additional information, such as grouping information for a list of unbounded primitives (curves, polygons).

There are also some special attributes that GT supports:

  • __primitive_id : Specifics an int32 per primitive in the GT_Primitive. This is the GA_Offset of the primitive within the GU_Detail. If this is not specified, it will not participate in primitive selection.
  • __vertex_id : Specifies an int32 per vertex defining the GA_Offset of the vertex within the GU_Detail. If it does not exist, the primitive will not participate in vertex selection. It can also be used to determine point offsets if the __primitive_id is specified.
  • __point_id : Specifies an int32 per point defining the GA_Offset of all shared points within the GU_Detail. This isn't required if __vertex_id and __primitive_id are both present. If none of these are present, the primitive will not participate in point selection.
  • __topology : A unique int64 assigned to geometry when the topology changes. If it remains constant, the topology of the geometry has not changed.

These special attributes need to be placed in the appropriate attribute list, which varies slightly between GT primitives. See the list of GR/GT primitive pairs below for more details.

The GR_GeoRender classes expect that the attributes have the following basic structures:

  • P (point position): vec3 or vec4 (only the first 3 components are used)
  • N (normal): vec3
  • Cd (color): vec3
  • Alpha: float
  • uv (texture coords): vec2, vec3 or vec4 (only the first 2 components are used)
  • MatID (primitive material reference): int, reference to material in the material atlas materials in the GR_DrawParms passed to render().

When passing a GT_Primitive to GR_GeoRender::update() or GR_GeoRender::updateDecorations(), the GT_Primitive pointer must be first assigned to a GT_PrimitiveHandle. This provides reference counting for the primitive. If a raw pointer is passed to update(), the compiler will create a temporary GT_PrimitiveHandle, pass it in, then destroy both the GT_PrimitiveHandle and your GT_Primitive object. If you want to keep the GT_Primitive around for multiple GR_Primitive::update() calls, assign it to a member GT_PrimitiveHandle. When no GT_PrimitiveHandles reference the GT_Primitive anymore, it will be deleted.

GR_PolySurface and GT_PrimPolygonMesh

A GR_PolySurface is built from a GT_PrimPolygonMesh. The polygon mesh does not need to be connected in any regular way; it could be a regular linear mesh or separate, unconnected polygons. GR_PolySurface expects the following Houdini attribute layout (attributes in square brackets are optionally allowed for that class):

  • Vertex (per polygon vertex): __vertex_id [Alpha, Cd, N, uv]
  • Shared (per shared point): P [Alpha, Cd, N, uv]
  • Uniform: int32 __primitive_id [ Alpha, Cd, MatID ]
  • Detail: [ Alpha, Cd, MatID ]

If __vertex_id or __primitive_id are not present, it will not be possible to pick points or vertices (__vertex_id missing), or points or primitives (__primitive_id missing) on the primitive.

Custom attributes may be added as well for decoration purposes, as well as custom GLSL shaders which can bind them using a name match.

The primitive mesh also requires a list of vertex counts and a list of vertex indices. Each entry in the vertex count list defines the number of vertices in polygon i. The vertex indices reference the shared points to form polygons.

For irregular meshes where the vertices per polygon fluctuate, you can pass the data using GT_DANumeric<int32, GT_STORE_INT32> data arrays. If a mesh is regular, containing all triangles or quads, you can use:

counts.init( num_quads, 4);
GT_DataArrayHandle vertex_counts = counts.extractCounts();
// if all points are unique; shared points require more complex indexing than this:
GT_DataArrayHandle vertex_index = counts.extractOffsets();

Finally, you can specify the min and max vertex count for optimization purposes. For example, a tetmesh would be filled with triangles and so does not need convexing; setting min_vertex_count = max_vertex_count = 3 would avoid computing the counts.

The length of all vertex attribute arrays must be the length of the vertex index list. The length of the unform attributes must be the number of polygons in the polygon mesh.

GR_PolyCurve and GT_PrimCurveMesh

Polygon curves are build from a GT_PrimCurveMesh. Similar to a polygon mesh, the curve mesh takes a list of curve counts to specify the segments of each individual curve.

Curves do not have a shared list. All points are unique. They expect:

  • Vertex (per point): P, __vertex_id [Alpha, Cd, uv]
  • Uniform (per curve): __primitive_id [Alpha, Cd, MatID]
  • Detail (for all curves): __primitive_id [Alpha, Cd, MatID]

The __primitive_id can be per curve, or defined for all curves. The __vertex_id specifies the GA_Offset of each vertex in the curves. Both must be present for point selection, and each must be present for their selection type.

The GT_PrimCurveMesh constructor also requires vertex count array. This specifies the number of connected vertices in each curve. Since there are no shared points in a curve mesh, only the vertex count of each curve needs to be specified.

The vertex attribute array length must be the sum of the vertex counts, and the uniform attribute array length must be the number of entries in the vertex count array (ie, equal to the number of curves).

If a GT_Basis of GT_BASIS_BEZIER is specified, the curve mesh must first be refined before passing it to a GR_PolySurface. This is done via:

GT_PrimitiveHandle refineBezier(GT_PrimCurveMesh *bezier_mesh,
const GR_DisplayOption *opts)
{
GT_RefineParms ref_parms;
ref_parms.setLOD( opts->common().LOD() );
return bezier_mesh->refineToLinear(&ref_parms);
}

GR_Point, GR_Sprite and GT_PrimPointMesh

Point clouds are built from GT_PrimPointMesh. This primitive is fairly simple, as it has no connectivity and only per-point or constant attributes.

  • Point (per point): P, __point_id [Alpha, Cd]
  • Detail (constant): [Alpha, Cd, __primitive_id]

The __point_id specifies the GA_Offset point offset. If point selection is not needed it can be omitted. __primitive_id is used for primitive selection, if the point cloud belongs to a primitive and are not just unconnected points.

Sprites (GR_SpriteGL*) also allow additional attributes:

  • Point: pscale, spriterot, spritescale, spriteuv, spriteshop, shop_materialpath

These have no effect if passed to a GR_Point object. The spriteshop is similar to shop_materialpath, both of which should be a GT_DAIndexedString (to build this attribute, see GR_PolySurface and GT_PrimPolygonMesh). These attributes are expected to have the following formats:

  • pscale: float
  • spriterot: float
  • spritescale: vec2
  • spriteuv: vec4 (uv upper left, uv lower right)
  • spriteshop: indexed string referencing SHOP or Material OPs
  • shop_materialpath: indexed string referencing SHOP or Material OPs