HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
16.0: Major Changes In The HDK

Mantra loading of VRAYprocedural

Mantra now uses the new registerProcedural() function to install procedurals. This is done automatically for all dynamic objects found in the MANTRA_DSO_PATH. The loading of the VRAYprocedural file is still supported, but it's use is discouraged.

VRAY_Procedural Changes

There were significant changes to the VRAY_Procedural API with respect to geometry methods:

  • Many of the deprecated methods have now been removed.
  • It's now possible to construct VRAY_ProceduralGeo objects with existing GU_DetailHandle objects (or multiple GU_DetailHandle objects for deformation motion blur). The GU_DetailHandle objects can be shared between multiple procedurals.
  • Adding motion segments now returns GU_DetailHandle objects rather than VRAY_ProceduralGeo objects.
  • Internally, segment attributes (appendSegmentAttribute) are now stored as detached attributes. This will likely have no impact on existing code, but it does mean that you won't be able to do a search for the attribute on the detail itself. It's still possible to call queryGeometrySegment to find these attributes.

To help with the transition, here are some common code patterns:

// Old Code
VRAY_ProceduralGeo seg0 = createGeometry();
GU_Detail *blur_gdp = blur.get();
// New Code
VRAY_ProceduralGeo seg0 = createGeometry();
GU_Detail *blur_gdp = wlock.getGdp();
// Alternate New Code
GU_Detail *seg0_gdp = new GU_Detail(false);
GU_Detail *blur_gdp = new GU_Detail(false);
handles[0].allocateAndSet(seg0_gdp, true);
handles[1].allocateAndSet(blur_gdp, true);
VRAY_ROProceduralGeo gdo = createGeometry(handles.array(), 2);

Another common pattern is:

// Old Code
VRAY_ProceduralGeo geo = createGeometry();
for (int i = 0; i < nsegments; ++i)
{
GU_Detail *seg;
if (i == 0)
seg = geo.get();
else
seg = geo.appendSegmentGeometry(shutter_time[i]);
computeGeometry(seg, i);
}
// New Code
VRAY_ProceduralGeo geo = createGeometry();
for (int i = 0; i < nsegments; ++i)
{
if (i == 0)
gdh = geo.get();
else
gdh = geo.appendSegmentGeometry(shutter_time[i]);
computeGeometry(gdh.getGdp(), i);
}
// Alternate New Code
for (int i = 0; i < nsegments; ++i)
{
GU_Detail *gdp = new GU_Detail(false);
computeGeometry(gdp, i);
handles.allocateAndSet(gdp, true); // Pass ownership to handle
}
VRAY_ProceduralGeo geo = createGeometry(handles.array(), nsegments);

OBJ_Node myWorldXform and myXform moved from protected to private.

OBJ_Node protected member variables myWorldXform and myXform are now private. Use setWorldXform() and setLocalXform() during cooking to set the matrices. Use getWorldXform() and getLocalXform() during cooking to get the matrices. inverseDirty() has also been depracated and is no longer needed because setWorldMatrix() now sets the myInverseDirty flag.

GA_Primitive Now Manages Vertex Lists

To reduce code duplication and complexity, and increase performance, GA_Primitive now has a GA_OffsetList, myVertexList, indicating the vertices of the primitive, so subclasses no longer need to manage vertex offsets, themselves. This also avoids the need for subclasses to override some functions, since GA_Primitive now has direct access to the vertex list.

Iterating over vertices using GA_Primitive::beginVertex() is now deprecated, because it's slower and more complicated than just using:

for (GA_Size i = 0, n = prim->getVertexCount(); i < n; ++i) {
GA_Offset vtxoff = prim->getVertexOffset(i);
...
}

or alternatively, if you just need the vertex offsets, using the new forEachVertex() function:

prim->forEachVertex([&](GA_Offset vtxoff) {
...
});

or for point offsets, forEachPoint():

prim->forEachPoint([&](GA_Offset ptoff) {
...
});

Merge Constructors No Longer Supported

Primitive type constructors are no longer allowed to modify the detail, so they cannot, for example, add a vertex to the detail. Adding a vertex can be done after creating the primitive. This allows all primitive types to be constructed in parallel and removes the need for merge constructors. The signature for the primitive type constructor has also changed, so that it fills in an array of pointers. See the geoNewPrimTetraBlock function in the GEO_PrimTetra.C example file.

To create primitives of some type with vertices, you can use

GA_Offset start_vtxoff;
GA_Offset start_primoff = detail.appendPrimitivesAndVertices(
type, nprimitives, nvertices_each, start_vtxoff);

For example, if you were previously creating sphere primitives with appendPrimitive(GA_PRIMSPHERE) or appendPrimitiveBlock(GA_PRIMSPHERE, nspheres), you should now use:

GA_Offset start_vtxoff;
GA_Offset start_primoff = detail.appendPrimitivesAndVertices(
GA_PRIMSPHERE, nspheres, 1, start_vtxoff);

You will still need to wire the vertices to points, for example:

GA_Offset start_ptoff = detail.appendPointBlock(nspheres);
for (GA_Size i = 0; i < nspheres; ++i) {
detail.getTopology().wireVertexPoint(start_vtxoff+i, start_ptoff+i);
}

GA_ElementGroup Merged With GA_ATIGroupBool

GA_ElementGroup and GA_ATIGroupBool are now the same class. Most code should work exactly the same as before. If you had any forward declarations of GA_ATIGroupBool, you'll need to change them to GA_ElementGroup, since the remaining real class name is GA_ElementGroup, and GA_ATIGroupBool is just a typedef.

