HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GA Porting Guide

GA Porting Guide: Table Of Contents

Introduction to GA

Houdini 12.0 has a new underlying geometry library. One of the design constraints for the design of the GA library was to minimize effort required to port code from the previous GB incarnation. Thus, though the underlying data structures are very different, the GA library can provide interfaces which make it look similar (not exact), to the older GB library.

The GB library was an "object centric" view of geometry. There were point, vertex, and primitive objects (or "elements").

  • Each object owned its own attribute data
  • All objects of the same type had the same kind of attribute data
  • Points could be shared between primitives
  • Primitives used vertex objects to reference points

While the GA library keeps the concepts of points, vertices and primitives, there are no longer any individual objects (with the current exception of primitives). Instead of individual objects, the geometry stores arrays of attribute data for each class of object.

GA Terms and Definitions

  • GA_Index/GA_Offset
    The GA_Index represents the "ordered" of the element in the array. This corresponds to the getNum() concept found in the old GB elements.

    GA_Offset represents the offset of an element into the attribute arrays. This is immutable over the lifetime of the geometry object (though it may be altered under the defragment operation).

    When an object is deleted, the following objects in the array will have their GA_Offsets unchanged, but their GA_Index will be changed.
  • ATI - Attribute Type Implementation
    An ATI (Attribute Type Implementation) is responsible for managing an array of data for an attribute. Put simply, an ATI implements an attribute type.
  • AIF - Attribute Interface
    An AIF (Attribute Interface) provides a way of accessing data in an ATI. Not all ATI's will support all AIF's. For example, the GA_AIFTuple interface provides access to an attribute as an array of tuple data. Numeric attributes should provide this AIF, while string attributes may not provide this attribute interface.
  • RTI - Range Type Interface
    Ranges provide a unified, generic way of specifying ranges of elements. For example, there can be a GA_Range for all points, for all the points in a particular group, or for all the vertices contained in a particular primitive. Each RTI is a sub-class of GA_RangeTypeInterface and implements a way of getting at the contents of the range.

GA Blind Data (GB_ATTRIB_MIXED)

In earlier versions of geometry code, each element (point, vertex, etc.) contained a pointer to attribute data. It was possible to allocate a chunk of blind data for each element by allocating an attribute of type GB_ATTRIB_MIXED. Since the GA library maintains arrays of attribute data (indexed by the element offset), blind data must be allocated in a different fashion.

The GA library comes with an ATI (Attribute Type Implementation) named "blinddata". This ATI provides the GA_AIFBlindData interface to its data.

Note that unless you create a sub-class of GA_ATIBlindData (or a new ATI which implements GA_AIFBlindData), there isn't any easy way of constructing/destructing blind data. Please see GA Blind Blob Data for a process to add reference counted arbitrary blobs of data to objects.

GA Blind Blob Data

When using GA_ATIBlindData a chunk of memory will always be allocated for each element.

For some algorithms, you may need to allocate a "blob" of data for some elements, but not others. GA_ATIBlob is an ATI (Attribute Type Implementation) which allocates a table of shared blobs. Blobs are reference counted as they are attached/detatched to elements. The blobs are automatically cleaned up. The blobs may contain arbitrary data, but must be sub-classed off GA_BlobData. For example, to implement strings using ATIBlob, you might have something like:

class StringBlob : public GA_BlobData
{
public:
StringBlob()
: myString()
{}
StringBlob(const char *str)
: myString(UT_String::ALWAYS_DEEP, str)
{}
virtual ~StringBlob() {}
// Implementation of methods on GA_Blob
virtual uint hash() const { return myString.hash(); }
virtual bool isEqual(const GA_BlobData &blob)
{
const StringBlob *s;
s = dynamic_cast<const StringBlob *>(&blob);
return s && (s->myString == myString);
}
// Optionally implement save/load of blobs
// virtual bool jsonSave(UT_JSONWriter &w, const GA_SaveMap &map) const;
// virtual bool jsonLoad(UT_JSONParser &p, const GA_LoadMap &map) const;
private:
UT_String myString;
};

With a class like this, you can add "string" objects to elements. For example.

"blob_name",
NULL, NULL,
"blob");
GA_Attribute *a = blob_gah.getAttribute();
const GA_AIFBlob *aif = a->getAIFBlob();
// This will only create blobs for objects in the range (not all
// objects).
for (GA_Iterator it = range.begin(); !it.atEnd(); ++it)
{
GA_BlobRef strblob(new StringBlob(getStringValue(*it)));
aif->setBlob(a, strblob, *it);
}

You can access blobs in a similar fashion:

