Getting Started With HDK C++ Wrangles

Apr 02, 25

sample image In my continuing quest to primarily use Houdini for its Real-time viewport for rendering, having lots of control over the viewport rendering seems like it could be promising. Although the new COPs system has a lot of potential, it still has a relatively high performance impact. There is comparatively little information about using the Houdini Development Kit on the way, and it feels “separate” from other Houdini documentation. The documentation, while exceptionally thorough, does not use the same format as the rest of the application and is a little less user-friendly. The SideFX lab team has a single video on building a basic HDA using the HDK, which goes over setting up your build environment.

I was inspired when I remembered there is a C++ wrangle by Animatrix that basically takes care of the build environment for you. lecopivo also authored an enhanced version of this HDA with a couple of extra features, but unfortunately it is a .hdanc file. I thought I would be able to get away with compiling the viewport rendering examples by basically stuffing them into the wrangle, but unfortunately it’s not quite that simple. These HDAs act as wrappers for Houdini’s inlinecpp python module. This module lets you create compiled C++ functions, which you can then call using python.

Hello World

Here’s what a basic hello world looks like using the C++ wrangle:

sample image

Read Geometry

We can pass the current geometry in as an argument. Note that the hou.Geometry class is equivalent to the C++ class GU_detail. GU_detail

void printGeoInfo(GU_Detail *gdp){
	GQ_Detail *gqd = new GQ_Detail ( gdp );
	std::cout << "number of points: " << gqd->nPoints() << std::endl;
	std::cout << "number of edges: " << gqd->nEdges() << std::endl;
	std::cout << "number of faces: " << gqd->nFaces() << std::endl;
	// Get a read-only handle to the "N" attribute (assumed to be a vector3 attribute)
    GA_ROHandleV3 nHandle(gdp, GA_ATTRIB_POINT, "N");

    // Check if the attribute exists
    if (!nHandle.isValid()) {
        std::cout << "Attribute 'N' not found on points." << std::endl;
        return;
    }

    // Iterate over all points and print the "N" values
    GA_Offset ptoff;
    GA_Iterator it(gdp->getPointRange());
    for (it.begin(); !it.atEnd(); ++it) {
        ptoff = *it;
        UT_Vector3 N = nHandle.get(ptoff);
        std::cout << "Point " << ptoff << " N: (" << N.x() << ", " << N.y() << ", " << N.z() << ")" << std::endl;
    }
}

node = hou.node("..")
geo = hou.pwd( ).geometry ( ) # returns a hou.Geometry

myFirstModule.helloWorld()
printGeoInfo.printGeoInfo(geo)

Slicing Example

Animatrix’s C++ provides a more advanced example which slices the geometry numslices of times using the crease function. How exactly you would know this is what the crease function does, I’m not exactly sure, as the documentation doesn’t say much. We can just grab some spare parameter values and feed them into our compiled function:

void clip ( GU_Detail *gdp, float nx, float ny, float nz, float s, float size, int numslices )
{
	GQ_Detail *gqd = new GQ_Detail ( gdp );
	UT_Vector3 normal ( nx, ny, nz );
	float step = size / numslices;
	
	for ( int i = 0; NUM_SLICES_IT){
		gqd->crease ( normal, s, 0, NULL, NULL );
		s += step;
	}
	delete gqd;
}

node = hou.node("..")
geo = hou.pwd().geometry()

nx, ny, nz = node.parmTuple("n").eval()
s = node.parm("s").eval()
size = node.parm("size").eval()
ns = node.parm("ns").eval()

m = hou.hmath.buildTranslate(geo.boundingBox().center())
geo.transform(m.inverted())

hdkclip.clip (geo,nx,ny,nz,s,size,ns)

geo.transform (m)

sample image

SnapToGrid

There’s quite a few GU_Detail methods that are great “out of the box”, like snapToGrid

void snapToGrid(GU_Detail *gdp, int typeinput, float xlines, float ylines, float zlines,
                  float xoff,
                  float yoff,
                  float zoff,
                  float tol ){
    gdp -> snapGrid(typeinput, xlines, ylines, zlines, xoff, yoff, zoff, tol);
}

node = hou.node("..")
geo = hou.pwd( ).geometry ( )
typeinput = node.parm("typeinput").eval()
xlines = node.parm("xlines").eval()
ylines = node.parm("ylines").eval()
zlines = node.parm("zlines").eval()
xoff = node.parm("xoff").eval()
yoff = node.parm("yoff").eval()
zoff = node.parm("zoff").eval()
tol = node.parm("tol").eval()
myPrintInfo.snapToGrid(geo, typeinput,xlines,ylines,zlines,xoff,yoff,zoff,tol)

sample image

Shrink

This is great if you want to use some of the HDKs built in libraries to performantly process some geometry, but if your use case involves anything which can’t compile within a functions scope, like classes or namespaces, then it won’t really work.

After some amount of fiddling around with getting Visual Studio 2022 set up (always a nightmare), I gave up on using CMake or Makefiles to compile my code. Although you need to have Visual Studio installed, if your project is smaller in scope hcustom is probably the best way to compile.

The documentation says

On Windows, remember to set MSVCDir to point to your Microsoft Visual Studio C++ installation before running make or nmake.

After getting a message for sometime that this environment variable do not exist, I found out from some forum research the directory it actually wants is something like:

C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433

![[HDK_header.png]]


Helpful Resources

https://jurajtomori.wordpress.com/2017/12/01/creating-simple-c-openvdb-node-in-hdkcreating-simple-c-openvdb-node-in-hdk/