Unity Houdini插件编写data Table传递数据给Houdini
自定义输入接口里面处理DataTable,将每个列表项转换成点的属性。
上次说到Unity Houdini插件将Tag设为group,里面用到了自定义输入接口。然后那个Houdini教程又给我出难题了,unreal 可以用一种叫data Table的数据结构来完成向Houdini结构化数据的传递(链接),我没找到Unity类似的功能,又得自己写。用chatGPT的话,写起来还是挺容易的。
定义带有数据对象列表的组件
首先得有一个在编辑器里定义结构数据的组件,用于定义数据结构。
public abstract class HEU_PDE_DataTable<T> : MonoBehaviour {
public List<T> dataTable;
}
using UnityEngine;
using System.Collections.Generic;
[System.Serializable]
public struct TileObject {
public string name;
public GameObject gameObject;
public float height;
public int num;
public Vector3 N;
public Color Cd;
}
public class HEU_PDE_TileObject : HEU_PDE_DataTable<TileObject> {}
这里使用了泛型,定义了一个抽象的组件,带有一个List属性,其子类可以定义dataTable的数据结构。
效果如下
接着把这个GameObject传递到Houdini的HDA的属性或输入里。
Houdini定义HDA的参数:
这里使用了object merge节点,将Object抽取成HDA的参数。
然后在自定义输入接口里面处理DataTable,将每个列表项转换成点的属性。
编辑自定义输入接口
首先更改IsThisInputObjectSupported函数,加入对DataTable的支持
private bool isDataTable(GameObject gameObject) {
// judge if it is a subclass of the HEU_PDE_DataTable
MonoBehaviour[] components = gameObject.GetComponents<MonoBehaviour>();
Type baseGenericType = typeof(HEU_PDE_DataTable<>);
foreach (MonoBehaviour component in components) {
Type componentType = component.GetType();
Type baseType = componentType.BaseType;
if (baseType != null && baseType.IsGenericType && baseType.GetGenericTypeDefinition() == baseGenericType) {
return true;
}
}
return false;
}
public override bool IsThisInputObjectSupported(GameObject inputObject) {
if (_inputInterfaceMesh.IsThisInputObjectSupported(inputObject)) return true;
if (isDataTable(inputObject)) return true;
return false;
}
就是遍历对象的组件,用反射获取其父类,判断父类是不是DataTable。
接着在CreateInputNodeWithDataUpload中遍历DataTable,转换成点属性即可
关键代码是
switch (fieldType.Name) {
case nameof(Double):
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName,
1, (values.Cast<double>().Select(v => (float)v)).ToArray(), ref partInfo)) {
continue;
}
break;
case nameof(System.Single):
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName, 1,
(values.Cast<float>()).ToArray(), ref partInfo)) {
continue;
}
break;
case nameof(Boolean):
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName, 1,
(values.Cast<bool>().Select(v => new Vector3Int(v ? 1 : 0, 0, 0))).ToArray(), ref partInfo)) {
continue;
}
break;
case nameof(Int32):
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName, 1,
(values.Select(v => new Vector3Int((int)v, 0, 0))).ToArray(), ref partInfo)) {
continue;
}
break;
case nameof(Color):
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName, 3,
(values.Cast<Color>().Select(v => new Vector3(v.r, v.g, v.b))).ToArray(), ref partInfo, false)) {
continue;
}
break;
case nameof(Vector3):
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName,
3, (values.Cast<Vector3>()).ToArray(), ref partInfo, false)) {
continue;
}
break;
case nameof(String):
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName,
(values.Cast<string>()).ToArray(), ref partInfo)) {
continue;
}
break;
case nameof(GameObject):
// Convert to resource file path
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName,
(values.Select(v => (AssetDatabase.GetAssetPath((GameObject)v)))).ToArray(), ref partInfo)) {
continue;
}
break;
}
HEU_Logger.LogError(string.Format("Failed to set attribute {0}!", fieldName));
return false;
根据不同的C#类型转成对应的点属性
效果:
全部代码
HEU_PDE_DataTable.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class HEU_PDE_DataTable<T> : MonoBehaviour
{
public List<T> dataTable;
}
HEU_PDE_TileObject.cs
using UnityEngine;
using System.Collections.Generic;
[System.Serializable]
public struct TileObject {
public string name;
public GameObject gameObject;
public float height;
public int num;
public Vector3 N;
public Color Cd;
}
public class HEU_PDE_TileObject : HEU_PDE_DataTable<TileObject> {}
HEU_PDE_InputInterfaceMesh.cs
using HoudiniEngineUnity;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using TMPro;
using UnityEditor;
using UnityEngine;
using static UnityEditor.Searcher.SearcherWindow.Alignment;
public class HEU_PDE_InputInterfaceMesh : HEU_InputInterface {
#if UNITY_EDITOR
[InitializeOnLoadMethod]
[UnityEditor.Callbacks.DidReloadScripts]
private static void OnScriptsReloaded() {
HEU_PDE_InputInterfaceMesh inputInterface = new HEU_PDE_InputInterfaceMesh();
HEU_InputUtility.RegisterInputInterface(inputInterface);
}
#endif
private readonly HEU_InputInterfaceMesh _inputInterfaceMesh;
public HEU_PDE_InputInterfaceMesh() : base(DEFAULT_PRIORITY + 100)// higher priority
{
// reflect to get instance of HEU_InputInterfaceMesh
Type type = typeof(HEU_InputInterfaceMesh);
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
ConstructorInfo privateConstructor = type.GetConstructor(bindingFlags, null, new Type[0], null);
_inputInterfaceMesh = (HEU_InputInterfaceMesh)privateConstructor.Invoke(null);
}
public override bool CreateInputNodeWithDataUpload(HEU_SessionBase session, int connectNodeID, GameObject inputObject, out int inputNodeID) {
if (isDataTable(inputObject)) {
MonoBehaviour[] components = inputObject.GetComponents<MonoBehaviour>();
Type baseGenericType = typeof(HEU_PDE_DataTable<>);
inputNodeID = HEU_Defines.HEU_INVALID_NODE_ID;
int mergeNodeId = HEU_Defines.HEU_INVALID_NODE_ID;
HAPI_PartInfo partInfo = new HAPI_PartInfo();
string inputName = null;
int newNodeID = HEU_Defines.HEU_INVALID_NODE_ID;
session.CreateInputNode(out newNodeID, inputName);
if (newNodeID == HEU_Defines.HEU_INVALID_NODE_ID || !HEU_HAPIUtility.IsNodeValidInHoudini(session, newNodeID)) {
HEU_Logger.LogError("Failed to create new input node in Houdini session!");
return false;
}
inputNodeID = newNodeID;
foreach (MonoBehaviour component in components) {
Type componentType = component.GetType();
Type baseType = componentType.BaseType;
if (baseType != null && baseType.IsGenericType && baseType.GetGenericTypeDefinition() == baseGenericType) {
// Get the generic argument type
Type dataListType = baseType.GetGenericArguments()[0];
// Iterate through the properties of the generic argument type
FieldInfo dataTableInfo = componentType.GetField("dataTable");
IList dataTable = (IList)dataTableInfo.GetValue(component);
FieldInfo[] fields = dataListType.GetFields();
partInfo.pointCount = dataTable.Count;
if (!session.SetPartInfo(inputNodeID, 0, ref partInfo)) {
HEU_Logger.LogError("Failed to set input part info. ");
return false;
}
if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, HEU_HAPIConstants.HAPI_ATTRIB_POSITION,
3, Enumerable.Repeat(new Vector3(0, 0, 0), partInfo.pointCount).ToArray(), ref partInfo, true)) {
HEU_Logger.LogError("Failed to set input geometry position.");
return false;
}
partInfo.pointAttributeCount = fields.Length;
foreach (FieldInfo field in fields) {
Type fieldType = field.FieldType;
string fieldName = field.Name;
List<object> values = new();
for (int i = 0; i < partInfo.pointCount; i++) {
object fieldValue = field.GetValue(dataTable[i]);
values.Add(fieldValue);
}
// Handle available types
switch (fieldType.Name) {
case nameof(Double):
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName,
1, (values.Cast<double>().Select(v => (float)v)).ToArray(), ref partInfo)) {
continue;
}
break;
case nameof(System.Single):
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName, 1,
(values.Cast<float>()).ToArray(), ref partInfo)) {
continue;
}
break;
case nameof(Boolean):
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName, 1,
(values.Cast<bool>().Select(v => new Vector3Int(v ? 1 : 0, 0, 0))).ToArray(), ref partInfo)) {
continue;
}
break;
case nameof(Int32):
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName, 1,
(values.Select(v => new Vector3Int((int)v, 0, 0))).ToArray(), ref partInfo)) {
continue;
}
break;
case nameof(Color):
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName, 3,
(values.Cast<Color>().Select(v => new Vector3(v.r, v.g, v.b))).ToArray(), ref partInfo, false)) {
continue;
}
break;
case nameof(Vector3):
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName,
3, (values.Cast<Vector3>()).ToArray(), ref partInfo, false)) {
continue;
}
break;
case nameof(String):
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName,
(values.Cast<string>()).ToArray(), ref partInfo)) {
continue;
}
break;
case nameof(GameObject):
// Convert to resource file path
if (HEU_InputMeshUtility.SetMeshPointAttribute(session, inputNodeID, 0, fieldName,
(values.Select(v => (AssetDatabase.GetAssetPath((GameObject)v)))).ToArray(), ref partInfo)) {
continue;
}
break;
}
HEU_Logger.LogError(string.Format("Failed to set attribute {0}!", fieldName));
return false;
}
break;
}
}
if (!session.CommitGeo(inputNodeID)) {
HEU_Logger.LogError("Filed to commit geo!");
return false;
}
int parentId = HEU_HAPIUtility.GetParentNodeID(session, newNodeID);
if (!session.CreateNode(parentId, "merge", null, false, out mergeNodeId)) {
HEU_Logger.LogErrorFormat("Unable to create merge SOP node for connecting input assets.");
return false;
}
if (!session.ConnectNodeInput(mergeNodeId, 0, newNodeID)) {
HEU_Logger.LogErrorFormat("Unable to connect to input node!");
return false;
}
if (!session.SetNodeDisplay(mergeNodeId, 1)) {
HEU_Logger.LogWarningFormat("Unable to set display flag!");
}
inputNodeID = mergeNodeId;
}
else {
bool bRes = _inputInterfaceMesh.CreateInputNodeWithDataUpload(session, connectNodeID, inputObject, out inputNodeID);
if (!bRes) return false;
//SetDetailStringAttribute(session, inputNodeID, "unity_tag",inputObject.tag);
String groupName = inputObject.tag;
SetPointGroup(session, inputNodeID, groupName);
if (!session.CommitGeo(inputNodeID)) {
HEU_Logger.LogError("Filed to commit geo!");
return false;
}
}
if (!session.CookNode(inputNodeID, false)) {
HEU_Logger.LogError("New input node failed to cook!");
return false;
}
return true;
}
private bool SetDetailStringAttribute(HEU_SessionBase session, int inputNodeID, string attributeName, string attributeValue) {
HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo();
attrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL;
attrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_STRING;
attrInfo.count = 1;
attrInfo.tupleSize = 1;
if (!session.AddAttribute(inputNodeID, 0, attributeName, ref attrInfo)
|| !session.SetAttributeStringData(inputNodeID, 0, attributeName, ref attrInfo, new String[] { attributeValue }, 0, 1)) {
HEU_Logger.LogError("Failed to add detail attribute.");
return false;
}
return true;
}
private bool SetPointGroup(HEU_SessionBase session, int inputNodeID, string groupName) {
HAPI_GeoInfo geoInfo = new HAPI_GeoInfo();
if (session.GetGeoInfo(inputNodeID, ref geoInfo)) {
for (int i = 0; i < geoInfo.partCount; i++) {
HAPI_PartInfo partInfo = new HAPI_PartInfo();
session.GetPartInfo(inputNodeID, i, ref partInfo);
session.AddGroup(inputNodeID, i, HAPI_GroupType.HAPI_GROUPTYPE_POINT, groupName);
int pointCount = partInfo.pointCount;
int[] membership = new int[pointCount];
for (int j = 0; j < pointCount; j++) {
membership[j] = 1;
}
session.SetGroupMembership(inputNodeID, i, HAPI_GroupType.HAPI_GROUPTYPE_POINT, groupName, membership, 0, pointCount);
}
}
else return false;
return true;
}
private bool isDataTable(GameObject gameObject) {
// judge if it is a subclass of the HEU_PDE_DataTable
MonoBehaviour[] components = gameObject.GetComponents<MonoBehaviour>();
Type baseGenericType = typeof(HEU_PDE_DataTable<>);
foreach (MonoBehaviour component in components) {
Type componentType = component.GetType();
Type baseType = componentType.BaseType;
if (baseType != null && baseType.IsGenericType && baseType.GetGenericTypeDefinition() == baseGenericType) {
return true;
}
}
return false;
}
public override bool IsThisInputObjectSupported(GameObject inputObject) {
if (_inputInterfaceMesh.IsThisInputObjectSupported(inputObject)) return true;
if (isDataTable(inputObject)) return true;
return false;
}
}

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)