const GA_Attribute *a = blob_gah.getAttribute();
const GA_AIFBlob *aif = a->getAIFBlob();
for (GA_Iterator it = range.begin(); !it.atEnd(); ++it)
{
GA_BlobRef strblob = aif->getBlob(a, *it);
if (strblob) { ... }
}

To delete a blob on an element, just set the blob to an empty blob handle:

aif->setBlob(attrib, GA_BlobRef(), offset);

Porting Cookbook

This section covers porting recipes for common code patterns in GB.

Simple Name Translation

Many classes have simple prefix renaming for similar classes in GA

GB_Primitive ==> GA_Primitive
GB_PrimitiveGroup ==> GA_PrimitiveGroup
GB_PointGroup ==> GA_PointGroup
GB_Basis ==> GA_Basis
GB_PointRef ==> GA_PointRef
GB_Attribute ==> GA_Attribute
GB_AttributeDict ==> GA_AttributeDict
GB_AttributeInstanceMatrix ==> GA_AttributeInstanceMatrix
GB_PointRedirectArray ==> GA_PointRedirectArray

Non-Intuitive Name Translation

Other classes/enums have less intuitive equivalents in GA

GB_BaseGroup ==> GA_Group
GB_Group ==> GA_ElementGroup
GB_Edge ==> GA_EdgeGroup::Entry

GB_AttribType

There isn't a perfect mapping from GB_AttribType to the GA library. The closest is probably GA_StorageClass (which gives a generic float, int or string storage). The "type" part of the qualifier has been split out into GA_TypeInfo.

// GB Code
GB_AttributeRef N = addPointAttrib("N", sizeof(float)*3, GA_ATTRIB_VECTOR);
GB_AttributeRef Cd = addPointAttrib("Cd", sizeof(float)*3, GA_ATTRIB_FLOAT);
==>
// GA Code
GA_RWAttributeRef N = addFloatTuple(GA_ATTRIB_POINT, "N", 3);
GA_RWAttributeRef Cd = addFloatTuple(GA_ATTRIB_POINT, "Cd", 3);

GB_ElementList

GB had arrays of pointers to objects. As these objects no longer exist, code using element arrays should likely be re-written to be more efficient. The GA version of element lists return by value (not by reference).

// GB Code
GEO_PointList &pnts = gdp->points();
==>
// GA Code
GEO_PointList pnts = gdp->points();

GB Macros (FOR_ALL*)

FOR_ALL_POINTS(gdp,ppt) ==> GA_FOR_ALL_POINTS(gdp,ppt)
FOR_ALL_PRIMITIVES(gdp,prim) ==> GA_FOR_ALL_PRIMITIVES(gdp,prim)
etc.

Copying Point/Vertex Attributes

// GB Code
vtx->copyAttributeValues(src_vertex, gdp->vertexAttribs()),
ppt->copyAttributeValues(src_point, pt_dict);
==>
// GA Code
vtx->copyAttributeValues(src_vertex, gdp->getAttributes());
ppt->copyAttributeValues(*src_point, gdp->getAttributes());
// Alternatively, a more efficient way would be to
// Declare the wrangler at the highest level you can
GA_ElementWrangelerCache wranglers(gdp, GA_PointWrangler::INCLUDE_P);
// Now, use the wrangler
wranglers.getVertex().copyAttributeValues(vtx->getMapOffset(), src_vertex.getMapOffset());
wranglers.getPoint().copyAttributeValues(ppt->getMapOffset(), src_point->getMapOffset());

Point/Vertex setPos()

// GB Code
ppt->setPos(const UT_Vector3 &P3); // Also sets w component to 1
==>
// GA Code
ppt->setPos(P3); // No change needed
// Alternately, use the new GA interface
gdp->setPos3(ppt->getMapOffset(), P3);
gdp->setPw(ppt->getMapOffset(), 1);

Point/Vertex getPos()

The GB library had methods to return a non-const reference to the UT_Vector4 storing the point's position. This is no longer possible in GA. Code which uses this technique must be changed to use getPos()/setPos()

// GB Code
const UT_Vector4 &P4_read = ppt->getPos();
UT_Vector4 &P4_write = ppt->getPos();
==>
// GA Code
const UT_Vector4 P4_read = ppt->getPos(); // No reference
const UT_Vector3 P3_read = ppt->getPos3(); // Use getPos3() for efficiency.
// There is no corresponding way to get a writable reference

Destroying All Points In A Group

// GB Code
gdp.deletePoint(*group, [fast], [remove_degenerate]);
==>
// GA Code
gdp.deletePoints(*group, remove_degenerate ? GA_Detail::GA_DESTROY_DEGENERATE : GA_Detail::GA_LEAVE_PRIMITIVES);

Destroying Unused Points

// GB Code
gdp.removeUnusedPoints(group);
==>
// GA Code
gdp.destroyUnusedPoints(group);

