houdini HDK开发5——创建一个自定义节点

本文内容来自于jurajtomori 的 CREATING A SIMPLE C++ OPENVDB NODE IN HDK

一、节点功能

基于输入点激活VDB中的体素。

二、代码编写

头文件(vdb_activate_from_points.h)

头文件包含类(节点)的定义和用于构造(myConstructor()、SOP_VdbActivateFromPoints()、~SOP_VdbActivateFromPoints()、UI参数(myTemplateList[])、输入标记(inputLabel()的函数。执行实际几何处理的函数cookMySop()。这里还设置了一个DEBUG()函数,它将在node的UI中计算调试参数(最后一个参数可以是计算的帧数,但是我们不太可能激活调试选项)。

#ifndef __SOP_vdb_activate_from_points_h__
#define __SOP_vdb_activate_from_points_h__

#include <SOP/SOP_Node.h>

namespace VdbActivateFromPoints {
    
    
	class SOP_VdbActivateFromPoints : public SOP_Node
	{
    
    
	public:
		// HDK的节点构造函数
		static OP_Node *myConstructor(OP_Network*, const char *, OP_Operator *);

		// Houdini UI的参数数组
		static PRM_Template myTemplateList[];

	protected:
		// 构造函数与析构函数
		SOP_VdbActivateFromPoints(OP_Network *net, const char *name, OP_Operator *op);

		virtual ~SOP_VdbActivateFromPoints();

		// 在Houdini UI中标记节点输入
		virtual const char *inputLabel(unsigned idx) const;

		// 节点的主要功能函数
		virtual OP_ERROR cookMySop(OP_Context &context);

	private:
		// 帮助函数访问节点的参数值
		int DEBUG() {
    
     return evalInt("debug", 0, 0); }

	};
}

#endif


源文件(vdb_activate_from_points.C)


//加入相关头文件,然后是访问Houdini功能的头文件,最后是Houdini附带的OpenVDB头文件。

#include "vdb_activate_from_points.h"
#include <limits.h>
#include <SYS/SYS_Math.h>
#include <UT/UT_DSOVersion.h>
#include <UT/UT_Interrupt.h>
#include <OP/OP_Operator.h>
#include <OP/OP_OperatorTable.h>
#include <GU/GU_Detail.h>
#include <GEO/GEO_PrimPoly.h>
#include <PRM/PRM_Include.h>
#include <CH/CH_LocalVariable.h>
#include <OP/OP_AutoLockInputs.h>
#include <GU/GU_PrimVDB.h>
#include <openvdb/openvdb.h>


using namespace VdbActivateFromPoints;
//注册节点并标记节点输入(可以在网络编辑器中看到)。newSopOperator()是
//Houdini要找的函数。它的目的是将我们的节点添加到Houdini的操作表中。它是必需的

void newSopOperator(OP_OperatorTable *table)
{
    
    
    OP_Operator *op;

	op = new OP_Operator(
    		"vdbActivateFromPoints",                      // 内部名称
    		"VDB Activate from Points",                   // UI名称
    		SOP_VdbActivateFromPoints::myConstructor,     // 如何构建节点——一个构造这种类型节点的函数
    		SOP_VdbActivateFromPoints::myTemplateList,    // 参数列表
    		2,                                            // 最小输入
    		2);                                           // 最大输入
    
    // 将这个节点放在Tab键下的VDB submenu菜单下
    op->setOpTabSubMenuPath("VDB");

    // 执行addOperator()之后,'table'将获得'op'的所有权
    table->addOperator(op);
}

//标节点输入,0对应第一个输入,1对应第二个输入
const char *
SOP_VdbActivateFromPoints::inputLabel(unsigned idx) const
{
    
    
    switch (idx){
    
    
        case 0: return "VDB";
        case 1: return "Points where active voxels should be";
        default: return "default";
    }
}


//建立参数用户界面:你可以参考所有可用的文档;参数类型(浮点型,整型,复选框,
//菜单)。创建了PRM Name对象之后,我们需要将它们分配给PRM模板数组,Houdini将
//使用该数组在UI中显示参数。注意,即使我们希望没有参数,我们仍然需要在数组中
//包含一个空的PRM模板()。

// 为调试选项定义参数
static PRM_Name debugPRM("debug", "Print debug information"); // 内部名称,ui名称

// 将参数赋给接口,它是PRM_Template对象的数组
PRM_Template SOP_VdbActivateFromPoints::myTemplateList[] = 
{
    
    
    PRM_Template(PRM_TOGGLE, 1, &debugPRM, PRMzeroDefaults), 
    // 类型(复选框),大小(在我们的例子中是1,但是rgb/xyz值需要3),指向描述参数名的PRM_Name的指针,默认值(0 -disable)
    PRM_Template() // 最后需要有一个空的 PRM_Template object
};

// 构造函数,析构函数,通常这里不需要修改任何东西,构造函数的工作是确保节点被放到正确的网络中
OP_Node * 
SOP_VdbActivateFromPoints::myConstructor(OP_Network *net, const char *name, OP_Operator *op)
{
    
    
    return new SOP_VdbActivateFromPoints(net, name, op);
}

SOP_VdbActivateFromPoints::SOP_VdbActivateFromPoints(OP_Network *net, const char *name, OP_Operator *op) : SOP_Node(net, name, op) {
    
    }

SOP_VdbActivateFromPoints::~SOP_VdbActivateFromPoints() {
    
    }



// 完成实际工作的函数
OP_ERROR
SOP_VdbActivateFromPoints::cookMySop(OP_Context &context)
{
    
    
    // 我们必须锁定我们的输入,然后访问其的几何图形,当我们返回时,
    // OP_AutoLockInputs将自动解锁我们的输入
    OP_AutoLockInputs inputs(this);
    if (inputs.lock(context) >= UT_ERROR_ABORT)
        return error();

    // 复制传入的几何体
    duplicateSource(0, context);

    // 检查中断-中断作用域在“进程”被销毁时自动关闭。
    UT_AutoInterrupt progress("Activating voxels...");

    // 从第二个输入获取指向几何图形的指针
    const GU_Detail *points = inputGeo(1);

    // 检查是否启用调试参数,在头文件中定义debug()函数
    if (DEBUG())
    {
    
    
        std::cout << "number of points: " << points->getNumPoints() << std::endl;
    }

    GEO_PrimVDB* vdbPrim = NULL; // 指向vdb原语的空指针

    //遍历所有传入原语并找到第一个原语是VDB
    for (GA_Iterator it(gdp->getPrimitiveRange()); !it.atEnd(); it.advance())
    {
    
    
        GEO_Primitive* prim = gdp->getGEOPrimitive(it.getOffset());
        if(dynamic_cast<const GEO_PrimVDB *>(prim))
        {
    
    
            vdbPrim = dynamic_cast<GEO_PrimVDB *>(prim);
            break;
        }
    }

    // 如果不是VDB,则终止
    if(!vdbPrim)
    {
    
    
        addError(SOP_MESSAGE, "First input must contain a VDB!");
        return error();
    }
	//默认情况下,Houdini中不同节点中的volume primitives共享相同的volume 
	//tree(用于内存优化),这将确保我们有自己的volume tree的深度拷贝,并且
	//我们可以将它写入
    
    vdbPrim->makeGridUnique();
    
    // 获取网格基指针并将其转换为浮动网格指针
    openvdb::GridBase::Ptr vdbPtrBase = vdbPrim->getGridPtr();
    openvdb::FloatGrid::Ptr vdbPtr = openvdb::gridPtrCast<openvdb::FloatGrid>(vdbPtrBase);

    // 获取浮动网格的访问器
    openvdb::FloatGrid::Accessor vdb_access = vdbPtr->getAccessor();

    // 获取网格转换的参考
    const openvdb::math::Transform &vdbGridXform = vdbPtr->transform();

    // 通过句柄遍历所有的点
    int i = 0;
    GA_ROHandleV3 Phandle(points->findAttribute(GA_ATTRIB_POINT, "P")); // 只读属性的句柄
    GA_Offset ptoff;
    GA_FOR_ALL_PTOFF(points, ptoff)
    {
    
    
        // 测试用户是否请求中止
        if (progress.wasInterrupted())
            return error();

        // 获取当前的pont位置
        UT_Vector3 Pvalue = Phandle.get(ptoff);

        // 使用来自houdini的向量的值创建openvdb向量,将它从世界空间转换为
        //vdb的索引空间(基于vdb的转换),并在点位置激活体素
        openvdb::Vec3R p_( Pvalue[0], Pvalue[1], Pvalue[2] );
        openvdb::Coord p_xformed( vdbGridXform.worldToIndexCellCentered(p_) );
        vdb_access.setValueOn( p_xformed );
        
        if (DEBUG())
        {
    
    
            std::cout << i << ". point world space position: " << Pvalue << std::endl;
            std::cout << "  volmue index space position: " << p_xformed << std::endl;
        }

        i++;
    }

    return error();
}

三、编译输出,并在houdini里面创建该节点

在这里插入图片描述

源文件下载地址原作者github仓库

猜你喜欢

转载自blog.csdn.net/peixin_huang/article/details/104094384