栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

尝试建立一个可运行HAPI(HoudiniEngine API)的独立C++程序

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

尝试建立一个可运行HAPI(HoudiniEngine API)的独立C++程序

目标

建立一个可运行HAPI的独立C++程序。(不需要启动UE引擎,但是借用了一些官方插件的代码)

官方插件加载HAPI的细节

可以看到在官方插件的模块启动函数中有加载HAPI的相关逻辑:

其中 HAPILibraryHandle是读取到的 dll 的 handle,路径就是Houdini安装目录中的binlibHAPIL.dll:

随后调用了FHoudiniApi::InitializeHAPI来得到每个函数的地址。

如果打开HoudiniApi.h/cpp文件,能看到大量针对每个接口函数的代码。(代码的结构可参阅本篇附录)

步骤0. 创建一个独立的C++工程

以 “控制台应用” 为模板即可。

步骤1. 拷贝HAPI头文件

HAPI头文件的文件夹在Houdini安装目录toolkitincludeHAPI,将这个文件夹拷贝到工程里来。

步骤2. 加载HAPI的DLL

首先要切换为64位来和DLL匹配:

这里有个小插曲是,我加载DLL一直失败,地址是0,而返回的错误代码是193。最后在《VS2015 LoadLibrary加载DLL失败的解决方案,GetLastError()返回值193》中才注意到问题是,应该切换为64位。