Checking Whether a Point is Referenced

Check whether a point is referenced by any primitives. This will use the topology attributes if they exist

// GB Code
gdp.isPointUsed(ppt)
==>
// GA Code
gdp.isPointUsed(ppt->getMapOffset());

Destroying Degenerate Primitives

// GB Code
gdp.removeDegeneratePrimitives()
==>
// GA Code
gdp.destroyDegeneratePrimitives();

Destroying Unused Groups

// GB Code
gdp.removeUnusedGroups();
==>
// GA Code
gdp.destroyAllEmptyGroups();

Destroy Unused Points & Degenerate Primitives

// GB Code
gdp.removeUnused(const GB_PrimitiveGroup *prims, const GB_PointGroup *points);
==>
// GA Code
// Note the order of the two calls matters!
gdp.destroyDegeneratePrimitives(prims);
gdp.destroyUnusedPoints(points);

Iterating Over Points

In GA, iteration can be done using a GA_Range and GA_Iterator. There are also macros to encapsulate many typical types of iteration.

// GB Code
UT_Vector3 avg(0,0,0);
GEO_Point *ppt;
FOR_ALL_GPOINTS(gdp, ppt)
avg += ppt->getPos();
avg /= gdp->getNumPoints();
==>
// GA Code
UT_Vector3 avg(0,0,0);
GA_Offset ptoff;
GA_ROHandleV3 P_h(gdp->getP());
GA_FOR_ALL_PTOFF(gdp, ptoff)
avg += P_h(ptoff);
// or iterate using a GA_Iterator
for (GA_Iterator it(gdp->getPointRange()); !it.atEnd(); ++it)
avg += P_h(*it);
// or iterate using a GA_PageIterator - this gives contiguous blocks
GA_Offset block_start, block_end;
for (GA_Iterator pageI(gdp->getPointRange()); pageI.blockAdvance(block_start, block_end); )
{
for (GA_Offset ptoff = blk_start; ptoff < blk_end; ++ptoff)
avg += P_h(ptoff);
}

Iterating Over Primitives

Similar to iteration over points, use a GA_Iterator. However, the iterator gives you a GA_Offset, and often when iterating over primitives, you want the actual GEO_Primitive.

// GB Code
FOR_ALL_PRIMITIVES(gpd, prim)
processPrimitive(prim);
==>
// GA Code
processPrimitive(prim);
// or, using a GA_Iterator
for (GA_Itertor it(gdp->getPrimitiveRange()); !it.atEnd(); ++it)
processPrimitive(gdp->getGEOPrimitive(*it));

Vertex Iteration of a Polygon

GEO_Vertex is now a "handle" instead of a pointer. Primitives no longer contain vertex objects.

// GB Code
GEO_FOR_FACE_VERTICES(poly, vtx, idx) {}
==>
// GA Code
GEO_Vertex vtx; // An object (not a pointer)
GEO_FOR_FACE_VERTICES(poly, vtx, idx) {}

Generic Vertex Iteration

Random access GEO_Primitive::getVertexElement() and GA_Primitive::getVertexOffset() methods exist, but it is best to use an explicit iterator.

// GB Code
for (GA_Size i = 0; i < prim->getVertexCount(); i++)
{ doSomething(prim->getVertexOffset(i)); }
==>
// GA Code
GA_Primitive::const_iterator it;
for (prim->beginVertex(it); !it.atEnd(); ++it)
{ doSomething(it.getVertexOffset()); }

Vertex Pointers

Primitives no longer store vertex objects, so they return vertex objects (rather than references to objects). Some methods still require GEO_Vertex pointers. In these cases, you will need to create a temporary object and pass the address of the temporary. gcc will warn if you attempt to take the address of a temporary. You should pay attention to this warning.

// GB Code
myCurVtx[0] = &prim->getVertex(i);
==>
// GA Code
myCurVtxObject[0] = prim->getVertex(i);
myCurVtx[0] = &myCurVtxObject[0];

GBclearAttributeRef

// GB Code
GB_AttributeRef attrib_ref;
GBclearAttributeRef(attrib_ref);
==>
// GA Code
GA_RWAttributeRef attrib_ref;
attrib_ref.clear();

GBisAttributeRefValid

// GB Code
GB_AttributeRef attrib_ref;
if (!GBisAttributeRefValid(attrib_ref)) return false;
==>
// GA Code
GA_RWAttributeRef attrib_ref;
if (!attrib_ref.isValid()) return false;

GB_AttributeDict::appendClone()

// GB Code
GB_AttributeDict::appendClone(atr)
==>
// GA Code
gdp->addPointAttrib(atr);
gdp->addVertexAttrib(atr);
gdp->addPrimAttrib(atr);
gdp->addGlobalAttrib(atr);