This change avoids the complication of having two separate, but linked, classes for each group. They also now appear in only the group table, saving an extra hash table entry, and GA_AttributeDict defers to the GA_GroupTable when looking up attributes in GA_SCOPE_GROUP.

New Data Structure for Looking Up Attributes

GA_AttributeDict now has separate hashmaps for attributes in public and private scope, and defers to the corresponding GA_GroupTable for group scope. These hashmaps are also using a much more memory-efficient and memory-coherent data structure, saving memory on small details and time setting up and looking up attributes by name. It also means that attributes no longer needed a separate name and "full name", where the full name previously included an indicator of the scope, so the full name has been removed, saving a little more space per attribute, and a little more time when setting up a new attribute.

New Data Structure for Numeric and String Attributes

The main data structure for numeric attributes and the data structure for string indices in string attributes is now GA_PageArray, instead of GA_DataArray. This change results in much better performance in many cases for GA_ROHandleT and GA_RWHandleT, and reduces memory use in a few cases.

It also allows for more direct access to the data and control over constant pages and shared pages, for use cases that can benefit from that access.

GA_EdgeGroup changes

GA_EdgeGroup has been largely re-written to use more modern containers for its data. At the same time it has been made const-correct, and the semantics of removing entries based on the iterator has changed, both of which may affect some existing code.

For example, this type of iteration now requires the use of const:

// Before
`GA_EdgeGroup edgeGroup;
GA_Edge *edge;
GA_FOR_ALL_GROUP_EDGES(edgeGroup, edge)
{
// Do things with 'edge'.
}
// After (note the const):
GA_EdgeGroup edgeGroup;
const GA_Edge *edge;
GA_FOR_ALL_GROUP_EDGES(edgeGroup, edge)
{
// Do things with 'edge'.
}

Range-based for loops can also be used:

GA_EdgeGroup edgeGroup;
for (const GA_Edge &edge : edgeGroup)
{
// Do things with 'edge'.
}

If looping over the group and selectively removing elements from it, the same care is required as using std::list. Example:

GA_EdgeGroup edgeGroup;
for (auto it = edgeGroup.begin(); it != edgeGroup.end(); /* no ++it */)
{
if (criteria_for_removal(*it))
// GA_EdgeGroup::remove(iterator) returns an iterator to the element
// after the one we're removing. This is semantically equivalent to
// incrementing the iterator.
it = edgeGroup.remove(it);
else
// Only advance the iterator directly if we didn't remove anything.
// We can't advance the iterator pointing to the element we removed
// because it is invalid after the remove operation.
++it;
}

Any code that relied on modifying the GA_Edge returned from the iterator, or GA_EdgeGroup::find, was already relying on an undefined behavior and will now result in a compile-time error.

OP_Dot Class Added

Renamed OP_InputIndrect to OP_SubnetIndirectInput. Added OP_Dot to represent network dots. Both OP_Dot and OP_SubnetIndirectInput are subclasses of the new OP_IndirectInput class.

Before network dots, OP_Node and OP_SubnetIndirectInput could be connected to a node's input. But only nodes had exlicit inputs, and so when looking at the outputs of a node or subnet input, you could be assured that you would only get back an OP_Node.

Now, an output can lead to either an OP_Dot or an OP_Node. This required changes to the methods for getting output information from nodes or subnet inputs (and network dots as well). So OP_Node::nOutputs() and OP_Node::getOutput() have been removed. In their place are OP_Node::nOutputItems() and OP_Node::getOutputItem(). These functions refer to and return the OP_NetworkBoxItem objects that are directly connected to this node. The OP_Node::getOutputNodes() function traverses through dots (and optionally into subnetworks) to find any nodes connected directly or indirectly to the output of the node (but not recursively through other nodes). In addition, the OP_OutputIterator and OP_OutputReversedIterator classes wrap a call to OP_Node::getOutputNodes() inside an OP_NodeList subclass that makes iterating over nodes connected to an output of another node as simple as this:

for (auto &&outnode : OP_OutputIterator(innode))
std::cout << outnode->getName();

There are also other new utility functions like OP_Node::getFirstOutputNode() and OP_Node::hasAnyOutputNodes() that hide the complexities of dealing with network dots connected to your outputs.

Similarly, the output retrieval functions OP_InputIndirect::nOutputs() and OP_InputIndirect::getOutput() have been replaced by the new OP_IndirectInput::nOutputItems() and OP_IndirectOutput::getOutputItem(), along with most of the same utility functions that exist on OP_Node for hiding network dots and dealing only with the output nodes.

The OP_Dot class itself has all the same capabilities of other OP_NetworkBoxItem objects, with a name, a color, position, and optional parent network box. In general more functionality was moved from the various subclasses into the OP_NetworkBoxItem base class. Many functions were made more general by accepting or return network box items instead of having separate functions for each subclass. In general the term "Item" in a function name means that any OP_NetworkBoxItem is acceptable, though in certain cases (such as OP_Node::getOutputItem()), only a subset of the subclasses may be meaningful for a particular method.

Minor Changes

Removed GA_BlobRef.h. Now GA_BlobData.h is the only header required.

GA_ElementGroup::computeContentHash() has been removed, because it was always incorrect and only used in one place in Houdini. The hash value depended only on the set of active offsets in the detail, and completely ignored the group, so was not useful for groups.

The signatures of SOP_Node::duplicateChangedSource() that took a GU_Topology have been removed, because GU_Topology is not sufficient for determining whether topology has changed, and those signatures were not called anywhere in Houdini.