加载DLL用的函数为LoadLibrary(需要 #include )。
随后,我还尝试从DLL中读取了一个函数,然后打印其值,看是否是0。

#include 
#include 
#include "HAPI/HAPI.h"

int main()
{
	//Houdini安装目录:
	const std::wstring HoudiniPath = L"C:/Program Files/Side Effects Software/Houdini 19.0.383";
	//HAPI的DLL的路径:
	const std::wstring HAPIDllPath = L"/bin/libHAPIL.dll";

	//加载HAPI的DLL:
	HINSTANCE HAPILibraryHandle = LoadLibrary((HoudiniPath + HAPIDllPath).c_str());

	//定义AddAttribute函数的类型
	typedef HAPI_Result(*AddAttributeFuncPtr)(const HAPI_Session* session, HAPI_NodeId node_id, HAPI_PartId part_id, const char* name, const HAPI_AttributeInfo* attr_info);
	//从DLL中加载AddAttribute函数
	AddAttributeFuncPtr AddAttribute = (AddAttributeFuncPtr)GetProcAddress(HAPILibraryHandle, "HAPI_AddAttribute");
	//测试打印,看是否是非0值
	std::cout << AddAttribute << std::endl;
	
	//释放DLL
	FreeLibrary(HAPILibraryHandle);
	
	return 0;
}

结果非0,说明读取成功了:

步骤3. 借用官方插件的 HoudiniApi.h/cpp 来读取所有接口函数

上面的代码只读取了一个函数。但是HAPI里有相当多的函数,每个函数都写一遍显然太慢了。
因此,可以借用官方插件的 HoudiniApi.h/cpp 代码(他们其实也是自动生成的)。

下面,将 HoudiniApi.h/cpp 拷贝进工程,然后需要做一些小小的改动:

对于 HoudiniApi.h,要去掉不再需要的头文件 “HAL/PlatformProcess.h”,以及宏 HOUDINIENGINE_API。

对于 HoudiniApi.cpp,去掉头文件 “HoudiniEnginePrivatePCH.h”,但是补上

另外,为了编译通过,需要补上一些宏以及函数的实现,代码如下:

#define TEXT(x) x

struct FPlatformProcess
{
	static void* GetDllExport(void* DllHandle, const char* ProcName)
	{
		return GetProcAddress((HMODULE)DllHandle, ProcName);
	}
};

//这个函数名字和已有的宏冲突了,因此undef
#undef GetGeoInfo

这样,编译就可以通过了。

步骤4. 测试使用HAPI

所有准备已经完成,下面就可以测试用一下HAPI了,代码如下:

#include 
#include 
#include "HAPI/HAPI.h"
#include "HoudiniApi.h"

int main()
{
	//Houdini安装目录:
	const std::wstring HoudiniPath = L"C:/Program Files/Side Effects Software/Houdini 19.0.383";
	//HAPI的DLL的路径:
	const std::wstring HAPIDllPath = L"/bin/libHAPIL.dll";

	//加载HAPI的DLL:
	HINSTANCE HAPILibraryHandle = LoadLibrary((HoudiniPath + HAPIDllPath).c_str());

	//加载所有HAPI接口函数
	FHoudiniApi::InitializeHAPI(HAPILibraryHandle);

	//一段测试
	{
		//创建session
		HAPI_Session session;
		FHoudiniApi::CreateInProcessSession(&session);
		//初始化session
		HAPI_CookOptions options;
		FHoudiniApi::Initialize(&session, &options, false, -1, "", "", "", "", "");

		//得到obj节点
		HAPI_NodeId ObjNode = -1;
		FHoudiniApi::GetManagerNodeId(&session, HAPI_NodeType::HAPI_NODETYPE_OBJ, &ObjNode);

		//创建Geo节点
		HAPI_NodeId Geonode = -1;
		FHoudiniApi::CreateNode(&session, ObjNode, "geo", "MyGeo", true, &GeoNode);

		//创建一个测试用节点
		HAPI_NodeId TestNode = -1;
		FHoudiniApi::CreateNode(&session, GeoNode, "platonic", "MyTest", true, &TestNode);
		//设置其为二十面体
		FHoudiniApi::SetParmIntValue(&session, TestNode, "type", 0, 3);

		//测试输出到bgeo中
		FHoudiniApi::SaveGeoToFile(&session, TestNode, "D:/Temp/TestGeo.bgeo");

		//清理并关闭Session
		FHoudiniApi::Cleanup(&session);
		FHoudiniApi::CloseSession(&session);
	}
	
	//释放DLL
	FreeLibrary(HAPILibraryHandle);
	
	return 0;
}

在上面的测试代码中,我创建了一个测试用的二十面体节点,然后将其输出为bgeo文件:

将其使用 GPlay(应该是bgeo格式默认的打开方式)打开后可以看到:

看来HAPI成功运行了。

遗留问题

虽然上面代码成功运行了,但是在退出的时候还会有报错:

待之后研究。

延伸讨论:实际需要的dll

在上面代码中,虽然看起来只加载了一个dll,但是实际用到的dll非常多,可以在输出窗口看到他们:

附录:官方插件的 HoudiniApi.h/cpp 代码的结构

省略掉针对每个接口函数的形式重复的代码后,他们的结构实际上很简单:

HoudiniApi.h:

struct HOUDINIENGINE_API FHoudiniApi
{
public:

	static void InitializeHAPI(void* LibraryHandle);
	static void FinalizeHAPI();
	static bool IsHAPIInitialized();

public:

	typedef HAPI_Result (*AddAttributeFuncPtr)(const HAPI_Session * session, HAPI_NodeId node_id, HAPI_PartId part_id, const char * name, const HAPI_AttributeInfo * attr_info);
	typedef HAPI_Result (*AddGroupFuncPtr)(const HAPI_Session * session, HAPI_NodeId node_id, HAPI_PartId part_id, HAPI_GroupType group_type, const char * group_name);
	...

public:

	static AddAttributeFuncPtr AddAttribute;
	static AddGroupFuncPtr AddGroup;
	...
public:

	static HAPI_Result AddAttributeEmptyStub(const HAPI_Session * session, HAPI_NodeId node_id, HAPI_PartId part_id, const char * name, const HAPI_AttributeInfo * attr_info);
	static HAPI_Result AddGroupEmptyStub(const HAPI_Session * session, HAPI_NodeId node_id, HAPI_PartId part_id, HAPI_GroupType group_type, const char * group_name);
	...
};

HoudiniApi.cpp:

FHoudiniApi::AddAttributeFuncPtr
FHoudiniApi::AddAttribute = &FHoudiniApi::AddAttributeEmptyStub;

FHoudiniApi::AddGroupFuncPtr
FHoudiniApi::AddGroup = &FHoudiniApi::AddGroupEmptyStub;

...


void
FHoudiniApi::InitializeHAPI(void* LibraryHandle)
{
	if(!LibraryHandle) return;

	FHoudiniApi::AddAttribute = (AddAttributeFuncPtr) FPlatformProcess::GetDllExport(LibraryHandle, TEXT("HAPI_AddAttribute"));
	FHoudiniApi::AddGroup = (AddGroupFuncPtr) FPlatformProcess::GetDllExport(LibraryHandle, TEXT("HAPI_AddGroup"));
	...
}


void
FHoudiniApi::FinalizeHAPI()
{
	FHoudiniApi::AddAttribute = &FHoudiniApi::AddAttributeEmptyStub;
	FHoudiniApi::AddGroup = &FHoudiniApi::AddGroupEmptyStub;
	...
}


bool
FHoudiniApi::IsHAPIInitialized()
{
	return ( FHoudiniApi::IsInitialized != &FHoudiniApi::IsInitializedEmptyStub );
}


HAPI_Result
FHoudiniApi::AddAttributeEmptyStub(const HAPI_Session * session, HAPI_NodeId node_id, HAPI_PartId part_id, const char * name, const HAPI_AttributeInfo * attr_info)
{
	return HAPI_RESULT_FAILURE;
}


HAPI_Result
FHoudiniApi::AddGroupEmptyStub(const HAPI_Session * session, HAPI_NodeId node_id, HAPI_PartId part_id, HAPI_GroupType group_type, const char * group_name)
{
	return HAPI_RESULT_FAILURE;
}

...

可以看到:

函数名是函数指针,其类型是函数名FuncPtr。函数名FuncPtr定义了函数的参数和返回值类型,函数名EmptyStub是空实现,如果没有正常加载到HAPI实际的函数,则默认就是空实现。在InitializeHAPI中,每个函数名通过名字加载到DLL中的函数。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/779189.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号