getAttributeDict()

getAttributeDict() now returns a reference instead of a pointer

// GB Code
gdp->getAttributeDict(owner)
==>
// GA Code
&gdp->getAttributeDict(owner)

Renaming Attributes

// GB Code
attribute->setName(newname);
==>
// GA Code
gdp->renameAttribute(attribute->getOwner(), attribute->getScope(), attribute->getName(), newname);

Destroying a point

// GB Code
gdp.deletePoint(ppt->getNum(), [fast]);
==>
// GA Code
gdp.deletePoint(*ppt);
// Alternate methods:
gdp.destroyPointIndex(ppt->getMapIndex()); // OR
gdp.destroyPointOffset(ppt->getMapOffset());

Attribute Data As a const pointer

The GA library allows for data to be stored as fpreal16, fpreal32 or fpreal64 (or int8, int16, int32, int64). So, there isn't always an easy way to get a fpreal32 pointer. Data may or may not have to be marshalled out of the buffer

// GB Code
GB_AttributeElem &pt;
GB_AttributeRef &h;
GB_AttributeBuffer buffer; // Storage for non-float data
const float *data_ptr;
data_ptr = pt.getPointer(h, buffer, nfloats);
==>
// GA Code
GA_GBAttributeBuffer buffer;
const float *data_ptr;
data = pt.getPointer(h, buffer, nfloats);

internalN attribute

The viewport (and some other few places) might have created an internal attribute named internalN to store normals for rendering. In GB, this was "hidden" by making the attribute GB_ATTRIB_MIXED. In GA, there are convenience methods to deal with this attribute defined on GEO_Detail

// GB Code
gdp->findPointAttrib("internalN", sizeof(float)*3, GB_ATTRIB_MIXED);
gdp->destroyPointAttrib("internalN");
==>
// GA Code
gdp->findInternalNormalAttribute();
gdp->destroyInternalNormalAttribute()

Finding Attributes

Note: In the GB library size was specified in bytes. GA specifies size in components.

Also, because the type is part of the method name, this does not suit code which defines standard attributes like:

#define SIM_ATT_MASS GEO_STD_ATTRIB_MASS,sizeof(float),GB_ATTRIB_FLOAT
// GB Code
findPrimAttrib(name, size, type),
findVertexAttrib(name, size, type),
findPointAttrib(name, size, type),
==>
// GA Code
findIntTuple(GA_ATTRIB_POINT, name, min_size, [max_size]);
findFloatTuple(GA_ATTRIB_POINT, name, min_size, [max_size]);
findStringTuple(GA_ATTRIB_POINT, name, min_size, [max_size]);

Adding Attributes

Note: In the GB library size was specified in bytes. GA specifies size in components.

The GB library only allowed a tuple size of 1 for string attributes.

Also, because the type is part of the method name, this does not suit code which defines standard attributes like:

