这篇文章的目标是
1、编译跑通第一个HDK程序
2、以最少的代码量编译跑通一个HDK
————————————————————
————————————————————
HDK(Houdini Development Kit)是Houdini的开发者工具包,我们可以利用它实现一些Houdini功能的拓展,接下来进入正题一,编译并运行第一个HDK程序——SOP_PointWave。
我们来到这个目录,找到官方示例SOP_PointWave。
1、核心文件只有以下两个, 有了他俩我们就能构建一个HDK节点在Houdini里使用。
SOP_PointWave.h
SOP_PointWave.C
2、部分官方示例可以使用Cmake构建文件,博主将不会使用Cmake构建程序,而是使用Houdini Command Line的hcustom命令编译HDK程序,hcustom指令是编译HDK程序的一种方法,详见参考HDK的官方文档:https://www.sidefx.com/docs/hdk/_h_d_k__intro__tools.html。
接下来打开开始菜单找到Houdini Command Line,也就是Houdini内置的命令行窗口。
默认工作目录是C盘下,我们通过cd指令来到SOP_PointWave程序的目录下 ,之后输入hcustom指令编译HDK程序(hcustom会自动找到当前目录下对应名称的.h文件)。
完成了上述hcustom命令之后之后,我们可以发现Houdini的document路径下多了一个dso文件夹并且文件夹内多了一个dll文件。
博主认为dso表示是.dll文件/.so文件,专门用来放置动态库文件,houdini自身在启动时也会访问这个文件夹的信息,就像otls一样。而中间文件将会在源代码文件的目录下生成,可以删掉。
至此,我们已经完成了我们第一个HDK文件的创建!
我们重启houdini看看到底发生了什么吧,来到sop层级下,发现节点菜单多了一个Point Wave。
我们试试看把他用到平面上。
它的所有功能包括参数面板都由SOP_PointWave的h文件和C文件实现。
————————————————————
————————————————————
接下来我们进入今天的第二阶段,用最少的代码量实现一个控制geometry移动的hdk吧。
首先创建一个空的VS工程,创建一个名为SOP_TestXform的类,并且包含HDK工具包的include路径。
准备工作完成,现在,我们来编写.h文件。
我们想要创建的是一个类似于transform功能的节点,需要继承于SOP_Node,一个最简单的HDK程序由以下5个部分构成:
1、构造函数(默认规则)
2、析构函数(默认规则)
3、myConstructor函数(HDK默认规则)
4、参数列表成员(HDK默认规则,也就是节点的参数面板)
5、cookMySop函数(HDK默认规则,也是核心逻辑实现部分)
#include <SOP/SOP_Node.h>
class SOP_TestXform : public SOP_Node
{
public:
SOP_TestXform(OP_Network* net, const char* name, OP_Operator* op);
~SOP_TestXform() override;
//参数列表
static PRM_Template sTemplateList[];
//HDK默认规则
static OP_Node* constructor(OP_Network*, const char*, OP_Operator*);
protected:
//核心逻辑实现部分
OP_ERROR cookMySop(OP_Context& context) override;
};
上述就是一个完整的HDK头文件的表述,OP_前缀表示houdini的operator,还有以后将会接触到的各种前缀类似GA_(Houdini的底层几何库,自houdini12.0取代旧的GB库)、UT_(也就是Util),详见可参考官方文档。
有参数面板就有参数,我们希望控制geometry上下移动的距离大小,我们为其添加一个成员函数用来获取参数数值。
#include <SOP/SOP_Node.h>
class SOP_TestXform : public SOP_Node
{
public:
SOP_TestXform(OP_Network* net, const char* name, OP_Operator* op);
~SOP_TestXform() override;
//参数列表
static PRM_Template myTemplateList[];
//HDK默认规则
static OP_Node* constructor(OP_Network*, const char*, OP_Operator*);
protected:
//核心逻辑实现部分
OP_ERROR cookMySop(OP_Context& context) override;
private:
//控制移动距离大小
//fpreal等价于float
fpreal DISTANCE(fpreal t) { return evalFloat("distance", 0, t); }
};
注意:fpreal等价于float,而它接受的参数t表示当前的帧数。
我们的头文件编写完了,接下来开始编写cpp文件。
首先包含每个HDK都必要的3个头文件以及自身的头文件
#include "SOP_TestXform.h"
#include <OP/OP_AutoLockInputs.h> //必要
#include <OP/OP_OperatorTable.h> //必要
#include <UT/UT_DSOVersion.h> //必要
#include <UT/UT_Vector3.h> //我们需要用到点的位置属性,需要包含此头文件
接着是void newSopOperator(OP_OperatorTable* table) 函数,它被声明于SOP_Node,表示我们要开始new一个SOP的节点了哦~
void newSopOperator(OP_OperatorTable* table)
{
table->addOperator(new OP_Operator(
"hdk_testxform", //真正的operator name
"HDK Test Transform", //operator label
SOP_TestXform::constructor, //HDK的规则
SOP_TestXform::myTemplateList, //参数列表
1, //最小输入数量
1, //最大输入数量
0)); //CH_LocalVariable 我暂时也没用上
}
接下来定义参数distance
static PRM_Name names[] = {
// name label
// ↓ ↓
PRM_Name("distance", "Transform Distance"), //PRM表示parameter
};
参数定义好了,接下来当然是参数列表啦
PRM_Template SOP_TestXform::myTemplateList[] = {
//参数类型是float,参数个数为1个(如果是3则表示向量vector),名字为我们刚刚定义的PRM_Name
PRM_Template(PRM_FLT_J, 1, &names[0]),
PRM_Template(), //默认规则,缺少这行空构造直接报错
};
接下来是HDK默认写法,遵守就好,如果有需要可以自行拓展。
OP_Node* SOP_TestXform::constructor(OP_Network* net, const char* name, OP_Operator* op)
{
return new SOP_TestXform(net, name, op);
}
SOP_TestXform::SOP_TestXform(OP_Network* net, const char* name, OP_Operator* op)
:SOP_Node(net, name, op)
{
mySopFlags.setManagesDataIDs(true);
}
SOP_TestXform::~SOP_TestXform() {};
最后是重头戏,cookMySop核心逻辑实现
OP_ERROR SOP_TestXform::cookMySop(OP_Context& context)
{
//默认锁定我们的输入端信息。
//---------------------
OP_AutoLockInputs inputs(this);
if (inputs.lock(context) >= UT_ERROR_ABORT)
return error();
duplicatePointSource(0, context);
//---------------------
//用t接收当前的帧数,disntance接收参数值
//---------------------
fpreal t = context.getTime();
float distance = DISTANCE(t);
//-----------------------------
//表示element在attribute array中的偏移量,相当于索引
GA_Offset ptoff;
//相当于vex的point层级操作
//gdp表示geometry detail pointer,也就是当前节点所拥有的几何体信息的指针
//通过gdp指针我们可以得到所有当前节点的几何体信息,包括point, prim, vertex的位置,法线等
GA_FOR_ALL_PTOFF(gdp, ptoff)
{
//得到每一个点的当前位置
UT_Vector3 pos = gdp->getPos3(ptoff);
//每个点的y轴加上参数distance的值
pos.y() += distance;
//最后通过gdp指针修改点的位置
gdp->setPos3(ptoff, pos);
}
//通过gdp指针绑定所有修改的信息,这一步必不可少
gdp->bumpAllDataIds();
return error();
}
至此,头文件和cpp文件都写好了,我们使用houdini命令行编译该文件
果然,dso文件夹中也生成了dll文件
接下来重启houdini试试我们的hdk吧!
不出所料,达到了我们想要的效果,那么至此本片文章的目的已经达到,感谢观看!