public override bool CreateInputNodeWithDataUpload(HEU_SessionBase session, int connectNodeID, GameObject inputObject, out int inputNodeID) { inputNodeID = HEU_Defines.HEU_INVALID_NODE_ID; if (!HEU_HAPIUtility.IsNodeValidInHoudini(session, connectNodeID)) { Debug.LogError("Connection node is invalid."); return false; } HEU_InputDataTilemap inputTilemap = GenerateTilemapDataFromGameObject(inputObject); string inputName = null; HAPI_NodeId 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)) { Debug.LogError("Failed to create new input node in Houdini session!"); return false; } inputNodeID = newNodeID; if (!session.CookNode(inputNodeID, false)) { Debug.LogError("New input node failed to cook!"); return false; } return UploadData(session, inputNodeID, inputTilemap); }
/// <summary> /// Create the main heightfield network for input. /// </summary> /// <param name="session"></param> /// <param name="idt"></param> /// <returns>True if successfully created the network</returns> public bool CreateHeightFieldInputNode(HEU_SessionBase session, HEU_InputDataTerrain idt) { idt._heightfieldNodeID = HEU_Defines.HEU_INVALID_NODE_ID; idt._heightNodeID = HEU_Defines.HEU_INVALID_NODE_ID; idt._maskNodeID = HEU_Defines.HEU_INVALID_NODE_ID; idt._mergeNodeID = HEU_Defines.HEU_INVALID_NODE_ID; // Create the HeightField node network bool bResult = session.CreateHeightfieldInputNode(idt._parentNodeID, idt._heightFieldName, idt._numPointsX, idt._numPointsY, idt._voxelSize, out idt._heightfieldNodeID, out idt._heightNodeID, out idt._maskNodeID, out idt._mergeNodeID); if (!bResult || idt._heightfieldNodeID == HEU_Defines.HEU_INVALID_NODE_ID || idt._heightNodeID == HEU_Defines.HEU_INVALID_NODE_ID || idt._maskNodeID == HEU_Defines.HEU_INVALID_NODE_ID || idt._mergeNodeID == HEU_Defines.HEU_INVALID_NODE_ID) { Debug.LogError("Failed to create new heightfield node in Houdini session!"); return false; } if (!session.CookNode(idt._heightNodeID, false)) { Debug.LogError("New input node failed to cook!"); return false; } return true; }
protected virtual bool CookNode(HEU_SessionBase session, HAPI_NodeId cookNodeID) { // Cooking it will load the bgeo if (!session.CookNode(cookNodeID, false)) { AppendLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Unable to cook node.")); return false; } // Wait until cooking has finished bool bResult = true; HAPI_State statusCode = HAPI_State.HAPI_STATE_STARTING_LOAD; while (bResult && statusCode > HAPI_State.HAPI_STATE_MAX_READY_STATE) { bResult = session.GetCookState(out statusCode); Sleep(); } // Check cook results for any errors if (statusCode == HAPI_State.HAPI_STATE_READY_WITH_COOK_ERRORS || statusCode == HAPI_State.HAPI_STATE_READY_WITH_FATAL_ERRORS) { string statusString = session.GetStatusString(HAPI_StatusType.HAPI_STATUS_COOK_RESULT, HAPI_StatusVerbosity.HAPI_STATUSVERBOSITY_ERRORS); AppendLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Cook failed: {0}.", statusString)); return false; } return true; }
/// <summary> /// Create the main heightfield network for input. /// </summary> /// <param name="session"></param> /// <param name="idt"></param> /// <returns>True if successfully created the network</returns> public bool CreateHeightFieldInputNode(HEU_SessionBase session, HEU_InputDataTerrain idt) { idt._heightfieldNodeID = HEU_Defines.HEU_INVALID_NODE_ID; idt._heightNodeID = HEU_Defines.HEU_INVALID_NODE_ID; idt._maskNodeID = HEU_Defines.HEU_INVALID_NODE_ID; idt._mergeNodeID = HEU_Defines.HEU_INVALID_NODE_ID; idt._parentNodeID = HEU_Defines.HEU_INVALID_NODE_ID; // Heightfields should have its own objects. // Create the HeightField node network bool bResult = session.CreateHeightFieldInput(idt._parentNodeID, idt._heightFieldName, idt._numPointsX, idt._numPointsY, idt._voxelSize, HAPI_HeightFieldSampling.HAPI_HEIGHTFIELD_SAMPLING_CORNER, out idt._heightfieldNodeID, out idt._heightNodeID, out idt._maskNodeID, out idt._mergeNodeID); if (!bResult || idt._heightfieldNodeID == HEU_Defines.HEU_INVALID_NODE_ID || idt._heightNodeID == HEU_Defines.HEU_INVALID_NODE_ID || idt._maskNodeID == HEU_Defines.HEU_INVALID_NODE_ID || idt._mergeNodeID == HEU_Defines.HEU_INVALID_NODE_ID) { HEU_Logger.LogError("Failed to create new heightfield node in Houdini session!"); return(false); } if (!session.CookNode(idt._heightNodeID, false)) { HEU_Logger.LogError("New input node failed to cook!"); return(false); } return(true); }
public void Initialize(HEU_SessionBase session, HAPI_ObjectInfo objectInfo, HAPI_Transform objectTranform, HEU_HoudiniAsset parentAsset) { _objectInfo = objectInfo; _objectTransform = objectTranform; _parentAsset = parentAsset; SyncWithObjectInfo(session); // Translate transform to Unity (TODO) List<HAPI_GeoInfo> geoInfos = new List<HAPI_GeoInfo>(); // Get display geo info HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo(); if(!session.GetDisplayGeoInfo(_objectInfo.nodeId, ref displayGeoInfo)) { return; } //Debug.LogFormat("Found geoinfo with name {0} and id {1}", HEU_SessionManager.GetString(displayGeoInfo.nameSH, session), displayGeoInfo.nodeId); geoInfos.Add(displayGeoInfo); // TODO: The following editable node query also retrieves geo nodes for terrain with visualization nodes. Need to review to check if we're using the // correct query flags, and handling returned nodes correctly. // Get editable nodes, cook em, then create geo nodes for them HAPI_NodeId[] editableNodes = null; HEU_SessionManager.GetComposedChildNodeList(session, _objectInfo.nodeId, (int)HAPI_NodeType.HAPI_NODETYPE_SOP, (int)HAPI_NodeFlags.HAPI_NODEFLAGS_EDITABLE, true, out editableNodes); if(editableNodes != null) { foreach(HAPI_NodeId editNodeID in editableNodes) { if (editNodeID != displayGeoInfo.nodeId) { session.CookNode(editNodeID, HEU_PluginSettings.CookTemplatedGeos); HAPI_GeoInfo editGeoInfo = new HAPI_GeoInfo(); if (session.GetGeoInfo(editNodeID, ref editGeoInfo)) { geoInfos.Add(editGeoInfo); } } } } //Debug.LogFormat("Object id={5}, name={0}, isInstancer={1}, isInstanced={2}, instancePath={3}, instanceId={4}", // HEU_SessionManager.GetString(objectInfo.nameSH, session), objectInfo.isInstancer, objectInfo.isInstanced, // HEU_SessionManager.GetString(objectInfo.objectInstancePathSH, session), objectInfo.objectToInstanceId, objectInfo.nodeId); // Go through geo infos to create geometry int numGeoInfos = geoInfos.Count; for(int i = 0; i < numGeoInfos; ++i) { // Create GeoNode for each _geoNodes.Add(CreateGeoNode(session, geoInfos[i])); } // This has been moved to GenerateGeometry but kept here just in case. //ApplyObjectTransformToGeoNodes(); }
/// <summary> /// Cooks node and returns true if successfull. /// </summary> /// <param name="nodeID">The node to cook</param> /// <param name="bCookTemplatedGeos">Whether to cook templated geos</param> /// <returns>True if successfully cooked node</returns> public static bool CookNodeInHoudini(HEU_SessionBase session, HAPI_NodeId nodeID, bool bCookTemplatedGeos, string assetName) { bool bResult = session.CookNode(nodeID, bCookTemplatedGeos); if (bResult) { return(HEU_HAPIUtility.ProcessHoudiniCookStatus(session, assetName)); } return(bResult); }
/// <summary> /// Helper to set heightfield data for a specific volume node. /// Used for a specific terrain layer. /// </summary> /// <param name="session">Session that the volume node resides in.</param> /// <param name="volumeNodeID">ID of the target volume node</param> /// <param name="partID">Part ID</param> /// <param name="heightValues">Array of height or alpha values</param> /// <param name="heightFieldName">Name of the layer</param> /// <returns>True if successfully uploaded heightfield values</returns> public bool SetHeightFieldData(HEU_SessionBase session, HAPI_NodeId volumeNodeID, HAPI_PartId partID, float[] heightValues, string heightFieldName, ref HAPI_VolumeInfo baseVolumeInfo) { // Cook the node to get infos below if (!session.CookNode(volumeNodeID, false)) { return(false); } // Get Geo, Part, and Volume infos HAPI_GeoInfo geoInfo = new HAPI_GeoInfo(); if (!session.GetGeoInfo(volumeNodeID, ref geoInfo)) { return(false); } HAPI_PartInfo partInfo = new HAPI_PartInfo(); if (!session.GetPartInfo(geoInfo.nodeId, partID, ref partInfo)) { return(false); } HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); if (!session.GetVolumeInfo(volumeNodeID, partInfo.id, ref volumeInfo)) { return(false); } volumeInfo.tileSize = 1; // Use same transform as base layer volumeInfo.transform = baseVolumeInfo.transform; if (!session.SetVolumeInfo(volumeNodeID, partInfo.id, ref volumeInfo)) { Debug.LogError("Unable to set volume info on input heightfield node!"); return(false); } // Now set the height data if (!session.SetHeightFieldData(geoInfo.nodeId, partInfo.id, heightFieldName, heightValues, 0, heightValues.Length)) { Debug.LogErrorFormat("Unable to set `{0}` height values on input heightfield node!\n" + "Check your terrain sizes including Control Texture Resolution is less than the Heightmap Resolution.", heightFieldName); return(false); } return(true); }
/// <summary> /// Creates a heightfield network inside the same object as connectNodeID. /// Uploads the terrain data from inputObject into the new heightfield network, incuding /// all terrain layers/alphamaps. /// </summary> /// <param name="session">Session that connectNodeID exists in</param> /// <param name="connectNodeID">The node to connect the network to. Most likely a SOP/merge node</param> /// <param name="inputObject">The gameobject containing the Terrain components</param> /// <param name="inputNodeID">The created heightfield network node ID</param> /// <returns>True if created network and uploaded heightfield data.</returns> public override bool CreateInputNodeWithDataUpload(HEU_SessionBase session, HAPI_NodeId connectNodeID, GameObject inputObject, out HAPI_NodeId inputNodeID) { inputNodeID = HEU_Defines.HEU_INVALID_NODE_ID; // Create input node, cook it, then upload the geometry data if (!HEU_HAPIUtility.IsNodeValidInHoudini(session, connectNodeID)) { Debug.LogError("Connection node is invalid."); return false; } HEU_InputDataTerrain idt = GenerateTerrainDataFromGameObject(inputObject); if (idt == null) { return false; } HAPI_NodeId parentNodeID = HEU_HAPIUtility.GetParentNodeID(session, connectNodeID); idt._parentNodeID = parentNodeID; if (!CreateHeightFieldInputNode(session, idt)) { return false; } if (!UploadHeightValuesWithTransform(session, idt)) { return false; } inputNodeID = idt._heightfieldNodeID; if (!UploadAlphaMaps(session, idt)) { return false; } if (!session.CookNode(inputNodeID, false)) { Debug.LogError("New input node failed to cook!"); return false; } return true; }
/// <summary> /// Helper to set heightfield data for a specific volume node. /// Used for a specific terrain layer. /// </summary> /// <param name="session">Session that the volume node resides in.</param> /// <param name="volumeNodeID">ID of the target volume node</param> /// <param name="partID">Part ID</param> /// <param name="heightValues">Array of height or alpha values</param> /// <param name="heightFieldName">Name of the layer</param> /// <returns>True if successfully uploaded heightfield values</returns> public bool SetHeightFieldData(HEU_SessionBase session, HAPI_NodeId volumeNodeID, HAPI_PartId partID, float[] heightValues, string heightFieldName) { // Cook the node to get infos below if (!session.CookNode(volumeNodeID, false)) { return false; } // Get Geo, Part, and Volume infos HAPI_GeoInfo geoInfo = new HAPI_GeoInfo(); if (!session.GetGeoInfo(volumeNodeID, ref geoInfo)) { return false; } HAPI_PartInfo partInfo = new HAPI_PartInfo(); if (!session.GetPartInfo(geoInfo.nodeId, partID, ref partInfo)) { return false; } HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); if (!session.GetVolumeInfo(volumeNodeID, partInfo.id, ref volumeInfo)) { return false; } volumeInfo.tileSize = 1; if (!session.SetVolumeInfo(volumeNodeID, partInfo.id, ref volumeInfo)) { Debug.LogError("Unable to set volume info on input heightfield node!"); return false; } // Now set the height data if (!session.SetHeightFieldData(geoInfo.nodeId, partInfo.id, heightFieldName, heightValues, 0, heightValues.Length)) { Debug.LogError("Unable to set height values on input heightfield node!"); return false; } return true; }
/// <summary> /// Create input node for the given inputObject, and upload its mesh data (along with LOD meshes). /// Outputs the inputNodeID if successfully uploaded mesh data and returns true. /// </summary> /// <param name="session">Session to create input node</param> /// <param name="assetID">The parent asset ID</param> /// <param name="inputObject">The input GameObject to query mesh data from</param> /// <param name="inputNodeID">Output of input node ID if successfully created</param> /// <returns>True if successfully created and uploaded mesh data</returns> public static bool CreateInputNodeWithGeoData(HEU_SessionBase session, HAPI_NodeId assetID, GameObject inputObject, out HAPI_NodeId inputNodeID) { inputNodeID = HEU_Defines.HEU_INVALID_NODE_ID; if (!HEU_HAPIUtility.IsAssetValidInHoudini(session, assetID)) { return false; } bool bHasLODGroup = false; List<HEU_UploadMeshData> uploadMeshDatas = GenerateMeshDatasFromInputObject(inputObject, out bHasLODGroup); if (uploadMeshDatas == null || uploadMeshDatas.Count == 0) { return false; } // If connected asset is not valid, then need to create an input asset if (inputNodeID == HEU_Defines.HEU_INVALID_NODE_ID) { string inputName = null; HAPI_NodeId newNodeID = HEU_Defines.HEU_INVALID_NODE_ID; session.CreateInputNode(out newNodeID, inputName); if (newNodeID == HEU_Defines.HEU_INVALID_NODE_ID || !HEU_HAPIUtility.IsAssetValidInHoudini(session, newNodeID)) { Debug.LogErrorFormat("Failed to create new input node in Houdini session!"); return false; } inputNodeID = newNodeID; if (!session.CookNode(inputNodeID, false)) { Debug.LogErrorFormat("New input node failed to cook!"); return false; } } return UploadInputMeshData(session, inputNodeID, uploadMeshDatas, bHasLODGroup); }
/// <summary> /// Creates a mesh input node and uploads the mesh data from inputObject. /// </summary> /// <param name="session">Session that connectNodeID exists in</param> /// <param name="connectNodeID">The node to connect the network to. Most likely a SOP/merge node</param> /// <param name="inputObject">The gameobject containing the mesh components</param> /// <param name="inputNodeID">The created input node ID</param> /// <returns>True if created network and uploaded mesh data.</returns> public override bool CreateInputNodeWithDataUpload(HEU_SessionBase session, HAPI_NodeId connectNodeID, GameObject inputObject, out HAPI_NodeId inputNodeID) { inputNodeID = HEU_Defines.HEU_INVALID_NODE_ID; // Create input node, cook it, then upload the geometry data if (!HEU_HAPIUtility.IsNodeValidInHoudini(session, connectNodeID)) { Debug.LogError("Connection node is invalid."); return false; } // Get upload meshes from input object HEU_InputDataMeshes inputMeshes = GenerateMeshDatasFromGameObject(inputObject); if (inputMeshes == null || inputMeshes._inputMeshes == null || inputMeshes._inputMeshes.Count == 0) { Debug.LogError("No valid meshes found on input objects."); return false; } string inputName = null; HAPI_NodeId 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)) { Debug.LogError("Failed to create new input node in Houdini session!"); return false; } inputNodeID = newNodeID; if (!session.CookNode(inputNodeID, false)) { Debug.LogError("New input node failed to cook!"); return false; } return UploadData(session, inputNodeID, inputMeshes); }
internal bool UploadCapsuleColliderData(HEU_SessionBase session, CapsuleCollider collider, int inputIndex, HAPI_NodeId parentNodeID, out HAPI_NodeId inputNodeID) { inputNodeID = HEU_Defines.HEU_INVALID_NODE_ID; if (!collider) return false; // Copied from Unreal FKSphylElem::GetElemSolid because exact Unity capsule source code is not available Vector3 sphereCenter = collider.center; float sphereRadius = collider.radius; float sphereLength = collider.height; // Height in Unreal is only the line segment. Height in Unity is the total length, so to get the line length, subtract 2 * rad sphereLength = Mathf.Max(sphereLength - 2 * sphereRadius, 0); int direction = collider.direction; // 0 = X, 1 = Y, 2 = Z. Default is Y // Unreal Y -> Unity X, Unreal Z -> Unity Y int numSides = 6; int numRings = (numSides / 2) + 1; int numVerts = (numSides + 1) * (numRings + 1); // Calculate the vertices for one arc Vector3[] arcVertices = new Vector3[numRings + 1]; for (int ringIdx = 0; ringIdx < numRings + 1; ringIdx++) { float angle; float zOffset; if (ringIdx <= numSides / 4) { angle = ((float) ringIdx / (numRings - 1)) * Mathf.PI; zOffset = 0.5f * sphereLength; } else { angle = ((float)(ringIdx - 1) / (numRings - 1) ) * Mathf.PI; zOffset = -0.5f * sphereLength; } // Note- unit sphere, so position always has mag of one. We can just use it for normal! Vector3 spherePos = new Vector3(); spherePos.x = sphereRadius * Mathf.Sin(angle); spherePos.y = sphereRadius * Mathf.Cos(angle); spherePos.z = 0; arcVertices[ringIdx] = spherePos + new Vector3(0, zOffset, 0); } Vector3 directionRotationEuler = Vector3.zero; if (direction == 1) { // Y axis - This is the default after Unity unit conversion directionRotationEuler = Vector3.zero; } else if (direction == 0) { // X axis - Rotate around Z directionRotationEuler = new Vector3(0, 0, 90); } else if (direction == 2) { // Z axis - Rotate around X directionRotationEuler = new Vector3(90, 0, 0); } Quaternion directionRotation = Quaternion.Euler(directionRotationEuler); // Get the transform matrix for the rotation // Get the capsule vertices by rotating the arc NumSides+1 times float[] vertices = new float[numVerts * 3]; for (int sideIdx = 0; sideIdx < numSides + 1; sideIdx++) { Vector3 arcEuler = new Vector3(0, 360.0f *((float)sideIdx / (float)numSides), 0); Quaternion arcRot = Quaternion.Euler(arcEuler); for (int vertIdx = 0; vertIdx < numRings + 1; vertIdx++) { int vIx = (numRings + 1) * sideIdx + vertIdx; Vector3 arcVertex = arcRot * arcVertices[vertIdx]; arcVertex = directionRotation * arcVertex; Vector3 curPosition = sphereCenter + arcVertex; HEU_HAPIUtility.ConvertPositionUnityToHoudini(curPosition, out vertices[vIx * 3 + 0], out vertices[vIx * 3 + 1], out vertices[vIx * 3 + 2]); } } int numIndices = numSides * numRings * 6; int[] indices = new int[numIndices]; int curIndex = 0; for (int sideIdx = 0; sideIdx < numSides; sideIdx++) { int a0start = (sideIdx + 0) * (numRings + 1); int a1start = (sideIdx + 1) * (numRings + 1); for (int ringIdx = 0; ringIdx < numRings; ringIdx++) { // First tri (reverse winding) indices[curIndex+0] = a0start + ringIdx + 0; indices[curIndex+2] = a1start + ringIdx + 0; indices[curIndex+1] = a0start + ringIdx + 1; curIndex += 3; // Second Tri (reverse winding) indices[curIndex+0] = a1start + ringIdx + 0; indices[curIndex+2] = a1start + ringIdx + 1; indices[curIndex+1] = a0start + ringIdx + 1; curIndex += 3; } } HAPI_NodeId sphereNodeID = -1; string sphereName = string.Format("Sphyl{0}", inputIndex); if (!CreateInputNodeForCollider(session, out sphereNodeID, parentNodeID, inputIndex, sphereName, vertices, indices)) return false; if (!session.CookNode(sphereNodeID, false)) return false; HAPI_NodeId groupNodeID = HEU_Defines.HEU_INVALID_NODE_ID; string groupName = string.Format("group{0}", inputIndex); if (!session.CreateNode(parentNodeID, "groupcreate", groupName, false, out groupNodeID)) { HEU_Logger.LogErrorFormat("Unable to create group SOP node for connecting input assets."); return false; } HAPI_NodeId groupParmID = HEU_Defines.HEU_INVALID_NODE_ID; if (!session.GetParmIDFromName(groupNodeID, "groupname", out groupParmID) || groupParmID == HEU_Defines.HEU_INVALID_NODE_ID) return false; string baseGroupName = GetColliderGroupBaseName(collider, bIsConvex: false, bIsSimple: true); string groupNameStr = string.Format("{0}_capsule{1}", baseGroupName, inputIndex); if (!session.SetParamStringValue(groupNodeID, groupNameStr, groupParmID, 0)) return false; if (!session.ConnectNodeInput(groupNodeID, 0, sphereNodeID)) return false; inputNodeID = groupNodeID; return true; }
/// <summary> /// Creates a heightfield network inside the same object as connectNodeID. /// Uploads the terrain data from inputObject into the new heightfield network, incuding /// all terrain layers/alphamaps. /// </summary> /// <param name="session">Session that connectNodeID exists in</param> /// <param name="connectNodeID">The node to connect the network to. Most likely a SOP/merge node</param> /// <param name="inputObject">The gameobject containing the Terrain components</param> /// <param name="inputNodeID">The created heightfield network node ID</param> /// <returns>True if created network and uploaded heightfield data.</returns> public override bool CreateInputNodeWithDataUpload(HEU_SessionBase session, HAPI_NodeId connectNodeID, GameObject inputObject, out HAPI_NodeId inputNodeID) { inputNodeID = HEU_Defines.HEU_INVALID_NODE_ID; // Create input node, cook it, then upload the geometry data if (!HEU_HAPIUtility.IsNodeValidInHoudini(session, connectNodeID)) { Debug.LogError("Connection node is invalid."); return(false); } HEU_InputDataTerrain idt = GenerateTerrainDataFromGameObject(inputObject); if (idt == null) { return(false); } HAPI_NodeId parentNodeID = HEU_HAPIUtility.GetParentNodeID(session, connectNodeID); idt._parentNodeID = parentNodeID; if (!CreateHeightFieldInputNode(session, idt)) { return(false); } HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); if (!UploadHeightValuesWithTransform(session, idt, ref volumeInfo)) { return(false); } inputNodeID = idt._heightfieldNodeID; bool bMaskSet = false; if (!UploadAlphaMaps(session, idt, ref volumeInfo, out bMaskSet)) { return(false); } if (!bMaskSet) { // While the default HF created by the input node also creates a default mask layer, // we still need to set the mask layer's transform. So this uses the base VolumeInfo // to do just that. if (!SetMaskLayer(session, idt, ref volumeInfo)) { return(false); } } if (!session.CookNode(inputNodeID, false)) { Debug.LogError("New input node failed to cook!"); return(false); } return(true); }
// This is the old way of getting outputs. Keep it for now for legacy. TODO: Remove this later internal void GatherAllAssetOutputsLegacy(HEU_SessionBase session, HAPI_ObjectInfo objectInfo, bool bUseOutputNodes, ref List<HEU_GeoNode> geoNodes) { List<HAPI_GeoInfo> geoInfos = new List<HAPI_GeoInfo>(); // Get display geo info HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo(); if (!session.GetDisplayGeoInfo(objectInfo.nodeId, ref displayGeoInfo)) { return; } //HEU_Logger.LogFormat("Found geoinfo with name {0} and id {1}", HEU_SessionManager.GetString(displayGeoInfo.nameSH, session), displayGeoInfo.nodeId); geoInfos.Add(displayGeoInfo); if (bUseOutputNodes) { int outputCount = 0; if (!session.GetOutputGeoCount(objectInfo.nodeId, out outputCount)) { outputCount = 0; } if (outputCount > 0) { HAPI_GeoInfo[] outputGeoInfos = new HAPI_GeoInfo[outputCount]; if (!session.GetOutputGeoInfos(objectInfo.nodeId, ref outputGeoInfos, outputCount)) { outputGeoInfos = new HAPI_GeoInfo[0]; } foreach (HAPI_GeoInfo geoInfo in outputGeoInfos) { if (geoInfo.nodeId == displayGeoInfo.nodeId) { continue; } bool bValidOutput = true; int parentId = HEU_HAPIUtility.GetParentNodeID(session, geoInfo.nodeId); while (parentId >= 0) { if (parentId == geoInfo.nodeId) { // This output node is inside the display geo // Do not use this output to avoid duplicates bValidOutput = false; break; } parentId = HEU_HAPIUtility.GetParentNodeID(session, parentId); } if (bValidOutput) { // Need to cook output geometry to get their parts HAPI_GeoInfo cookedGeoInfo = new HAPI_GeoInfo(); session.CookNode(geoInfo.nodeId, HEU_PluginSettings.CookTemplatedGeos); // Get the refreshed geo info if (session.GetGeoInfo(geoInfo.nodeId, ref cookedGeoInfo)) { geoInfos.Add(cookedGeoInfo); } } } } } // Get editable nodes, cook em, then create geo nodes for them HAPI_NodeId[] editableNodes = null; HEU_SessionManager.GetComposedChildNodeList(session, objectInfo.nodeId, (int)HAPI_NodeType.HAPI_NODETYPE_SOP, (int)HAPI_NodeFlags.HAPI_NODEFLAGS_EDITABLE, true, out editableNodes); if (editableNodes != null) { foreach (HAPI_NodeId editNodeID in editableNodes) { if (editNodeID != displayGeoInfo.nodeId) { session.CookNode(editNodeID, HEU_PluginSettings.CookTemplatedGeos); HAPI_GeoInfo editGeoInfo = new HAPI_GeoInfo(); if (session.GetGeoInfo(editNodeID, ref editGeoInfo)) { geoInfos.Add(editGeoInfo); } } } } //HEU_Logger.LogFormat("Object id={5}, name={0}, isInstancer={1}, isInstanced={2}, instancePath={3}, instanceId={4}", // HEU_SessionManager.GetString(objectInfo.nameSH, session), objectInfo.isInstancer, objectInfo.isInstanced, // HEU_SessionManager.GetString(objectInfo.objectInstancePathSH, session), objectInfo.objectToInstanceId, objectInfo.nodeId); // Go through geo infos to create geometry int numGeoInfos = geoInfos.Count; for (int i = 0; i < numGeoInfos; ++i) { // Create GeoNode for each geoNodes.Add(CreateGeoNode(session, geoInfos[i])); } }
internal bool UploadMeshColliderData(HEU_SessionBase session, MeshCollider collider, int inputIndex, HAPI_NodeId parentNodeID, out HAPI_NodeId inputNodeID) { inputNodeID = HEU_Defines.HEU_INVALID_NODE_ID; if (!collider) return false; Mesh mesh = collider.sharedMesh; Vector3[] vertices = mesh.vertices; int numSubmeshes = mesh.subMeshCount; List<int> indices = new List<int>(); for (int i = 0; i < numSubmeshes; i++) { int[] indicesForSubmesh = mesh.GetIndices(i); indices.AddRange(indicesForSubmesh); } int[] indicesArr = indices.ToArray(); float[] verticesArr = new float[vertices.Length * 3]; for (int i = 0; i < vertices.Length; i++) { HEU_HAPIUtility.ConvertPositionUnityToHoudini(vertices[i], out verticesArr[i * 3 + 0], out verticesArr[i * 3 + 1], out verticesArr[i * 3 + 2]); } HAPI_NodeId meshNodeID = -1; string meshName = string.Format("MeshCollider{0}", inputIndex); if (!CreateInputNodeForCollider(session, out meshNodeID, parentNodeID, inputIndex, meshName, verticesArr, indicesArr)) return false; if (!session.CookNode(meshNodeID, false)) return false; HAPI_NodeId groupNodeID = HEU_Defines.HEU_INVALID_NODE_ID; string groupName = string.Format("group{0}", inputIndex); if (!session.CreateNode(parentNodeID, "groupcreate", groupName, false, out groupNodeID)) { HEU_Logger.LogErrorFormat("Unable to create group SOP node for connecting input assets."); return false; } HAPI_NodeId groupParmID = HEU_Defines.HEU_INVALID_NODE_ID; if (!session.GetParmIDFromName(groupNodeID, "groupname", out groupParmID) || groupParmID == HEU_Defines.HEU_INVALID_NODE_ID) return false; bool isConvex = collider.convex; string baseGroupName = GetColliderGroupBaseName(collider, bIsConvex: isConvex, bIsSimple: false); string groupNameStr = string.Format("{0}_mesh{1}", baseGroupName, inputIndex); if (!session.SetParamStringValue(groupNodeID, groupNameStr, groupParmID, 0)) return false; if (!session.ConnectNodeInput(groupNodeID, 0, meshNodeID)) return false; inputNodeID = groupNodeID; return true; }
internal bool UploadBoxColliderData(HEU_SessionBase session, BoxCollider collider, int inputIndex, HAPI_NodeId parentNodeID, out HAPI_NodeId inputNodeID) { inputNodeID = HEU_Defines.HEU_INVALID_NODE_ID; if (!collider) return false; HAPI_NodeId boxNodeID = HEU_Defines.HEU_INVALID_NODE_ID; string name = string.Format("Box{0}", inputIndex); Vector3 center = HEU_HAPIUtility.ConvertPositionUnityToHoudini(collider.center); Vector3 size = HEU_HAPIUtility.ConvertScaleUnityToHoudini(collider.size); if (!session.CreateNode(parentNodeID, "box", null, false, out boxNodeID)) { HEU_Logger.LogErrorFormat("Unable to create merge box node for connecting input assets."); return false; } string sizeParamName = "size"; if (!session.SetParamFloatValue(boxNodeID, sizeParamName, 0, size.x)) return false; if (!session.SetParamFloatValue(boxNodeID, sizeParamName, 1, size.y)) return false; if (!session.SetParamFloatValue(boxNodeID, sizeParamName, 2, size.z)) return false; string transformParamName = "t"; if (!session.SetParamFloatValue(boxNodeID, transformParamName, 0, center.x)) return false; if (!session.SetParamFloatValue(boxNodeID, transformParamName, 1, center.y)) return false; if (!session.SetParamFloatValue(boxNodeID, transformParamName, 2, center.z)) return false; if (!session.CookNode(boxNodeID, false)) return false; HAPI_NodeId groupNodeID = HEU_Defines.HEU_INVALID_NODE_ID; string groupName = string.Format("group{0}", inputIndex); if (!session.CreateNode(parentNodeID, "groupcreate", groupName, false, out groupNodeID)) { HEU_Logger.LogErrorFormat("Unable to create group SOP node for connecting input assets."); return false; } HAPI_NodeId groupParmID = HEU_Defines.HEU_INVALID_NODE_ID; if (!session.GetParmIDFromName(groupNodeID, "groupname", out groupParmID) || groupParmID == HEU_Defines.HEU_INVALID_NODE_ID) return false; string baseGroupName = GetColliderGroupBaseName(collider, bIsConvex: false, bIsSimple: true); string groupNameStr = string.Format("{0}_box{1}", baseGroupName, inputIndex); if (!session.SetParamStringValue(groupNodeID, groupNameStr, groupParmID, 0)) return false; if (!session.ConnectNodeInput(groupNodeID, 0, boxNodeID)) return false; inputNodeID = groupNodeID; return true; }
internal bool UploadSphereColliderData(HEU_SessionBase session, SphereCollider collider, int inputIndex, HAPI_NodeId parentNodeID, out HAPI_NodeId inputNodeID) { inputNodeID = HEU_Defines.HEU_INVALID_NODE_ID; if (!collider) return false; Vector3 center = HEU_HAPIUtility.ConvertPositionUnityToHoudini(collider.center); float radius = collider.radius; HAPI_NodeId sphereNodeID = HEU_Defines.HEU_INVALID_NODE_ID; string name = string.Format("Sphere{0}", inputIndex); if (!session.CreateNode(parentNodeID, "sphere", null, false, out sphereNodeID)) { HEU_Logger.LogErrorFormat("Unable to create merge box node for connecting input assets."); return false; } string radParamName = "rad"; if (!session.SetParamFloatValue(sphereNodeID, radParamName, 0, radius)) return false; if (!session.SetParamFloatValue(sphereNodeID, radParamName, 1, radius)) return false; if (!session.SetParamFloatValue(sphereNodeID, radParamName, 2, radius)) return false; string transformParamName = "t"; if (!session.SetParamFloatValue(sphereNodeID, transformParamName, 0, center.x)) return false; if (!session.SetParamFloatValue(sphereNodeID, transformParamName, 1, center.y)) return false; if (!session.SetParamFloatValue(sphereNodeID, transformParamName, 2, center.z)) return false; string typeParamName = "type"; if (!session.SetParamIntValue(sphereNodeID, typeParamName, 0, 1)) return false; if (!session.CookNode(sphereNodeID, false)) return false; HAPI_NodeId groupNodeID = HEU_Defines.HEU_INVALID_NODE_ID; string groupName = string.Format("group{0}", inputIndex); if (!session.CreateNode(parentNodeID, "groupcreate", groupName, false, out groupNodeID)) { HEU_Logger.LogErrorFormat("Unable to create group SOP node for connecting input assets."); return false; } HAPI_NodeId groupParmID = HEU_Defines.HEU_INVALID_NODE_ID; if (!session.GetParmIDFromName(groupNodeID, "groupname", out groupParmID) || groupParmID == HEU_Defines.HEU_INVALID_NODE_ID) return false; string baseGroupName = GetColliderGroupBaseName(collider, bIsConvex: false, bIsSimple: true); string groupNameStr = string.Format("{0}_sphere{1}", baseGroupName, inputIndex); if (!session.SetParamStringValue(groupNodeID, groupNameStr, groupParmID, 0)) return false; if (!session.ConnectNodeInput(groupNodeID, 0, sphereNodeID)) return false; inputNodeID = groupNodeID; return true; }
/// <summary> /// Retrieves object info from Houdini session and updates internal state. /// New geo nodes are created, unused geo nodes are destroyed. /// Geo nodes are then refreshed to be in sync with Houdini session. /// </summary> /// <returns>True if internal state has changed (including geometry).</returns> public void UpdateObject(HEU_SessionBase session, bool bForceUpdate) { // Update the geo info if (!session.GetObjectInfo(ObjectID, ref _objectInfo)) { return; } SyncWithObjectInfo(session); // Update the object transform _objectTransform = ParentAsset.GetObjectTransform(session, ObjectID); // Container for existing geo nodes that are still in use List<HEU_GeoNode> geoNodesToKeep = new List<HEU_GeoNode>(); // Container for new geo infos that need to be created List<HAPI_GeoInfo> newGeoInfosToCreate = new List<HAPI_GeoInfo>(); if (_objectInfo.haveGeosChanged || bForceUpdate) { // Indicates that the geometry nodes have changed //Debug.Log("Geos have changed!"); // Form a list of geo infos that are now present after cooking List<HAPI_GeoInfo> postCookGeoInfos = new List<HAPI_GeoInfo>(); // Get the display geo info HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo(); if (session.GetDisplayGeoInfo(_objectInfo.nodeId, ref displayGeoInfo, false)) { postCookGeoInfos.Add(displayGeoInfo); } else { displayGeoInfo.nodeId = HEU_Defines.HEU_INVALID_NODE_ID; } // Get editable nodes, cook em, then create geo nodes for them HAPI_NodeId[] editableNodes = null; HEU_SessionManager.GetComposedChildNodeList(session, _objectInfo.nodeId, (int)HAPI_NodeType.HAPI_NODETYPE_SOP, (int)HAPI_NodeFlags.HAPI_NODEFLAGS_EDITABLE, true, out editableNodes); if (editableNodes != null) { foreach (HAPI_NodeId editNodeID in editableNodes) { if (editNodeID != displayGeoInfo.nodeId) { session.CookNode(editNodeID, HEU_PluginSettings.CookTemplatedGeos); HAPI_GeoInfo editGeoInfo = new HAPI_GeoInfo(); if (session.GetGeoInfo(editNodeID, ref editGeoInfo)) { postCookGeoInfos.Add(editGeoInfo); } } } } // Now for each geo node that are present after cooking, we check if its // new or whether we already have it prior to cooking. int numPostCookGeoInfos = postCookGeoInfos.Count; for (int i = 0; i < numPostCookGeoInfos; i++) { bool bFound = false; for (int j = 0; j < _geoNodes.Count; j++) { string geoName = HEU_SessionManager.GetString(postCookGeoInfos[i].nameSH, session); if (geoName.Equals(_geoNodes[j].GeoName)) { _geoNodes[j].SetGeoInfo(postCookGeoInfos[i]); geoNodesToKeep.Add(_geoNodes[j]); _geoNodes.RemoveAt(j); bFound = true; break; } } if (!bFound) { newGeoInfosToCreate.Add(postCookGeoInfos[i]); } } // Whatever is left in _geoNodes is no longer needed so clean up int numCurrentGeos = _geoNodes.Count; for (int i = 0; i < numCurrentGeos; ++i) { _geoNodes[i].DestroyAllData(); } } else { Debug.Assert(_objectInfo.geoCount == _geoNodes.Count, "Expected same number of geometry nodes."); } // Go through the old geo nodes that are still in use and update if necessary. foreach (HEU_GeoNode geoNode in geoNodesToKeep) { // Get geo info and check if geo changed bool bGeoChanged = bForceUpdate || geoNode.HasGeoNodeChanged(session); if (bGeoChanged) { geoNode.UpdateGeo(session); } else { if (_objectInfo.haveGeosChanged) { // Clear object instances since the object info has changed. // Without this, the object instances were never getting updated // if only the inputs changed but not outputs (of instancers). geoNode.ClearObjectInstances(); } // Visiblity might have changed, so update that geoNode.CalculateVisiblity(IsVisible()); geoNode.CalculateColliderState(); } } // Create the new geo infos and add to our keep list foreach (HAPI_GeoInfo newGeoInfo in newGeoInfosToCreate) { geoNodesToKeep.Add(CreateGeoNode(session, newGeoInfo)); } // Overwrite the old list with new _geoNodes = geoNodesToKeep; // Updating the trasform is done in GenerateGeometry }
/// <summary> /// Creates a mesh input node and uploads the mesh data from inputObject. /// </summary> /// <param name="session">Session that connectNodeID exists in</param> /// <param name="connectNodeID">The node to connect the network to. Most likely a SOP/merge node</param> /// <param name="inputObject">The gameobject containing the mesh components</param> /// <param name="inputNodeID">The created input node ID</param> /// <returns>True if created network and uploaded mesh data.</returns> public override bool CreateInputNodeWithDataUpload(HEU_SessionBase session, HAPI_NodeId connectNodeID, GameObject inputObject, out HAPI_NodeId inputNodeID) { inputNodeID = HEU_Defines.HEU_INVALID_NODE_ID; // Create input node, cook it, then upload the geometry data if (!HEU_HAPIUtility.IsNodeValidInHoudini(session, connectNodeID)) { HEU_Logger.LogError("Connection node is invalid."); return false; } bool bExportColliders = settings != null && settings.ExportColliders == true; // Get upload meshes from input object HEU_InputDataMeshes inputMeshes = GenerateMeshDatasFromGameObject(inputObject, bExportColliders); if (inputMeshes == null || inputMeshes._inputMeshes == null || inputMeshes._inputMeshes.Count == 0) { HEU_Logger.LogError("No valid meshes found on input objects."); return false; } string inputName = null; HAPI_NodeId 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; if (!UploadData(session, inputNodeID, inputMeshes)) { if (!session.CookNode(inputNodeID, false)) { HEU_Logger.LogError("New input node failed to cook!"); return false; } return false; } bool createMergeNode = false; HAPI_NodeId mergeNodeId = HEU_Defines.HEU_INVALID_NODE_ID; if (bExportColliders) { createMergeNode = true; } if (!createMergeNode) { return true; } HAPI_NodeId 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; if (bExportColliders) { if (!UploadColliderData(session, mergeNodeId, inputMeshes, parentId)) { return false; } } if (!session.CookNode(inputNodeID, false)) { HEU_Logger.LogError("New input node failed to cook!"); return false; } return true; }