#define SIM_ATT_MASS GEO_STD_ATTRIB_MASS,sizeof(float),GB_ATTRIB_FLOAT
// GB Code
addPrimAttrib(name, size, type, default);
addVertexAttrib(name, size, type, default);
addPointAttrib(name, size, type, default);
// Generic attribute creation
addAttribute(name, size, type, default, owner);
==>
// GA Code
addIntTuple(GA_ATTRIB_POINT, name, tuple_size, [defaults], [options], [storage])
addFloatTuple(GA_ATTRIB_VERTEX, name, tuple_size, [defaults], [options], [storage])
addStringTuple(GA_ATTRIB_PRIMITIVE, name, tuple_size, [options])
// Generic tuple creation
addTuple(GA_STORE_INT32, GA_ATTRIB_POINT, name, tuple_size...

Destroying Attribute

// GB Code
destroyPointAttrib(name, size, type);
==>
// GA Code
destroyPointAttrib(name);

The GB_ATTRIB_INDEX String Type

The GB_ATTRIB_INDEX stored strings using a table of shared strings where each string had a unique integer associated with it. The attribute stored an integer (-1 for no string) for each element.

If an attribute in GA provides the GA_AIFSharedStringTuple interface, similar operations can be done.

// GB Code
static int minus_one = -1;
GB_AttributeRef h = gdp->addPrimAttrib("tex", sizeof(int), GA_ATTRIB_INDEX, &minus_one);
GB_Attribute *atr = gdp->primitiveAttribs().findByOffset(h);
int idx;
idx = atr->addIndex("foo"); // Add string to shared string table
prim->setValue<int>(h, idx); // Store the string on the primitive
atr->renameIndex(idx, "bar"); // Change the string for the index
==>
// GA Code
GA_RWAttributeRef h = gdp->addStringTuple(GA_ATTRIB_PRIMITIVE, "tex", 1);
prim->setString(h, "foo");
int idx = prim->getStringHandle(h);

Enumerating Attributes

Note: the GA point attributes includes the "P" attribute, as well as any boolean attributes used to store group membership.

// GB Code
destroyPointAttrib(name, size, type);
destroyPrimAttrib(name, size, type);
destroyPointAttrib(name);
==>
// GA Code
gdp->getAttributes().getDict(GA_ATTRIB_POINT).entries()
gdp->getAttributes().getDict(GA_ATTRIB_VERTEX).entries()
gdp->getAttributes().getDict(GA_ATTRIB_PRIMITIVE).entries()

Testing for any attributes

// GB Code
vtx->hasAllocatedAttributeValues()
==>
// GA Code
gdp->getAttributes().getDict(GA_ATTRIB_VERTEX).entries() > 0

Iterating Over Attributes

Use iterator objects

// GB Code
GB_ConstAttributeDictOffsetIterator it;
// Or code like
for (atr = dict.getHead(); atr; atr = atr->next()) {}
==>
// GA Code
for (GA_AttributeDict::iterator it=dict.begin(); !it.atEnd(); ++it)
{ atr = it.attrib(); ... }

GB_ATTRIB_VECTOR

The GB library used the attribute type GB_ATTRIB_VECTOR to represent normal attributes. The GA library now stores more complete type information on attributes.

  • GA_TYPE_POINT
    Represents a position
  • GA_TYPE_VECTOR
    Represents a direction vector
  • GA_TYPE_NORMAL
    Represents a normal vector
  • GA_TYPE_COLOR
    Represents a color
  • etc.
// GB Code
findPointAttrib(name, 3*sizeof(float), GB_ATTRIB_VECTOR);
findVertexAttrib(name, 3*sizeof(float), GB_ATTRIB_VECTOR);
findPrimAttrib(name, 3*sizeof(float), GB_ATTRIB_VECTOR);
==>
// GA Code
GA_RWAttributeRef ref = gdp.findFloatTuple(GA_ATTRIB_POINT, name, 3, 3);
if (ref.isValid() && ref.getTypeInfo() == GA_TYPE_NORMAL) {}

Finding a GB_AttributeRef from a GB_Attribute pointer

// GB Code
GB_AttributeRef r = gdp->pointAttribs().getOffset(attribute);
==>
// GA Code
GA_ROAttributeRef r(attribute);

Finding a GB_Attribute from a GB_AttributeRef

The GA_ROAttributeRef/GA_RWAttributeRef objects now hold a pointer to the attribute

// GB Code
GB_Attribute *a = gdp.pointAttribs().findByOffset(aref);
==>
// GA Code
GA_Attribute *a = aref.getAttribute()

Detail Attributes Using Objects

// GB Code
gdp->attribs().getElement().getValue(aref, ...);
gdp->attribs().getElement().setValue(aref, ...);
==>
// GA Code
gdp.element().getValue(ref...);
gdp.element().setValue(ref...);

Duplicating Attribute Definition Between Details 1

In GB, GEO_AttribDict or GB_AttributeDict could be used to clone attributes. In GA, there is GA_AttributeDict.

// GB Code
GEO_PointAttribDict &pattrib = gdp->pointAttribs();
for (GB_Attribute *atr = (GB_Attribute*)head(); atr;
atr = (GB_Attribute*)atr->next())
==>
// GA Code
for (GA_AttributeDict::iterator it = src_gdp->getAttributes().getDict(GA_ATTRIB_POINT).begin(GA_SCOPE_PUBLIC);
!it.atEnd(); ++it)
{
GA_Attribute *a = it.attrib();
dst_gdp->getAttributes().cloneAttribute(GA_ATTRIB_POINT, a->getName(), *a, true);
}

Duplicating Attribute Definition Between Details 2

In GB, GEO_Detail::sortAllAttributes() and GEO_Detail::mergeDetailAttributes() could be used to clone attributes. In GA, there is GA_Detail::cloneMissingAttributes(), though, unlike GEO_Detail::mergeDetailAttributes(), it only clones attribute definitions and does not copy attribute values.

// GB Code
gdp->sortAllAttributes(*src_gdp);
...
gdp->mergeDetailAttributes(*src_gdp, original_point_count);
==>
// GA Code
GA_AttributeFilter filter(GA_AttributeFilter::selectAll());
gdp->cloneMissingAttributes(*src_gdp, GA_ATTRIB_POINT, filter);
gdp->cloneMissingAttributes(*src_gdp, GA_ATTRIB_VERTEX, filter);
gdp->cloneMissingAttributes(*src_gdp, GA_ATTRIB_PRIMITIVE, filter);
gdp->cloneMissingAttributes(*src_gdp, GA_ATTRIB_GLOBAL, filter);

Checking the attribute type (now called storage)

In GB, GB_Attribute::getType() would return a numerical type of an attribute. In GA, GA_Attribute::getType() returns a GA_AttributeType object, which contains overall information about an attribute, like a name and uniqe ID, rather than just a numeric type. To find out the numeric type, the simplest way is to call GA_Attribute::getStorageClass(). GA_StorageClass is a convenience enumeration that is based on the GA_Storeage and it is GA_Storage that is used for setting the underlying storage.

// GB Code
if (atr->getType() == GB_ATTRIB_INT)
...
atr->setType(GB_ATTRIB_FLOAT);
==>
// GA Code
...

Adding Index Pair Attributes

// GB Code
gdp->addPtIndexPairAttribute(name, npairs*sizeof(float32));
==>
// GA Code
gdp->addPtIndexPairAttribute(name, GEO_Detail::geo_NPairs(npairs), GA_STORE_REAL32);

GB_AttributeIndexPairs

// GB Code
GB_AttributeIndexPairs<int, float> attrib(ppt, ref);
==>
// GA Code
GEO_AttributeIndexPairs attrib(ppt, ref);

Working With Point Capture Attributes

// GB Code
const char *name = GEO_Detail::getPointCaptureIndexAttribName(type);
GB_Attribute *path_attrib = gdp->attribs().find(name, sizeof(int), GB_ATTRIB_INDEX);
if(path_attrib)
{
int num_paths = path_attrib->getIndexSize();
for (int i = 0; i < num_paths; ++i)
{
<tab> const char *path = path_attrib->getIndex(i);
<tab> // ...
}
}
==>
// GA Code
GEO_AttributeCapturePath path_attrib(gdp);
if(path_attrib.isValid())
{
int num_paths = path_attrib.getNumPaths();
for (int i = 0; i < num_paths; ++i)
{
<tab> const char *path = path_attrib.getPath(i);
<tab> // ...
}
}

GEOPRIMPOLY: Primitive Ids

In GB, the some primitive ids were a bit mask, others were not.

There are two ways to port querying primitive types.

For compatibility, there is a GEO_PrimTypeCompat namespace containing an object for each of the old named bitfields. These compatibility objects work much in the same way as the previous defines. That is, you can or them together as bit fields, etc. Using them as simple as pre-pending GEO_PrimTypeCompat::

if (prim->getPrimitiveId() == GEO_PrimTypeCompat::GEOPRIMPOLY) {}
GA_PrimCompat::TypeMask prim_type(prim->getPrimitiveId());

The alternative is to switch to the new code. Rather than using prim->getPrimitiveId(), you would use prim->getTypeId() instead.

The primitive type ID provides a simple integer (not a bit field mask). The new IDs for well-known primitives add an underscore to the old name. For example GEO_PRIMPOLY.

if (prim->getTypeId() == GEO_PRIMPOLY) {}
// GB Code
switch (prim->getPrimitiveId()) { case GEOPRIMPOLY: ... }
==>
// GA Code
switch (prim->getTypeId().get()) { case GEO_PRIMPOLY: ... }
/// Or change the switch to an if/else if there are few cases
if (prim->getPrimitiveId() == GEO_PrimTypeCompat::GEOPRIMPOLY) {}
else if (prim->getPrimitiveId() == GEO_PrimTypeCompat::GEOPRIMMESH) {}

FOR_MASK_PRIMITIVES

// GB Code
FOR_MASK_PRIMITIVES(gdp, prim, GEOTPSURF) {}
==>
// GA Code
// for now use the old bit-field

Traversing Edges

// GB Code
FOR_ALL_GROUP_EDGES(edgelist(), e) {}
==>
// GA Code

Edges And Primitive Pointers

The optional primitive is now stored in the group entry and not the edge object.

// GB Code
FOR_ALL_GROUP_EDGES(group, edge) { processPrimitive(edge->prim()); }
==>
// GA Code
for (GA_EdgeGroup::iterator it = group->begin(); !it.atEnd(); ++it)
processPrimitive(it.getPrimitive());

Edge Points

// GB Code
(GEO_Point *)edge.p0()
==>
// GA Code
gdp->getGEOPoint(edge.p0()); // Or preferably, use the offset

Edge Point Position

// GB Code
(GEO_Point *)edge.p0()->getPos()
==>
// GA Code
gdp->getPos4(edge.p0()); // or use getPos3() for efficiency if no w component is desired

Edge Hash

Edge hashes are constructed with the point offsets

// GB Code
GB_EdgeHash(pt0, pt1)
==>
// GA Code
GA_EdgeHash(pt0->getMapOffset(), pt1->getMapOffset())

Iterating Over Groups

Use iterator objects

// GB Code
for (grp = gdp->primitiveGroups().head(); grp; grp = grp->next())
==>
// GA Code
for (GA_ElementGroupTable::iterator it = gdp->primitiveGroups().beginTraverse(); !it.atEnd(); ++it) {}
// Or use the GA_GBMacro
GA_FOR_ALL_GROUPS(gdp->primitiveGroups(), grp) {}

Iterating Over Groups

Note: It is not safe to delete groups from within the macro traversal

// GB Code
FOR_ALL_POINTGROUPS, FOR_ALL_PRIMGROUPS
==>
// GA Code

Creating a GB_Edge

You likely won't have to allocate a new edge now as edges are no longer linked list nodes themselves. You should be able to create edges off the stack.

// GB Code
new GB_Edge(p0, p1);
==>
// GA Code
GA_Edge(p0->getMapOffset(), p1->getMapOffset());

Querying Group Type

The GB defines have been changed to more consistent names for GA:

  • GBGROUP ==> GA_GROUP_TYPE_MASK
  • GBPOINTGROUP ==> GA_GROUP_POINT
  • GBPRIMITIVEGROUP ==> GA_GROUP_PRIMITIVE
  • GBEDGEGROUP ==> GA_GROUP_EDGE
  • GBBREAKPOINTGROUP ==> GA_GROUP_BREAKPOINT
  • GBVERTEXGROUP ==> GA_GROUP_VERTEX
    // GB Code
    if (group()->classType() == GBPRIMITIVEGROUP)
    ==>
    // GA Code
    if (group()->classType() == GA_GROUP_PRIMITIVE)

Adding to a group and making it ordered.

Making a group ordered is no longer part of adding/removing elements. The creation of ordered groups should be done outside of any loops

// GB Code
group->add(element, 1);
==>
// GA Code
group->makeOrdered();
group->add(element);

Checking if a Group is Ordered

// GB Code
group->ordered()
==>
// GA Code
group->getOrdered() != NULL

Copying A Group

// GB Code
gdp.copyGroup(existing, new_name);
==>
// GA Code
// If the group is a temporary group
grp = gdp.createInternalElementGroup(GA_ATTRIB_PRIMITIVE);
// If the group is persistent
grp = gdp.createElementGroup(GA_ATTRIB_POINT, new_name);
// Now, copy membership
grp->copyMembership(existing);

Copying Group Membership

// GB Code
group->setFlags(source_group)
==>
// GA Code
group->copyMembership(*source_group)

Testing group membership

// GB Code
group->contains(prim->getNum())
==>
// GA Code
group->contains(*prim); // OR
group->containsIndex(prim->getNum());

Optimizing tests of group membership

// GB Code
prim->getGroups().isset(group->getOffset(), group->getMask());
==>
// GA Code
group->contains(prim);

Removing From A Group

// GB Code
group->remove(prim->getNum())
==>
// GA Code
group->remove(*prim); // OR
group->removeIndex(prim->getNum());

Adding To A Group

// GB Code
group->add(prim->getNum())
==>
// GA Code
group->add(*prim);
group->addIndex(prim->getNum());

Destroying A Group

// GB Code
group->destroyGroup(group);
==>
// GA Code
gdp->destroyElementGroup(group); // Element groups
gdp->destroyGroup(group); // Generic groups

Vertex Group Iteration

// GB Code
GB_VertexGroupIterator it(group);
GB_VertexData vtxdata;
int valid;
for (valid = it.head(vtxdata); valid; valid = it.next(vtxdata))
{
GB_Primitive *prim = vtxdata.prim();
int index = vtxdata.linearIndex();
...
}
==>
// GA Code
GA_GBVertexGroupIterator it(group);
for (it.rewind(); !it.atEnd(); it.advance())
{
const GA_Primitive *prim = it.getPrimitive();
int index = it.getLinearIndex();
...
}

GB_Basis

// GB Code
GB_Basis::breakCount()
GB_Basis::interpolatesEnds() const
GB_Basis::interpolatesEnds(value)
GB_Basis::multiplicity()
==>
// GA Code
GA_Basis::getBreakCount()
GA_Basis::getEndInterpolation() const
GA_Basis::setEndInterpolation(value) const
GA_Basis::getMultiplicity()

GB_NUBBasis

// GB Code
GB_NUBBasis::knotsEqualSpace()
GB_NUBBasis::knotsAveraging()
GB_NUBBasis::knotsSpreading()
GB_NUBBasis::knotsBreakpoints()
GB_NUBBasis::breakpnt(int/fpreal)
GB_NUBBasis::periodic()
GB_NUBBasis::validInterval()
==>
// GA Code
GA_NUBBasis::setKnotsByEqualSpace()
GA_NUBBasis::setKnotsByAveraging()
GA_NUBBasis::setKnotsBySpreading()
GA_NUBBasis::setKnotsByBreakpoints()
GA_NUBBasis::findBreakpoint(int/fpreal)
GA_NUBBasis::makeNURBSPeriodic()
GA_NUBBasis::getValidInterval()

Wrapping GB_NUBBasis

// GB Code
GB_NUBBasis::wrap();
GB_NUBBasis::unwrap();
==>
// GA Code

Getting Basis Data

// GB Code
const fpreal64 *data = GB_Basis::getData();
==>
// GA Code
const GA_KnotVector &data = GB_Basis::getKnotVector();

Binomial Coefficients

// GB Code
gb_biCoeff[i][j]
==>
// GA Code
GA_Basis::theBinomal[i][j]

Break Points

// GB Code
GB_Breakpoint::evaluate(pos)
==>
// GA Code
GA_Breakpoint::getSpline()
GEO_Breakpoint::evaluate(bkpt, pos)

Break Point Groups

// GB Code
breakpoint_group.add(*(new GEO_Breakpoint(original_bkpt))
==>
// GA Code
breakpoint_group.add(original_bkpt)

Barycentric Coordinates

// GB Code
vtx.getPt()->baryAttributeValues(v0->getPt(), v1->getPt(), v2->getPt(), u, v);
vtx.baryAttributeValues(v0, v1, v2, u, v);
==>
// GA Code
// Interpolate all vertex & point attributes
map.append(GA_AttributeFilter::selectAll(), GA_ATTRIB_VERTEX);
map.append(GA_AttributeFilter::selectAll(), GA_ATTRIB_POINT);
// Set the destination vertex
gah.setElement(vtx.getMapOffset());
// This will compute both vertex & point attributes -- see GA_AttributeRefMap.h
gah.barycentric(v0.getMapOffset(), v1.getMapOffset(), v2.getMapOffset(), u, v);

Bilinear Interpolation

// GB Code
vtx.getPt()->bilinearAttributeValues(v0->getPt(), v1->getPt(), v2->getPt(), u, v);
vtx.bilinearAttributeValues(v0, v1, v2, u, v);
==>
// GA Code
GA_MultiAttributeHandle map(gdp);
// Interpolate all vertex & point attributes
map.append(GA_AttributeFilter::selectAll(), GA_ATTRIB_VERTEX);
map.append(GA_AttributeFilter::selectAll(), GA_ATTRIB_POINT);
// Set the destination vertex
gah.setElement(vtx.getMapOffset());
// This will compute both vertex & point attributes -- see GA_AttributeRefMap.h
gah.bilinear(v0.getMapOffset(), v1.getMapOffset(), v2.getMapOffset(), u, v);

Enumerating Profile Curves

In GA, primitives can have multiple secondary details (rather than a single detail). Thus, there is an additional query getNumSecondaryDetails().

You can also query primitives in a secondary detail using GA_Offset using prim->getSecondaryByOffset(detail_index,offset).

// GB Code
prim->numSecondary();
prim->getSecondary(index);
==>
// GA Code
prim->getNumSecondary(0); // Number of primitives in the first secondary detail
prim->getSecondaryByIndex(0, index);

Profile Curve Groups

// GB Code
group->addMix(prim, profile);
==>
// GA Code
GA_SecondaryLookupInfo sec(0, prof->getMapOffset());
group->addMix(prim, &sec);

Iterating Over Profiles

// GB Code
==>
// GA Code
// Note: This is not GA_ prefixed, instead use <GEO/GEO_Macros.h>
'FOR_ALL_MIX_GROUP_PRIMITIVES()

Setting Crease Weights

GA_Edge no longer has a reference to a primitive. Thus, you need to specify the primitive explicitly

// GB Code
GQ_Detail::setCreaseWeight(const GB_Edge &edge,
const GB_AttributeRef &vtxoff,
const GB_AttributeRef &ptoff);
==>
// GA Code
const GA_Primitive &prim,
const GA_ROAttributeRef &vtxoff,
const GA_ROAttributeRef &ptoff);

Setting Local Variables in SOPs

SOP_Node no longer has a myCurPt[] or myCurPrim[]. Instead, the myCurPrimOff[] and myCurPtOff[] are used.

// GB Code
myCurPt[0] = ppt;
myCurPt[0] = 0;
==>
// GA Code
myCurPtOff[0] = ppt ? ppt->getMapOffset() : GA_INVALID_OFFSET;
myCurPtOff[0] = GA_INVALID_OFFSET;