private HEU_GeoNode CreateGeoNode(HEU_SessionBase session, HAPI_GeoInfo geoInfo) { HEU_GeoNode geoNode = ScriptableObject.CreateInstance<HEU_GeoNode>(); geoNode.Initialize(session, geoInfo, this); geoNode.UpdateGeo(session); return geoNode; }
public void Initialize(HEU_SessionBase session, HAPI_GeoInfo geoInfo, HEU_ObjectNode containerObjectNode) { _containerObjectNode = containerObjectNode; _geoInfo = geoInfo; _geoName = HEU_SessionManager.GetString(_geoInfo.nameSH, session); //Debug.Log(string.Format("GeoNode initialized with ID={0}, name={1}, type={2}", GeoID, GeoName, geoInfo.type)); }
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(); }
public void Reset() { _geoName = ""; _geoInfo = new HAPI_GeoInfo(); _geoInfo.nodeId = -1; _geoInfo.type = HAPI_GeoType.HAPI_GEOTYPE_DEFAULT; _parts = new List <HEU_PartData>(); }
private HAPI_NodeId GetDisplayNodeID(HAPI_NodeId objNodeID) { HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo(); if (_session.GetDisplayGeoInfo(objNodeID, ref displayGeoInfo)) { return displayGeoInfo.nodeId; } return HEU_Defines.HEU_INVALID_NODE_ID; }
public void SetGeoInfo(HAPI_GeoInfo geoInfo) { _geoInfo = geoInfo; // Also update input node ID in case of this node // being recreated in new session with a different GeoID if (_inputNode != null) { _inputNode.SetInputNodeID(GeoID); } }
/// <summary> /// Gets the group names for given group type. /// </summary> /// <param name="nodeID">The node ID</param> /// <param name="groupType">The group type to query</param> /// <returns>Populated array of string names, or null if failed</returns> public static string[] GetGroupNames(HAPI_NodeId nodeID, HAPI_PartId partID, HAPI_GroupType groupType, bool isInstanced) { HEU_SessionBase session = GetOrCreateDefaultSession(); if (session != null) { HAPI_GeoInfo geoInfo = new HAPI_GeoInfo(); if (session.GetGeoInfo(nodeID, ref geoInfo)) { int groupCount = 0; if(!isInstanced) { groupCount = geoInfo.getGroupCountByType(groupType); ; } else { int pointGroupCount = 0; int primGroupCount = 0; if (session.GetGroupCountOnPackedInstancePart(nodeID, partID, out pointGroupCount, out primGroupCount)) { groupCount = (groupType == HAPI_GroupType.HAPI_GROUPTYPE_POINT) ? pointGroupCount : primGroupCount; } } if(groupCount <= 0) { return null; } int[] groupNames = new int[groupCount]; bool bSuccess = false; if(!isInstanced) { bSuccess = session.GetGroupNames(nodeID, groupType, ref groupNames, groupCount); } else { bSuccess = session.GetGroupNamesOnPackedInstancePart(nodeID, partID, groupType, ref groupNames, groupCount); } if (bSuccess) { string[] nameStrings = new string[groupCount]; for (int i = 0; i < groupCount; ++i) { nameStrings[i] = GetString(groupNames[i]); } return nameStrings; } } } return null; }
/// <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); }
public static bool CreateAndCookInputAsset(HEU_SessionBase session, string assetName, bool bCookTemplatedGeos, out HAPI_NodeId newAssetID) { newAssetID = HEU_Defines.HEU_INVALID_NODE_ID; if (!session.CreateInputNode(out newAssetID, null)) { return(false); } // Make sure cooking is successfull before proceeding. Any licensing or file data issues will be caught here. if (!HEU_HAPIUtility.ProcessHoudiniCookStatus(session, assetName)) { return(false); } // In case the cooking wasn't done previously, force it now. bool bResult = HEU_HAPIUtility.CookNodeInHoudini(session, newAssetID, bCookTemplatedGeos, assetName); if (!bResult) { // When cook failed, deleted the node created earlier session.DeleteNode(newAssetID); newAssetID = HEU_Defines.HEU_INVALID_NODE_ID; return(false); } // After cooking, set an empty partinfo HAPI_GeoInfo inputGeoInfo = new HAPI_GeoInfo(); if (!session.GetDisplayGeoInfo(newAssetID, ref inputGeoInfo)) { return(false); } HAPI_PartInfo newPart = new HAPI_PartInfo(); newPart.init(); newPart.id = 0; newPart.vertexCount = 0; newPart.faceCount = 0; newPart.pointCount = 0; // TODO: always set to mesh type? newPart.type = HAPI_PartType.HAPI_PARTTYPE_MESH; if (!session.SetPartInfo(inputGeoInfo.nodeId, 0, ref newPart)) { Debug.LogErrorFormat(HEU_Defines.HEU_NAME + ": Failed to set partinfo for input node!"); return(false); } return(true); }
internal void Initialize(HEU_SessionBase session, HAPI_GeoInfo geoInfo, HEU_ObjectNode containerObjectNode) { _containerObjectNode = containerObjectNode; _geoInfo = geoInfo; string realName = HEU_SessionManager.GetString(_geoInfo.nameSH, session); if (!HEU_PluginSettings.ShortenFolderPaths || realName.Length < 3) { _geoName = HEU_SessionManager.GetString(_geoInfo.nameSH, session); } else { _geoName = realName.Substring(0, 3) + this.GetHashCode(); } //HEU_Logger.Log(string.Format("GeoNode initialized with ID={0}, name={1}, type={2}", GeoID, GeoName, geoInfo.type)); }
/// <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; }
protected virtual bool LoadObjectBuffers(HEU_SessionBase session, ref HAPI_ObjectInfo objectInfo) { // Get display SOP geo info and cook the node HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo(); if (!_session.GetDisplayGeoInfo(objectInfo.nodeId, ref displayGeoInfo)) { return false; } if (!CookNode(session, displayGeoInfo.nodeId)) { return false; } bool bResult = true; bool bHasInstancer = false; HEU_LoadObject obj = new HEU_LoadObject(); obj._objectNodeID = objectInfo.nodeId; obj._displayNodeID = displayGeoInfo.nodeId; if (LoadNodeBuffer(session, obj._displayNodeID, obj)) { _loadData._loadedObjects.Add(obj); if (!bHasInstancer && obj._instancerBuffers != null && obj._instancerBuffers.Count > 0) { bHasInstancer = true; } } else { bResult = false; } if (bResult && bHasInstancer) { BuildBufferIDsMap(_loadData); } return bResult; }
/// <summary> /// Gets the group names for given group type. /// </summary> /// <param name="nodeID">The node ID</param> /// <param name="groupType">The group type to query</param> /// <returns>Populated array of string names, or null if failed</returns> public static string[] GetGroupNames(HAPI_NodeId nodeID, HAPI_GroupType groupType) { HEU_SessionBase sessionBase = GetOrCreateDefaultSession(); if (sessionBase != null) { HAPI_GeoInfo geoInfo = new HAPI_GeoInfo(); if (sessionBase.GetGeoInfo(nodeID, ref geoInfo)) { int count = geoInfo.getGroupCountByType(groupType); int[] names = new int[count]; if (sessionBase.GetGroupNames(nodeID, groupType, ref names, count)) { string[] nameStrings = new string[count]; for (int i = 0; i < count; ++i) { nameStrings[i] = GetString(names[i]); } return nameStrings; } } } return null; }
public bool UploadAttributeViaMeshInput(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID) { // Need to upload same geometry as in the original Editable node, and with custom attribute values. // First get the current geometry info and data. HAPI_GeoInfo geoInfo = new HAPI_GeoInfo(); if(!session.GetGeoInfo(geoID, ref geoInfo)) { return false; } HAPI_PartInfo oldPartInfo = new HAPI_PartInfo(); if(!session.GetPartInfo(geoID, partID, ref oldPartInfo)) { return false; } int pointCount = oldPartInfo.pointCount; int vertexCount = oldPartInfo.vertexCount; int faceCount = oldPartInfo.faceCount; // Get facecounts int[] faceCountData = new int[faceCount]; if(!HEU_GeneralUtility.GetArray2Arg(geoID, partID, session.GetFaceCounts, faceCountData, 0, faceCount)) { return false; } // Get indices int[] vertexList = new int[vertexCount]; if (!HEU_GeneralUtility.GetArray2Arg(geoID, partID, session.GetVertexList, vertexList, 0, vertexCount)) { return false; } List<HEU_AttributeData> pointAttributeDatas = new List<HEU_AttributeData>(); List<HEU_AttributeData> vertexAttributeDatas = new List<HEU_AttributeData>(); List<HEU_AttributeData> primitiveAttributeDatas = new List<HEU_AttributeData>(); List<HEU_AttributeData> detailAttributeDatas = new List<HEU_AttributeData>(); // Get all attributes, including editted GetAttributesList(session, geoID, partID, pointAttributeDatas, HAPI_AttributeOwner.HAPI_ATTROWNER_POINT, oldPartInfo.pointAttributeCount); GetAttributesList(session, geoID, partID, vertexAttributeDatas, HAPI_AttributeOwner.HAPI_ATTROWNER_VERTEX, oldPartInfo.vertexAttributeCount); GetAttributesList(session, geoID, partID, primitiveAttributeDatas, HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM, oldPartInfo.primitiveAttributeCount); GetAttributesList(session, geoID, partID, detailAttributeDatas, HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL, oldPartInfo.detailAttributeCount); // Now create new geometry info and populate it HAPI_PartInfo newPartInfo = new HAPI_PartInfo(); newPartInfo.faceCount = faceCount; newPartInfo.vertexCount = vertexCount; newPartInfo.pointCount = pointCount; newPartInfo.pointAttributeCount = oldPartInfo.pointAttributeCount; newPartInfo.vertexAttributeCount = oldPartInfo.vertexAttributeCount; newPartInfo.primitiveAttributeCount = oldPartInfo.primitiveAttributeCount; newPartInfo.detailAttributeCount = oldPartInfo.detailAttributeCount; int newPartID = partID; if (!session.SetPartInfo(geoID, newPartID, ref newPartInfo)) { return false; } if (!HEU_GeneralUtility.SetArray2Arg(geoID, newPartID, session.SetFaceCount, faceCountData, 0, newPartInfo.faceCount)) { return false; } if (!HEU_GeneralUtility.SetArray2Arg(geoID, newPartID, session.SetVertexList, vertexList, 0, newPartInfo.vertexCount)) { return false; } // Upload all attributes, include editted UpdateAttributeList(session, geoID, partID, pointAttributeDatas); UpdateAttributeList(session, geoID, partID, vertexAttributeDatas); UpdateAttributeList(session, geoID, partID, primitiveAttributeDatas); UpdateAttributeList(session, geoID, partID, detailAttributeDatas); return session.CommitGeo(geoID); }
public void SyncAllAttributesFrom(HEU_SessionBase session, HEU_HoudiniAsset asset, HAPI_NodeId geoID, ref HAPI_PartInfo partInfo, GameObject outputGameObject) { _geoID = geoID; _partID = partInfo.id; HAPI_GeoInfo geoInfo = new HAPI_GeoInfo(); if(session.GetGeoInfo(_geoID, ref geoInfo)) { _geoName = HEU_SessionManager.GetString(geoInfo.nameSH, session); } if (outputGameObject != null) { _outputTransform = outputGameObject.transform; } // Need the vertex list of indices to map the positions to vertex colors _vertexIndices = new int[partInfo.vertexCount]; if (!HEU_GeneralUtility.GetArray2Arg(geoID, partInfo.id, session.GetVertexList, _vertexIndices, 0, partInfo.vertexCount)) { return; } // Note that this currently only supports point attributes int attributePointCount = partInfo.attributeCounts[(int)HAPI_AttributeOwner.HAPI_ATTROWNER_POINT]; string[] pointAttributeNames = new string[attributePointCount]; if(!session.GetAttributeNames(geoID, partInfo.id, HAPI_AttributeOwner.HAPI_ATTROWNER_POINT, ref pointAttributeNames, attributePointCount)) { Debug.LogErrorFormat("Failed to sync attributes. Unable to retrieve attribute names."); return; } // Create new list of attributes. We'll move existing attributes that are still in use as we find them. List<HEU_AttributeData> newAttributeDatas = new List<HEU_AttributeData>(); foreach (string pointAttributeName in pointAttributeNames) { if(string.IsNullOrEmpty(pointAttributeName)) { continue; } // Get position attribute values separately. Used for painting and editing points in 3D scene. HAPI_AttributeInfo pointAttributeInfo = new HAPI_AttributeInfo(); if(session.GetAttributeInfo(geoID, partInfo.id, pointAttributeName, HAPI_AttributeOwner.HAPI_ATTROWNER_POINT, ref pointAttributeInfo)) { if (pointAttributeName.Equals(HEU_Defines.HAPI_ATTRIB_POSITION)) { if (pointAttributeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT) { Debug.LogErrorFormat("Expected float type for position attribute, but got {0}", pointAttributeInfo.storage); return; } _positionAttributeValues = new Vector3[pointAttributeInfo.count]; float[] data = new float[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partInfo.id, pointAttributeName, ref pointAttributeInfo, ref data, session.GetAttributeFloatData); for (int i = 0; i < pointAttributeInfo.count; ++i) { _positionAttributeValues[i] = new Vector3(-data[i * pointAttributeInfo.tupleSize + 0], data[i * pointAttributeInfo.tupleSize + 1], data[i * pointAttributeInfo.tupleSize + 2]); } // We don't let position attributes be editted (for now anyway) continue; } HEU_AttributeData attrData = GetAttributeData(pointAttributeName); if (attrData == null) { // Attribute data not found. Create it. attrData = CreateAttribute(pointAttributeName, ref pointAttributeInfo); //Debug.LogFormat("Created attribute data: {0}", pointAttributeName); } // Add to new list. newAttributeDatas.Add(attrData); // Sync the attribute info to data. PopulateAttributeData(session, geoID, partInfo.id, attrData, ref pointAttributeInfo); if(pointAttributeName.Equals(HEU_Defines.HAPI_ATTRIB_COLOR) || pointAttributeInfo.typeInfo == HAPI_AttributeTypeInfo.HAPI_ATTRIBUTE_TYPE_COLOR) { _hasColorAttribute = true; } } else { // Failed to get point attribute info! } } // Overwriting the old list with the new should automatically remove unused attribute datas. _attributeDatas = newAttributeDatas; }
/// <summary> /// Upload the given list of mesh data in uploadMeshes into node with inputNodeID. /// If the source was a LOD group, then group names are assigned to the geometry. /// </summary> /// <param name="session">Session to upload to</param> /// <param name="inputNodeID">The ID of the input node to upload into</param> /// <param name="uploadMeshes">List of mesh data</param> /// <param name="bHasLODGroup">Whether the source was a LOD group and therefore treat it specially</param> /// <returns></returns> public static bool UploadInputMeshData(HEU_SessionBase session, HAPI_NodeId inputNodeID, List<HEU_UploadMeshData> uploadMeshes, bool bHasLODGroup) { List<Vector3> vertices = new List<Vector3>(); List<Vector3> normals = new List<Vector3>(); List<Vector2> uvs = new List<Vector2>(); List<Color> colors = new List<Color>(); List<int> triIndexList = new List<int>(); int numMaterials = 0; // Accumulate vertices, normals, uvs, colors, and indices. // Keep track of indices start and count for each mesh for later when uploading material assignments and groups. // Indices need to be reindexed with vertex offset as we accumulate vertices. int numMeshes = uploadMeshes.Count; for (int i = 0; i < numMeshes; ++i) { int vertexOffset = vertices.Count; vertices.AddRange(uploadMeshes[i]._mesh.vertices); normals.AddRange(uploadMeshes[i]._mesh.normals); uvs.AddRange(uploadMeshes[i]._mesh.uv); colors.AddRange(uploadMeshes[i]._mesh.colors); uploadMeshes[i]._indexStart = new uint[uploadMeshes[i]._numSubMeshes]; uploadMeshes[i]._indexCount = new uint[uploadMeshes[i]._numSubMeshes]; for (int j = 0; j < uploadMeshes[i]._numSubMeshes; ++j) { int indexStart = triIndexList.Count; // Indices have to be re-indexed with our own offset int[] meshIndices = uploadMeshes[i]._mesh.GetTriangles(j); int numIndices = meshIndices.Length; for (int k = 0; k < numIndices; ++k) { triIndexList.Add(vertexOffset + meshIndices[k]); } uploadMeshes[i]._indexStart[j] = (uint)indexStart; uploadMeshes[i]._indexCount[j] = (uint)(triIndexList.Count) - uploadMeshes[i]._indexStart[j]; } numMaterials += uploadMeshes[i]._materials != null ? uploadMeshes[i]._materials.Length : 0; } // It is possible for some meshes to not have normals/uvs/colors while others do. // In the case where an attribute is missing on some meshes, we clear out those attributes so we don't upload // partial attribute data. int numVertices = vertices.Count; if (normals.Count != numVertices) { normals = null; } if (uvs.Count != numVertices) { uvs = null; } if (colors.Count != numVertices) { colors = null; } HAPI_PartInfo partInfo = new HAPI_PartInfo(); partInfo.faceCount = triIndexList.Count / 3; partInfo.vertexCount = triIndexList.Count; partInfo.pointCount = vertices.Count; partInfo.pointAttributeCount = 1; partInfo.vertexAttributeCount = 0; partInfo.primitiveAttributeCount = 0; partInfo.detailAttributeCount = 0; if (normals != null && normals.Count > 0) { partInfo.vertexAttributeCount++; } if (uvs != null && uvs.Count > 0) { partInfo.vertexAttributeCount++; } if (colors != null && colors.Count > 0) { partInfo.vertexAttributeCount++; } if (numMaterials > 0) { partInfo.primitiveAttributeCount++; } if (numMeshes > 0) { partInfo.primitiveAttributeCount++; } if (bHasLODGroup) { partInfo.primitiveAttributeCount++; partInfo.detailAttributeCount++; } HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo(); if (!session.GetDisplayGeoInfo(inputNodeID, ref displayGeoInfo)) { return false; } HAPI_NodeId displayNodeID = displayGeoInfo.nodeId; if (!session.SetPartInfo(displayNodeID, 0, ref partInfo)) { return false; } int[] faceCounts = new int[partInfo.faceCount]; for (int i = 0; i < partInfo.faceCount; ++i) { faceCounts[i] = 3; } int[] triIndices = triIndexList.ToArray(); if (!HEU_GeneralUtility.SetArray2Arg(displayNodeID, 0, session.SetFaceCount, faceCounts, 0, partInfo.faceCount)) { return false; } if (!HEU_GeneralUtility.SetArray2Arg(displayNodeID, 0, session.SetVertexList, triIndices, 0, partInfo.vertexCount)) { return false; } if (!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_POSITION, 3, vertices.ToArray(), ref partInfo, true)) { return false; } //if(normals != null && !SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_NORMAL, 3, normals, ref partInfo, true)) if (normals != null && !SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_NORMAL, 3, normals.ToArray(), triIndices, ref partInfo, true)) { return false; } if (uvs != null && uvs.Count > 0) { Vector3[] uvs3 = new Vector3[uvs.Count]; for (int i = 0; i < uvs.Count; ++i) { uvs3[i][0] = uvs[i][0]; uvs3[i][1] = uvs[i][1]; uvs3[i][2] = 0; } //if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_UV, 3, uvs3, ref partInfo, false)) if (!SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_UV, 3, uvs3, triIndices, ref partInfo, false)) { return false; } } if (colors != null && colors.Count > 0) { Vector3[] rgb = new Vector3[colors.Count]; float[] alpha = new float[colors.Count]; for (int i = 0; i < colors.Count; ++i) { rgb[i][0] = colors[i].r; rgb[i][1] = colors[i].g; rgb[i][2] = colors[i].b; alpha[i] = colors[i].a; } //if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_COLOR, 3, rgb, ref partInfo, false)) if (!SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_COLOR, 3, rgb, triIndices, ref partInfo, false)) { return false; } //if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_ALPHA, 1, alpha, ref partInfo, false)) if (!SetMeshVertexFloatAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_ALPHA, 1, alpha, triIndices, ref partInfo)) { return false; } } // Set material names for round-trip perservation of material assignment // Each HEU_UploadMeshData might have a list of submeshes and materials // These are all combined into a single mesh, with group names if (numMaterials > 0) { bool bFoundAtleastOneValidMaterial = false; string[] materialIDs = new string[partInfo.faceCount]; for (int g = 0; g < uploadMeshes.Count; ++g) { if (uploadMeshes[g]._numSubMeshes != uploadMeshes[g]._materials.Length) { // Number of submeshes should equal number of materials since materials determine submeshes continue; } for (int i = 0; i < uploadMeshes[g]._materials.Length; ++i) { string materialName = HEU_AssetDatabase.GetAssetPath(uploadMeshes[g]._materials[i]); if (materialName == null) { materialName = ""; } else if (materialName.StartsWith(HEU_Defines.DEFAULT_UNITY_BUILTIN_RESOURCES)) { materialName = HEU_AssetDatabase.GetUniqueAssetPathForUnityAsset(uploadMeshes[g]._materials[i]); } bFoundAtleastOneValidMaterial |= !string.IsNullOrEmpty(materialName); int faceStart = (int)uploadMeshes[g]._indexStart[i] / 3; int faceEnd = faceStart + ((int)uploadMeshes[g]._indexCount[i] / 3); for (int m = faceStart; m < faceEnd; ++m) { materialIDs[m] = materialName; } } } if (bFoundAtleastOneValidMaterial) { HAPI_AttributeInfo materialIDAttrInfo = new HAPI_AttributeInfo(); materialIDAttrInfo.exists = true; materialIDAttrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM; materialIDAttrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_STRING; materialIDAttrInfo.count = partInfo.faceCount; materialIDAttrInfo.tupleSize = 1; materialIDAttrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; if (!session.AddAttribute(displayNodeID, 0, HEU_PluginSettings.UnityMaterialAttribName, ref materialIDAttrInfo)) { return false; } if (!session.SetAttributeStringData(displayNodeID, 0, HEU_PluginSettings.UnityMaterialAttribName, ref materialIDAttrInfo, materialIDs, 0, partInfo.faceCount)) { return false; } } } // Set mesh name attribute HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); attrInfo.exists = true; attrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM; attrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_STRING; attrInfo.count = partInfo.faceCount; attrInfo.tupleSize = 1; attrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; if (session.AddAttribute(displayNodeID, 0, HEU_PluginSettings.UnityInputMeshAttr, ref attrInfo)) { string[] primitiveNameAttr = new string[partInfo.faceCount]; for (int g = 0; g < uploadMeshes.Count; ++g) { for (int i = 0; i < uploadMeshes[g]._numSubMeshes; ++i) { int faceStart = (int)uploadMeshes[g]._indexStart[i] / 3; int faceEnd = faceStart + ((int)uploadMeshes[g]._indexCount[i] / 3); for (int m = faceStart; m < faceEnd; ++m) { primitiveNameAttr[m] = uploadMeshes[g]._meshPath; } } } if (!session.SetAttributeStringData(displayNodeID, 0, HEU_PluginSettings.UnityInputMeshAttr, ref attrInfo, primitiveNameAttr, 0, partInfo.faceCount)) { return false; } } else { return false; } // Set LOD group membership if (bHasLODGroup) { int[] membership = new int[partInfo.faceCount]; for (int g = 0; g < uploadMeshes.Count; ++g) { if (g > 0) { // Clear array for (int m = 0; m < partInfo.faceCount; ++m) { membership[m] = 0; } } // Set 1 for faces belonging to this group for (int s = 0; s < uploadMeshes[g]._numSubMeshes; ++s) { int faceStart = (int)uploadMeshes[g]._indexStart[s] / 3; int faceEnd = faceStart + ((int)uploadMeshes[g]._indexCount[s] / 3); for (int m = faceStart; m < faceEnd; ++m) { membership[m] = 1; } } if (!session.AddGroup(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_PRIM, uploadMeshes[g]._meshName)) { return false; } if (!session.SetGroupMembership(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_PRIM, uploadMeshes[g]._meshName, membership, 0, partInfo.faceCount)) { return false; } } } return session.CommitGeo(displayNodeID); }
/// <summary> /// Returns the various geometry types (parts) from the given node. /// Only part instancers and point instancers (via attributes) are returned. /// </summary> private bool QueryParts(HAPI_NodeId nodeID, ref List<HAPI_PartInfo> meshParts, ref List<HAPI_PartInfo> volumeParts, ref List<HAPI_PartInfo> instancerParts, ref List<HAPI_PartInfo> curveParts, ref List<HAPI_PartInfo> scatterInstancerParts) { // Get display geo info HAPI_GeoInfo geoInfo = new HAPI_GeoInfo(); if (!_session.GetGeoInfo(nodeID, ref geoInfo)) { return false; } //Debug.LogFormat("GeoNode name:{0}, type: {1}, isTemplated: {2}, isDisplayGeo: {3}, isEditable: {4}, parts: {5}", // HEU_SessionManager.GetString(geoInfo.nameSH, _session), // geoInfo.type, geoInfo.isTemplated, // geoInfo.isDisplayGeo, geoInfo.isEditable, geoInfo.partCount); if (geoInfo.type == HAPI_GeoType.HAPI_GEOTYPE_DEFAULT) { int numParts = geoInfo.partCount; for(int i = 0; i < numParts; ++i) { HAPI_PartInfo partInfo = new HAPI_PartInfo(); if (!_session.GetPartInfo(geoInfo.nodeId, i, ref partInfo)) { return false; } bool isAttribInstancer = false; bool isScatterInstancer = false; // Preliminary check for attribute instancing (mesh type with no verts but has points with instances) if (HEU_HAPIUtility.IsSupportedPolygonType(partInfo.type) && partInfo.vertexCount == 0 && partInfo.pointCount > 0) { if (HEU_GeneralUtility.HasValidInstanceAttribute(_session, nodeID, partInfo.id, HEU_PluginSettings.UnityInstanceAttr)) { isAttribInstancer = true; } else if (HEU_GeneralUtility.HasValidInstanceAttribute(_session, nodeID, partInfo.id, HEU_Defines.HEIGHTFIELD_TREEINSTANCE_PROTOTYPEINDEX)) { isScatterInstancer = true; } } if (isScatterInstancer) { scatterInstancerParts.Add(partInfo); } else if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_VOLUME) { volumeParts.Add(partInfo); } else if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_INSTANCER || isAttribInstancer) { instancerParts.Add(partInfo); } else if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_CURVE) { curveParts.Add(partInfo); } else if(HEU_HAPIUtility.IsSupportedPolygonType(partInfo.type)) { meshParts.Add(partInfo); } else { string partName = HEU_SessionManager.GetString(partInfo.nameSH, _session); SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Part {0} with type {1} is not supported for GeoSync.", partName, partInfo.type)); } } } else if(geoInfo.type == HAPI_GeoType.HAPI_GEOTYPE_CURVE) { SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Currently {0} geo type is not implemented for threaded geo loading!", geoInfo.type)); } return true; }
// GEOMETRY GETTERS ------------------------------------------------------------------------------------------- /// <summary> /// Get the display geo (SOP) node inside an Object node. If there are multiple display SOP nodes, only /// the first one is returned. /// </summary> /// <param name="nodeID">Object node ID</param> /// <param name="geoInfo">Geo info to populate</param> /// <returns>True if successfully queried the geo info</returns> public virtual bool GetDisplayGeoInfo(HAPI_NodeId nodeID, ref HAPI_GeoInfo geoInfo, bool bLogError = false) { return false; }
/// <summary> /// Upload the inputData (mesh geometry) into the input node with inputNodeID. /// </summary> /// <param name="session">Session that the input node exists in</param> /// <param name="inputNodeID">ID of the input node</param> /// <param name="inputData">Container of the mesh geometry</param> /// <returns>True if successfully uploaded data</returns> public bool UploadData(HEU_SessionBase session, HAPI_NodeId inputNodeID, HEU_InputData inputData) { HEU_InputDataMeshes inputDataMeshes = inputData as HEU_InputDataMeshes; if (inputDataMeshes == null) { Debug.LogError("Expected HEU_InputDataMeshes type for inputData, but received unsupported type."); return false; } List<Vector3> vertices = new List<Vector3>(); List<Vector3> normals = new List<Vector3>(); List<Color> colors = new List<Color>(); #if UNITY_2018_2_OR_NEWER const int NumUVSets = 8; #else const int NumUVSets = 4; #endif List<Vector3>[] uvs = new List<Vector3>[NumUVSets]; for (int u = 0; u < NumUVSets; ++u) { uvs[u] = new List<Vector3>(); } // Use tempUVs to help with reindexing List<Vector3>[] tempUVs = new List<Vector3>[NumUVSets]; for (int u = 0; u < NumUVSets; ++u) { tempUVs[u] = new List<Vector3>(); } List<int> pointIndexList = new List<int>(); List<int> vertIndexList = new List<int>(); int numMaterials = 0; int numMeshes = inputDataMeshes._inputMeshes.Count; // Get the parent's world transform, so when there are multiple child meshes, // can merge and apply their local transform after subtracting their parent's world transform Matrix4x4 rootInvertTransformMatrix = Matrix4x4.identity; if (numMeshes > 1) { rootInvertTransformMatrix = inputDataMeshes._inputObject.transform.worldToLocalMatrix; } // Always using the first submesh topology. This doesn't support mixed topology (triangles and quads). MeshTopology meshTopology = inputDataMeshes._inputMeshes[0]._mesh.GetTopology(0); int numVertsPerFace = 3; if (meshTopology == MeshTopology.Quads) { numVertsPerFace = 4; } // For all meshes: // Accumulate vertices, normals, uvs, colors, and indices. // Keep track of indices start and count for each mesh for later when uploading material assignments and groups. // Find shared vertices, and use unique set of vertices to use as point positions. // Need to reindex indices for both unique vertices, as well as vertex attributes. for (int i = 0; i < numMeshes; ++i) { Vector3[] meshVertices = inputDataMeshes._inputMeshes[i]._mesh.vertices; Matrix4x4 localToWorld = rootInvertTransformMatrix * inputDataMeshes._inputMeshes[i]._transform.localToWorldMatrix; List<Vector3> uniqueVertices = new List<Vector3>(); // Keep track of old vertex positions (old vertex slot points to new unique vertex slot) int[] reindexVertices = new int[meshVertices.Length]; Dictionary<Vector3, int> reindexMap = new Dictionary<Vector3, int>(); // For each vertex, check against subsequent vertices for shared positions. for (int a = 0; a < meshVertices.Length; ++a) { Vector3 va = meshVertices[a]; if (!reindexMap.ContainsKey(va)) { if (numMeshes > 1 && !inputDataMeshes._hasLOD) { // For multiple meshes that are not LODs, apply local transform on vertices to get the merged mesh. uniqueVertices.Add(localToWorld.MultiplyPoint(va)); } else { uniqueVertices.Add(va); } // Reindex to point to unique vertex slot reindexVertices[a] = uniqueVertices.Count - 1; reindexMap[va] = uniqueVertices.Count - 1; } else { reindexVertices[a] = reindexMap[va]; } } int vertexOffset = vertices.Count; vertices.AddRange(uniqueVertices); Vector3[] meshNormals = inputDataMeshes._inputMeshes[i]._mesh.normals; Color[] meshColors = inputDataMeshes._inputMeshes[i]._mesh.colors; // This is really silly. mesh.GetUVs gives uvs regardless if they exist or not (makes duplicates of // first uv if they don't exist), but mesh.uv* gives correct UVs, but in Vector2 format. // Since we need to convert to Vector3 later, this checks mesh.uv*, then uses mesh.GetUVs to get in Vector3. // Note skipping uv1 as its internally used (i.e. the 2nd uv set is uv2) int uindex = 0; GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv, tempUVs[0], uindex++); GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv2, tempUVs[1], uindex++); GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv3, tempUVs[2], uindex++); GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv4, tempUVs[3], uindex++); #if UNITY_2018_2_OR_NEWER GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv5, tempUVs[4], uindex++); GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv6, tempUVs[5], uindex++); GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv7, tempUVs[6], uindex++); GetUVsFromMesh(inputDataMeshes._inputMeshes[i]._mesh, inputDataMeshes._inputMeshes[i]._mesh.uv8, tempUVs[7], uindex++); #endif inputDataMeshes._inputMeshes[i]._indexStart = new uint[inputDataMeshes._inputMeshes[i]._numSubMeshes]; inputDataMeshes._inputMeshes[i]._indexCount = new uint[inputDataMeshes._inputMeshes[i]._numSubMeshes]; // For each submesh: // Generate face to point index -> pointIndexList // Generate face to vertex attribute index -> vertIndexList for (int j = 0; j < inputDataMeshes._inputMeshes[i]._numSubMeshes; ++j) { int indexStart = pointIndexList.Count; int vertIndexStart = vertIndexList.Count; // Indices have to be re-indexed with our own offset // (using GetIndices to generalize triangles and quad indices) int[] meshIndices = inputDataMeshes._inputMeshes[i]._mesh.GetIndices(j); int numIndices = meshIndices.Length; for (int k = 0; k < numIndices; ++k) { int originalIndex = meshIndices[k]; meshIndices[k] = reindexVertices[originalIndex]; pointIndexList.Add(vertexOffset + meshIndices[k]); vertIndexList.Add(vertIndexStart + k); if (meshNormals != null && (originalIndex < meshNormals.Length)) { normals.Add(meshNormals[originalIndex]); } for (int u = 0; u < NumUVSets; ++u) { if (tempUVs[u].Count > 0) { uvs[u].Add(tempUVs[u][originalIndex]); } } if (meshColors != null && (originalIndex < meshColors.Length)) { colors.Add(meshColors[originalIndex]); } } inputDataMeshes._inputMeshes[i]._indexStart[j] = (uint)indexStart; inputDataMeshes._inputMeshes[i]._indexCount[j] = (uint)(pointIndexList.Count) - inputDataMeshes._inputMeshes[i]._indexStart[j]; } numMaterials += inputDataMeshes._inputMeshes[i]._materials != null ? inputDataMeshes._inputMeshes[i]._materials.Length : 0; } // It is possible for some meshes to not have normals/uvs/colors while others do. // In the case where an attribute is missing on some meshes, we clear out those attributes so we don't upload // partial attribute data. int totalAllVertexCount = vertIndexList.Count; if (normals.Count != totalAllVertexCount) { normals = null; } if (colors.Count != totalAllVertexCount) { colors = null; } HAPI_PartInfo partInfo = new HAPI_PartInfo(); partInfo.faceCount = vertIndexList.Count / numVertsPerFace; partInfo.vertexCount = vertIndexList.Count; partInfo.pointCount = vertices.Count; partInfo.pointAttributeCount = 1; partInfo.vertexAttributeCount = 0; partInfo.primitiveAttributeCount = 0; partInfo.detailAttributeCount = 0; //Debug.LogFormat("Faces: {0}; Vertices: {1}; Verts/Face: {2}", partInfo.faceCount, partInfo.vertexCount, numVertsPerFace); if (normals != null && normals.Count > 0) { partInfo.vertexAttributeCount++; } for (int u = 0; u < NumUVSets; ++u) { if (uvs[u].Count > 0 && uvs[u].Count == totalAllVertexCount) { partInfo.vertexAttributeCount++; } else { uvs[u].Clear(); } } if (colors != null && colors.Count > 0) { partInfo.vertexAttributeCount++; } if (numMaterials > 0) { partInfo.primitiveAttributeCount++; } if (numMeshes > 0) { partInfo.primitiveAttributeCount++; } if (inputDataMeshes._hasLOD) { partInfo.primitiveAttributeCount++; partInfo.detailAttributeCount++; } HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo(); if (!session.GetDisplayGeoInfo(inputNodeID, ref displayGeoInfo)) { return false; } HAPI_NodeId displayNodeID = displayGeoInfo.nodeId; if (!session.SetPartInfo(displayNodeID, 0, ref partInfo)) { Debug.LogError("Failed to set input part info. "); return false; } int[] faceCounts = new int[partInfo.faceCount]; for (int i = 0; i < partInfo.faceCount; ++i) { faceCounts[i] = numVertsPerFace; } int[] faceIndices = pointIndexList.ToArray(); if (!HEU_GeneralUtility.SetArray2Arg(displayNodeID, 0, session.SetFaceCount, faceCounts, 0, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry face counts."); return false; } if (!HEU_GeneralUtility.SetArray2Arg(displayNodeID, 0, session.SetVertexList, faceIndices, 0, partInfo.vertexCount)) { Debug.LogError("Failed to set input geometry indices."); return false; } if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_POSITION, 3, vertices.ToArray(), ref partInfo, true)) { Debug.LogError("Failed to set input geometry position."); return false; } int[] vertIndices = vertIndexList.ToArray(); //if(normals != null && !SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_NORMAL, 3, normals.ToArray(), ref partInfo, true)) if (normals != null && !HEU_InputMeshUtility.SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_NORMAL, 3, normals.ToArray(), vertIndices, ref partInfo, true)) { Debug.LogError("Failed to set input geometry normals."); return false; } for (int u = 0; u < NumUVSets; ++u) { if (uvs[u].Count > 0) { // Skip uv1 as its used internally. So it goes: uv, uv2, ..., uv8 string uvName = u == 0 ? HEU_Defines.HAPI_ATTRIB_UV : string.Format("{0}{1}", HEU_Defines.HAPI_ATTRIB_UV, u + 1); if (!HEU_InputMeshUtility.SetMeshVertexAttribute(session, displayNodeID, 0, uvName, 3, uvs[u].ToArray(), vertIndices, ref partInfo, false)) { Debug.LogError("Failed to set input geometry UV" + u); return false; } } } if (colors != null && colors.Count > 0) { Vector3[] rgb = new Vector3[colors.Count]; float[] alpha = new float[colors.Count]; for (int i = 0; i < colors.Count; ++i) { rgb[i][0] = colors[i].r; rgb[i][1] = colors[i].g; rgb[i][2] = colors[i].b; alpha[i] = colors[i].a; } //if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_COLOR, 3, rgb, ref partInfo, false)) if (!HEU_InputMeshUtility.SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_COLOR, 3, rgb, vertIndices, ref partInfo, false)) { Debug.LogError("Failed to set input geometry colors."); return false; } //if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_ALPHA, 1, alpha, ref partInfo, false)) if (!HEU_InputMeshUtility.SetMeshVertexFloatAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_ALPHA, 1, alpha, vertIndices, ref partInfo)) { Debug.LogError("Failed to set input geometry color alpha."); return false; } } // Set material names for round-trip perservation of material assignment // Each HEU_UploadMeshData might have a list of submeshes and materials // These are all combined into a single mesh, with group names if (numMaterials > 0) { bool bFoundAtleastOneValidMaterial = false; string[] materialIDs = new string[partInfo.faceCount]; for (int g = 0; g < inputDataMeshes._inputMeshes.Count; ++g) { if (inputDataMeshes._inputMeshes[g]._numSubMeshes != inputDataMeshes._inputMeshes[g]._materials.Length) { // Number of submeshes should equal number of materials since materials determine submeshes continue; } for (int i = 0; i < inputDataMeshes._inputMeshes[g]._materials.Length; ++i) { string materialName = HEU_AssetDatabase.GetAssetPathWithSubAssetSupport(inputDataMeshes._inputMeshes[g]._materials[i]); if (materialName == null) { materialName = ""; } else if (materialName.StartsWith(HEU_Defines.DEFAULT_UNITY_BUILTIN_RESOURCES)) { materialName = HEU_AssetDatabase.GetUniqueAssetPathForUnityAsset(inputDataMeshes._inputMeshes[g]._materials[i]); } bFoundAtleastOneValidMaterial |= !string.IsNullOrEmpty(materialName); int faceStart = (int)inputDataMeshes._inputMeshes[g]._indexStart[i] / numVertsPerFace; int faceEnd = faceStart + ((int)inputDataMeshes._inputMeshes[g]._indexCount[i] / numVertsPerFace); for (int m = faceStart; m < faceEnd; ++m) { materialIDs[m] = materialName; } } } if (bFoundAtleastOneValidMaterial) { HAPI_AttributeInfo materialIDAttrInfo = new HAPI_AttributeInfo(); materialIDAttrInfo.exists = true; materialIDAttrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM; materialIDAttrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_STRING; materialIDAttrInfo.count = partInfo.faceCount; materialIDAttrInfo.tupleSize = 1; materialIDAttrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; if (!session.AddAttribute(displayNodeID, 0, HEU_PluginSettings.UnityMaterialAttribName, ref materialIDAttrInfo)) { Debug.LogError("Failed to add input geometry unity material name attribute."); return false; } if (!HEU_GeneralUtility.SetAttributeArray(displayNodeID, 0, HEU_PluginSettings.UnityMaterialAttribName, ref materialIDAttrInfo, materialIDs, session.SetAttributeStringData, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry unity material name."); return false; } } } // Set mesh name attribute HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); attrInfo.exists = true; attrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM; attrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_STRING; attrInfo.count = partInfo.faceCount; attrInfo.tupleSize = 1; attrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; if (session.AddAttribute(displayNodeID, 0, HEU_PluginSettings.UnityInputMeshAttr, ref attrInfo)) { string[] primitiveNameAttr = new string[partInfo.faceCount]; for (int g = 0; g < inputDataMeshes._inputMeshes.Count; ++g) { for (int i = 0; i < inputDataMeshes._inputMeshes[g]._numSubMeshes; ++i) { int faceStart = (int)inputDataMeshes._inputMeshes[g]._indexStart[i] / numVertsPerFace; int faceEnd = faceStart + ((int)inputDataMeshes._inputMeshes[g]._indexCount[i] / numVertsPerFace); for (int m = faceStart; m < faceEnd; ++m) { primitiveNameAttr[m] = inputDataMeshes._inputMeshes[g]._meshPath; } } } if (!HEU_GeneralUtility.SetAttributeArray(displayNodeID, 0, HEU_PluginSettings.UnityInputMeshAttr, ref attrInfo, primitiveNameAttr, session.SetAttributeStringData, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry unity mesh name."); return false; } } else { return false; } // Set LOD group membership if (inputDataMeshes._hasLOD) { int[] membership = new int[partInfo.faceCount]; for (int g = 0; g < inputDataMeshes._inputMeshes.Count; ++g) { if (g > 0) { // Clear array for (int m = 0; m < partInfo.faceCount; ++m) { membership[m] = 0; } } // Set 1 for faces belonging to this group for (int s = 0; s < inputDataMeshes._inputMeshes[g]._numSubMeshes; ++s) { int faceStart = (int)inputDataMeshes._inputMeshes[g]._indexStart[s] / numVertsPerFace; int faceEnd = faceStart + ((int)inputDataMeshes._inputMeshes[g]._indexCount[s] / numVertsPerFace); for (int m = faceStart; m < faceEnd; ++m) { membership[m] = 1; } } if (!session.AddGroup(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_PRIM, inputDataMeshes._inputMeshes[g]._meshName)) { Debug.LogError("Failed to add input geometry LOD group name."); return false; } if (!session.SetGroupMembership(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_PRIM, inputDataMeshes._inputMeshes[g]._meshName, membership, 0, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry LOD group name."); return false; } } } return session.CommitGeo(displayNodeID); }
/// <summary> /// Upload the base height layer into heightfield network. /// </summary> /// <param name="session"></param> /// <param name="idt"></param> /// <returns></returns> public bool UploadHeightValuesWithTransform(HEU_SessionBase session, HEU_InputDataTerrain idt) { // Get Geo, Part, and Volume infos HAPI_GeoInfo geoInfo = new HAPI_GeoInfo(); if (!session.GetGeoInfo(idt._heightNodeID, ref geoInfo)) { Debug.LogError("Unable to get geo info from heightfield node!"); return false; } HAPI_PartInfo partInfo = new HAPI_PartInfo(); if (!session.GetPartInfo(geoInfo.nodeId, 0, ref partInfo)) { Debug.LogError("Unable to get part info from heightfield node!"); return false; } HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); if (!session.GetVolumeInfo(idt._heightNodeID, 0, ref volumeInfo)) { Debug.LogError("Unable to get volume info from heightfield node!"); return false; } if (volumeInfo.xLength != Mathf.RoundToInt(idt._numPointsX / idt._voxelSize) || volumeInfo.yLength != Mathf.RoundToInt(idt._numPointsY / idt._voxelSize) || idt._terrainData.heightmapResolution != volumeInfo.xLength || idt._terrainData.heightmapResolution != volumeInfo.yLength) { Debug.LogError("Created heightfield in Houdini differs in voxel size from input terrain!"); return false; } // Update volume infos, and set it. This is required. volumeInfo.tileSize = 1; volumeInfo.type = HAPI_VolumeType.HAPI_VOLUMETYPE_HOUDINI; volumeInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_FLOAT; volumeInfo.transform = idt._transform; volumeInfo.minX = 0; volumeInfo.minY = 0; volumeInfo.minZ = 0; volumeInfo.tupleSize = 1; volumeInfo.tileSize = 1; volumeInfo.hasTaper = false; volumeInfo.xTaper = 0f; volumeInfo.yTaper = 0f; if (!session.SetVolumeInfo(idt._heightNodeID, partInfo.id, ref volumeInfo)) { Debug.LogError("Unable to set volume info on input heightfield node!"); return false; } // Now set the height data float[,] heights = idt._terrainData.GetHeights(0, 0, volumeInfo.xLength, volumeInfo.yLength); int sizeX = heights.GetLength(0); int sizeY = heights.GetLength(1); int totalSize = sizeX * sizeY; // Convert to single array float[] heightsArr = new float[totalSize]; for (int j = 0; j < sizeY; j++) { for (int i = 0; i < sizeX; i++) { // Flip for coordinate system change float h = heights[i, (sizeY - j - 1)]; heightsArr[i + j * sizeX] = h * idt._heightScale; } } // Set the base height layer if (!session.SetHeightFieldData(idt._heightNodeID, 0, "height", heightsArr, 0, totalSize)) { Debug.LogError("Unable to set height values on input heightfield node!"); return false; } if (!session.CommitGeo(idt._heightNodeID)) { Debug.LogError("Unable to commit geo on input heightfield node!"); return false; } return true; }
private bool UploadData(HEU_SessionBase session, HAPI_NodeId inputNodeID, HEU_InputData inputData) { if (settings == null) { HEU_Logger.LogError("Tilemap Settings not found!"); return(false); } HEU_InputDataTilemap inputTilemap = inputData as HEU_InputDataTilemap; if (inputTilemap == null) { HEU_Logger.LogError("Expected HEU_InputDataTilemap type for inputData, but received unssupported type."); return(false); } List <Vector3> vertices = new List <Vector3>(); List <Vector3> colors = new List <Vector3>(); List <string> tileNames = new List <string>(); List <Vector3> tileSizes = new List <Vector3>(); List <Vector3Int> tileCoords = new List <Vector3Int>(); Tilemap tileMap = inputTilemap._tilemap; if (!tileMap.gameObject.activeInHierarchy) { HEU_Logger.LogWarning("Tilemap inputs must be active in the hierarchy in order to properly send input data"); } Grid gridLayout = tileMap.layoutGrid; Matrix4x4 orientation = tileMap.orientationMatrix; Vector3 orientationPosition = orientation.DecomposeToPosition(); Vector3 orientationRotationEuler = orientation.DecomposeToRotation().eulerAngles; orientationRotationEuler.y = -orientationRotationEuler.y; orientationRotationEuler.z = -orientationRotationEuler.z; Quaternion orientationRotation = Quaternion.Euler(orientationRotationEuler); Vector3 orientationScale = orientation.DecomposeToScale(); List <float> pointOrient = new List <float>(); List <Vector3> pointScale = new List <Vector3>(); TileBase[] tileArray = tileMap.GetTilesBlock(tileMap.cellBounds); int tileCount = 0; Vector3 anchorOffset = tileMap.tileAnchor; anchorOffset.Scale(gridLayout.cellSize); Vector3 pointPos; Vector3Int boundsMin = new Vector3Int(int.MaxValue, int.MaxValue, int.MaxValue); Vector3Int boundsMax = new Vector3Int(int.MinValue, int.MinValue, int.MinValue); foreach (Vector3Int tilePos in tileMap.cellBounds.allPositionsWithin) { if (tileMap.HasTile(tilePos)) { boundsMin = Vector3Int.Min(tilePos, boundsMin); boundsMax = Vector3Int.Max(tilePos, boundsMax); } } boundsMax += Vector3Int.one; BoundsInt tileMapBounds = new BoundsInt { min = boundsMin, max = boundsMax }; foreach (Vector3Int tilePos in tileMapBounds.allPositionsWithin) { if (!settings._exportUnusedTiles && !tileMap.HasTile(tilePos)) { continue; } Vector3Int usedTilePos = tilePos; //For Houdini (to use Labs Wang Tile tools, we need to reverse point order on the x axis) //so we just iterate in reverse order on the x //usedTilePos.x = tileMapBounds.size.x - 1 - tilePos.x + 2 * tileMapBounds.min.x; tileCount++; pointPos = tileMap.CellToLocal(usedTilePos) + anchorOffset; if (settings._applyTilemapOrientation) { pointPos += orientationPosition; pointOrient.Add(orientationRotation[0]); pointOrient.Add(orientationRotation[1]); pointOrient.Add(orientationRotation[2]); pointOrient.Add(orientationRotation[3]); pointScale.Add(orientationScale); } vertices.Add(pointPos); if (tileMap.HasTile(usedTilePos)) { Tile tile = tileMap.GetTile <Tile>(usedTilePos); tileNames.Add(tile.name); if (settings._applyTileColor) { colors.Add(new Vector3(tile.color.r, tile.color.g, tile.color.b)); } tileSizes.Add(new Vector3(tile.sprite.rect.size.x / tile.sprite.pixelsPerUnit, tile.sprite.rect.size.y / tile.sprite.pixelsPerUnit, 0.0f)); } else { tileNames.Add(""); if (settings._applyTileColor) { colors.Add(Vector3.zero); } tileSizes.Add(Vector3.zero); } tileCoords.Add(usedTilePos); } HAPI_PartInfo partInfo = new HAPI_PartInfo(); partInfo.faceCount = 0; partInfo.vertexCount = 0; partInfo.pointCount = tileCount; partInfo.pointAttributeCount = 1; partInfo.vertexAttributeCount = 0; partInfo.primitiveAttributeCount = 0; partInfo.detailAttributeCount = 1; if (tileSizes.Count > 0) { partInfo.pointAttributeCount++; } if (settings._applyTileColor && colors.Count > 0) { partInfo.pointAttributeCount++; } if (tileCoords.Count > 0) { partInfo.pointAttributeCount++; } if (pointOrient.Count > 0) { partInfo.pointAttributeCount++; } if (pointScale.Count > 0) { partInfo.pointAttributeCount++; } if (!settings._createGroupsForTiles && tileNames.Count > 0) { partInfo.pointAttributeCount++; } HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo(); if (!session.GetDisplayGeoInfo(inputNodeID, ref displayGeoInfo)) { return(false); } HAPI_NodeId displayNodeID = displayGeoInfo.nodeId; if (!session.SetPartInfo(displayNodeID, 0, ref partInfo)) { Debug.LogError("Failed to set input part info. "); return(false); } if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_HAPIConstants.HAPI_ATTRIB_POSITION, 3, vertices.ToArray(), ref partInfo, true)) { Debug.LogError("Failed to set point positions."); return(false); } if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, "unity_tile_size", 2, tileSizes.ToArray(), ref partInfo, false)) { Debug.Log("Failed to set tile size attributes. "); return(false); } if (settings._applyTileColor) { if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_HAPIConstants.HAPI_ATTRIB_COLOR, 3, colors.ToArray(), ref partInfo, false)) { Debug.Log("Failed to set tile color attributes. "); return(false); } } if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, "unity_tile_pos", 2, tileCoords.ToArray(), ref partInfo)) { Debug.Log("Failed to set point tile coordinates attributes."); return(false); } if (settings._createGroupsForTiles) { //Get a list of unique tiles used TileBase[] usedTiles = new TileBase[tileMap.GetUsedTilesCount()]; tileMap.GetUsedTilesNonAlloc(usedTiles); //Set point groups based on tile type int[] pointGroupMembership = new int[tileCount]; foreach (TileBase tileType in usedTiles) { if (!session.AddGroup(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_POINT, tileType.name)) { return(false); } int index = 0; foreach (string tileName in tileNames) { if (tileName.Equals(tileType.name)) { pointGroupMembership[index] = 1; } else { pointGroupMembership[index] = 0; } index++; } if (!session.SetGroupMembership(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_POINT, tileType.name, pointGroupMembership, 0, tileCount)) { return(false); } } } else { if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, "unity_tile_name", tileNames.ToArray(), ref partInfo)) { Debug.Log("Failed to set point tile name attributes."); return(false); } } if (!HEU_InputMeshUtility.SetMeshDetailAttribute(session, displayNodeID, 0, "unity_tile_bounds", 2, tileMapBounds.size, ref partInfo)) { Debug.Log("Failed to set detail tile map bounds attribute."); return(false); } if (settings._applyTilemapOrientation) { if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_ORIENT, 4, pointOrient.ToArray(), ref partInfo)) { Debug.LogError("Failed to set point rotations."); return(false); } if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_SCALE, 3, pointScale.ToArray(), ref partInfo, false)) { Debug.LogError("Failed to set point scales."); return(false); } } return(session.CommitGeo(displayNodeID)); }
// 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])); } }
public void SetGeoInfo(HAPI_GeoInfo geoInfo) { _geoInfo = geoInfo; }
private bool UploadData( HEU_SessionBase session, HAPI_NodeId inputNodeID, HEU_InputData inputData) { HEU_InputDataTilemap inputTilemap = inputData as HEU_InputDataTilemap; if(inputTilemap == null) { Debug.LogError("Expected HEU_InputDataTilemap type for inputData, but received unssupported type."); return false; } List<Vector3> vertices = new List<Vector3>(); List<Vector3> colors = new List<Vector3>(); //List<Vector3> uvs = new List<Vector3>(); List<Vector3> normals = new List<Vector3>(); List<string> tileNames = new List<string>(); List<Vector3> tileSizes = new List<Vector3>(); List<Vector3Int> tileCoords = new List<Vector3Int>(); Tilemap tileMap = inputTilemap._tilemap; Grid gridLayout = tileMap.layoutGrid; //Get a list of unique tiles used TileBase[] usedTiles = new TileBase[ tileMap.GetUsedTilesCount() ]; tileMap.GetUsedTilesNonAlloc(usedTiles); TileBase[] tileArray = tileMap.GetTilesBlock( tileMap.cellBounds ); //tileArray = tileArray.Where( x => x != null).ToArray(); //only existing tiles int tileCount = 0; Vector3 anchorOffset = tileMap.tileAnchor; anchorOffset.Scale(gridLayout.cellSize); Vector3 pointPos; Vector3 pointNormal = new Vector3(0.0f, 0.0f, 1.0f); Vector3Int boundsMin = new Vector3Int(int.MaxValue, int.MaxValue, int.MaxValue); Vector3Int boundsMax = new Vector3Int(int.MinValue, int.MinValue, int.MinValue); foreach (Vector3Int tilePos in tileMap.cellBounds.allPositionsWithin) { if (tileMap.HasTile(tilePos)) { boundsMin = Vector3Int.Min(tilePos, boundsMin); boundsMax = Vector3Int.Max(tilePos, boundsMax); } } boundsMax += Vector3Int.one; BoundsInt tileMapBounds = new BoundsInt { min = boundsMin, max = boundsMax }; //foreach (Vector3Int tilePos in tileMap.cellBounds.allPositionsWithin) Vector3Int tilePosReverseX = new Vector3Int(); foreach(Vector3Int tilePos in tileMapBounds.allPositionsWithin) { #if !EXPORT_RECT_GRID if(!tileMap.HasTile(tilePos)) continue; #endif tilePosReverseX = tilePos; //For Hudini (to use Labs Wang Tile tools, we need to reverse point order on the x axis) //so we just iterate in reverse order on the x tilePosReverseX.x = tileMapBounds.size.x - 1 - tilePos.x + 2 * tileMapBounds.min.x; tileCount++; pointPos = tileMap.CellToLocal(tilePosReverseX) + anchorOffset; vertices.Add(pointPos); normals.Add(pointNormal); if (tileMap.HasTile(tilePosReverseX)) { Tile tile = tileMap.GetTile<Tile>(tilePosReverseX); tileNames.Add(tile.name); colors.Add(new Vector3(tile.color.r, tile.color.g, tile.color.b)); tileSizes.Add(new Vector3(tile.sprite.rect.size.x / tile.sprite.pixelsPerUnit, tile.sprite.rect.size.y / tile.sprite.pixelsPerUnit, 0.0f)); } else { tileNames.Add(""); colors.Add(Vector3.zero); tileSizes.Add(Vector3.zero); } tileCoords.Add(tilePosReverseX); } HAPI_PartInfo partInfo = new HAPI_PartInfo(); partInfo.faceCount = 0; partInfo.vertexCount = 0; partInfo.pointCount = tileCount; partInfo.pointAttributeCount = 1; partInfo.vertexAttributeCount = 0; partInfo.primitiveAttributeCount = 0; partInfo.detailAttributeCount = 0; HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo(); if(!session.GetDisplayGeoInfo(inputNodeID, ref displayGeoInfo)) { return false; } HAPI_NodeId displayNodeID = displayGeoInfo.nodeId; if(!session.SetPartInfo(displayNodeID, 0, ref partInfo)) { Debug.LogError("Failed to set input part info. "); return false; } if(!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_POSITION, 3, vertices.ToArray(), ref partInfo, true)) { Debug.LogError("Failed to set point positions."); return false; } if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, "size", 2, tileSizes.ToArray(), ref partInfo, false)) { Debug.Log("Failed to set tile size attributes. "); return false; } if(!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_COLOR, 3, colors.ToArray(), ref partInfo, false)) { Debug.Log("Failed to set tile color attributes. "); return false; } if(!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_NORMAL, 3, normals.ToArray(), ref partInfo, true)) { Debug.Log("Failed to set point normal attributes."); return false; } if(!HEU_InputMeshUtilityExt.SetMeshPointAttribute(session, displayNodeID, 0, "tilepos", 2, tileCoords.ToArray(), ref partInfo)) { Debug.Log("Failed to set point tile coordinates attributes."); return false; } #if TILENAME_GROUPS //Set point groups based on tile type int[] pointGroupMembership = new int[tileCount]; foreach(TileBase tileType in usedTiles) { if(!session.AddGroup( displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_POINT, tileType.name)) return false; int index = 0; foreach( string tileName in tileNames) { if(tileName.Equals(tileType.name)) pointGroupMembership[index] = 1; else pointGroupMembership[index] = 0; index++; } if(!session.SetGroupMembership(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_POINT, tileType.name, pointGroupMembership, 0, tileCount)) return false; } #else if(!HEU_InputMeshUtilityExt.SetMeshPointAttribute(session, displayNodeID, 0, "tilename", tileNames.ToArray(), ref partInfo)) { Debug.Log("Failed to set point tile name attributes."); return false; } #endif if(!HEU_InputMeshUtilityExt.SetMeshDetailAttribute(session, displayNodeID, 0, "bounds", 2, tileMapBounds.size, ref partInfo)) { Debug.Log("Failed to set detail tile map bounds attribute."); return false; } return session.CommitGeo(displayNodeID); }
/// <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> /// Get the geometry info on a SOP node. /// </summary> /// <param name="nodeID">The SOP node ID</param> /// <param name="geoInfo">Geo info to populate</param> /// <returns>True if successfully queried the geo info</returns> public virtual bool GetGeoInfo(HAPI_NodeId nodeID, ref HAPI_GeoInfo geoInfo) { return false; }
/// <summary> /// Upload the inputData (mesh geometry) into the input node with inputNodeID. /// </summary> /// <param name="session">Session that the input node exists in</param> /// <param name="inputNodeID">ID of the input node</param> /// <param name="inputData">Container of the mesh geometry</param> /// <returns>True if successfully uploaded data</returns> public bool UploadData(HEU_SessionBase session, HAPI_NodeId inputNodeID, HEU_InputData inputData) { HEU_InputDataMeshes inputDataMeshes = inputData as HEU_InputDataMeshes; if (inputDataMeshes == null) { Debug.LogError("Expected HEU_InputDataMeshes type for inputData, but received unsupported type."); return false; } List<Vector3> vertices = new List<Vector3>(); List<Vector3> normals = new List<Vector3>(); List<Vector2> uvs = new List<Vector2>(); List<Color> colors = new List<Color>(); List<int> pointIndexList = new List<int>(); List<int> vertIndexList = new List<int>(); int numMaterials = 0; int numMeshes = inputDataMeshes._inputMeshes.Count; // Get the parent's world transform, so when there are multiple child meshes, // can merge and apply their local transform after subtracting their parent's world transform Matrix4x4 rootInvertTransformMatrix = Matrix4x4.identity; if (numMeshes > 1) { rootInvertTransformMatrix = inputDataMeshes._inputObject.transform.worldToLocalMatrix; } // For all meshes: // Accumulate vertices, normals, uvs, colors, and indices. // Keep track of indices start and count for each mesh for later when uploading material assignments and groups. // Find shared vertices, and use unique set of vertices to use as point positions. // Need to reindex indices for both unique vertices, as well as vertex attributes. for (int i = 0; i < numMeshes; ++i) { Vector3[] meshVertices = inputDataMeshes._inputMeshes[i]._mesh.vertices; Matrix4x4 localToWorld = inputDataMeshes._inputMeshes[i]._transform.localToWorldMatrix * rootInvertTransformMatrix; List<Vector3> uniqueVertices = new List<Vector3>(); // Keep track of old vertex positions (old vertex slot points to new unique vertex slot) int[] reindexVertices = new int[meshVertices.Length]; for (int j = 0; j < meshVertices.Length; ++j) { reindexVertices[j] = -1; } // For each vertex, check against subsequent vertices for shared positions. for (int a = 0; a < meshVertices.Length; ++a) { Vector3 va = meshVertices[a]; if (reindexVertices[a] == -1) { if (numMeshes > 1 && !inputDataMeshes._hasLOD) { // For multiple meshes that are not LODs, apply local transform on vertices to get the merged mesh. uniqueVertices.Add(localToWorld.MultiplyPoint(va)); } else { uniqueVertices.Add(va); } // Reindex to point to unique vertex slot reindexVertices[a] = uniqueVertices.Count - 1; } for (int b = a + 1; b < meshVertices.Length; ++b) { if (va == meshVertices[b]) { // Shared vertex -> reindex to point to unique vertex slot reindexVertices[b] = reindexVertices[a]; } } } int vertexOffset = vertices.Count; vertices.AddRange(uniqueVertices); Vector3[] meshNormals = inputDataMeshes._inputMeshes[i]._mesh.normals; Vector2[] meshUVs = inputDataMeshes._inputMeshes[i]._mesh.uv; Color[] meshColors = inputDataMeshes._inputMeshes[i]._mesh.colors; inputDataMeshes._inputMeshes[i]._indexStart = new uint[inputDataMeshes._inputMeshes[i]._numSubMeshes]; inputDataMeshes._inputMeshes[i]._indexCount = new uint[inputDataMeshes._inputMeshes[i]._numSubMeshes]; // For each submesh: // Generate face to point index -> pointIndexList // Generate face to vertex attribute index -> vertIndexList for (int j = 0; j < inputDataMeshes._inputMeshes[i]._numSubMeshes; ++j) { int indexStart = pointIndexList.Count; int vertIndexStart = vertIndexList.Count; // Indices have to be re-indexed with our own offset int[] meshIndices = inputDataMeshes._inputMeshes[i]._mesh.GetTriangles(j); int numIndices = meshIndices.Length; for (int k = 0; k < numIndices; ++k) { int originalIndex = meshIndices[k]; meshIndices[k] = reindexVertices[originalIndex]; pointIndexList.Add(vertexOffset + meshIndices[k]); vertIndexList.Add(vertIndexStart + k); if (meshNormals != null && (originalIndex < meshNormals.Length)) { normals.Add(meshNormals[originalIndex]); } if (meshUVs != null && (originalIndex < meshUVs.Length)) { uvs.Add(meshUVs[originalIndex]); } if (meshColors != null && (originalIndex < meshColors.Length)) { colors.Add(meshColors[originalIndex]); } } inputDataMeshes._inputMeshes[i]._indexStart[j] = (uint)indexStart; inputDataMeshes._inputMeshes[i]._indexCount[j] = (uint)(pointIndexList.Count) - inputDataMeshes._inputMeshes[i]._indexStart[j]; } numMaterials += inputDataMeshes._inputMeshes[i]._materials != null ? inputDataMeshes._inputMeshes[i]._materials.Length : 0; } // It is possible for some meshes to not have normals/uvs/colors while others do. // In the case where an attribute is missing on some meshes, we clear out those attributes so we don't upload // partial attribute data. int totalAllVertexCount = vertIndexList.Count; if (normals.Count != totalAllVertexCount) { normals = null; } if (uvs.Count != totalAllVertexCount) { uvs = null; } if (colors.Count != totalAllVertexCount) { colors = null; } HAPI_PartInfo partInfo = new HAPI_PartInfo(); partInfo.faceCount = vertIndexList.Count / 3; partInfo.vertexCount = vertIndexList.Count; partInfo.pointCount = vertices.Count; partInfo.pointAttributeCount = 1; partInfo.vertexAttributeCount = 0; partInfo.primitiveAttributeCount = 0; partInfo.detailAttributeCount = 0; if (normals != null && normals.Count > 0) { partInfo.vertexAttributeCount++; } if (uvs != null && uvs.Count > 0) { partInfo.vertexAttributeCount++; } if (colors != null && colors.Count > 0) { partInfo.vertexAttributeCount++; } if (numMaterials > 0) { partInfo.primitiveAttributeCount++; } if (numMeshes > 0) { partInfo.primitiveAttributeCount++; } if (inputDataMeshes._hasLOD) { partInfo.primitiveAttributeCount++; partInfo.detailAttributeCount++; } HAPI_GeoInfo displayGeoInfo = new HAPI_GeoInfo(); if (!session.GetDisplayGeoInfo(inputNodeID, ref displayGeoInfo)) { return false; } HAPI_NodeId displayNodeID = displayGeoInfo.nodeId; if (!session.SetPartInfo(displayNodeID, 0, ref partInfo)) { Debug.LogError("Failed to set input part info. "); return false; } int[] faceCounts = new int[partInfo.faceCount]; for (int i = 0; i < partInfo.faceCount; ++i) { faceCounts[i] = 3; } int[] triIndices = pointIndexList.ToArray(); if (!HEU_GeneralUtility.SetArray2Arg(displayNodeID, 0, session.SetFaceCount, faceCounts, 0, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry face counts."); return false; } if (!HEU_GeneralUtility.SetArray2Arg(displayNodeID, 0, session.SetVertexList, triIndices, 0, partInfo.vertexCount)) { Debug.LogError("Failed to set input geometry indices."); return false; } if (!HEU_InputMeshUtility.SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_POSITION, 3, vertices.ToArray(), ref partInfo, true)) { Debug.LogError("Failed to set input geometry position."); return false; } int[] vertIndices = vertIndexList.ToArray(); //if(normals != null && !SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_NORMAL, 3, normals.ToArray(), ref partInfo, true)) if (normals != null && !HEU_InputMeshUtility.SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_NORMAL, 3, normals.ToArray(), vertIndices, ref partInfo, true)) { Debug.LogError("Failed to set input geometry normals."); return false; } if (uvs != null && uvs.Count > 0) { Vector3[] uvs3 = new Vector3[uvs.Count]; for (int i = 0; i < uvs.Count; ++i) { uvs3[i][0] = uvs[i][0]; uvs3[i][1] = uvs[i][1]; uvs3[i][2] = 0; } //if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_UV, 3, uvs3, ref partInfo, false)) if (!HEU_InputMeshUtility.SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_UV, 3, uvs3, vertIndices, ref partInfo, false)) { Debug.LogError("Failed to set input geometry UVs."); return false; } } if (colors != null && colors.Count > 0) { Vector3[] rgb = new Vector3[colors.Count]; float[] alpha = new float[colors.Count]; for (int i = 0; i < colors.Count; ++i) { rgb[i][0] = colors[i].r; rgb[i][1] = colors[i].g; rgb[i][2] = colors[i].b; alpha[i] = colors[i].a; } //if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_COLOR, 3, rgb, ref partInfo, false)) if (!HEU_InputMeshUtility.SetMeshVertexAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_COLOR, 3, rgb, vertIndices, ref partInfo, false)) { Debug.LogError("Failed to set input geometry colors."); return false; } //if(!SetMeshPointAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_ALPHA, 1, alpha, ref partInfo, false)) if (!HEU_InputMeshUtility.SetMeshVertexFloatAttribute(session, displayNodeID, 0, HEU_Defines.HAPI_ATTRIB_ALPHA, 1, alpha, vertIndices, ref partInfo)) { Debug.LogError("Failed to set input geometry color alpha."); return false; } } // Set material names for round-trip perservation of material assignment // Each HEU_UploadMeshData might have a list of submeshes and materials // These are all combined into a single mesh, with group names if (numMaterials > 0) { bool bFoundAtleastOneValidMaterial = false; string[] materialIDs = new string[partInfo.faceCount]; for (int g = 0; g < inputDataMeshes._inputMeshes.Count; ++g) { if (inputDataMeshes._inputMeshes[g]._numSubMeshes != inputDataMeshes._inputMeshes[g]._materials.Length) { // Number of submeshes should equal number of materials since materials determine submeshes continue; } for (int i = 0; i < inputDataMeshes._inputMeshes[g]._materials.Length; ++i) { string materialName = HEU_AssetDatabase.GetAssetPathWithSubAssetSupport(inputDataMeshes._inputMeshes[g]._materials[i]); if (materialName == null) { materialName = ""; } else if (materialName.StartsWith(HEU_Defines.DEFAULT_UNITY_BUILTIN_RESOURCES)) { materialName = HEU_AssetDatabase.GetUniqueAssetPathForUnityAsset(inputDataMeshes._inputMeshes[g]._materials[i]); } bFoundAtleastOneValidMaterial |= !string.IsNullOrEmpty(materialName); int faceStart = (int)inputDataMeshes._inputMeshes[g]._indexStart[i] / 3; int faceEnd = faceStart + ((int)inputDataMeshes._inputMeshes[g]._indexCount[i] / 3); for (int m = faceStart; m < faceEnd; ++m) { materialIDs[m] = materialName; } } } if (bFoundAtleastOneValidMaterial) { HAPI_AttributeInfo materialIDAttrInfo = new HAPI_AttributeInfo(); materialIDAttrInfo.exists = true; materialIDAttrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM; materialIDAttrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_STRING; materialIDAttrInfo.count = partInfo.faceCount; materialIDAttrInfo.tupleSize = 1; materialIDAttrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; if (!session.AddAttribute(displayNodeID, 0, HEU_PluginSettings.UnityMaterialAttribName, ref materialIDAttrInfo)) { Debug.LogError("Failed to add input geometry unity material name attribute."); return false; } if (!HEU_GeneralUtility.SetAttributeArray(displayNodeID, 0, HEU_PluginSettings.UnityMaterialAttribName, ref materialIDAttrInfo, materialIDs, session.SetAttributeStringData, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry unity material name."); return false; } } } // Set mesh name attribute HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); attrInfo.exists = true; attrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM; attrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_STRING; attrInfo.count = partInfo.faceCount; attrInfo.tupleSize = 1; attrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; if (session.AddAttribute(displayNodeID, 0, HEU_PluginSettings.UnityInputMeshAttr, ref attrInfo)) { string[] primitiveNameAttr = new string[partInfo.faceCount]; for (int g = 0; g < inputDataMeshes._inputMeshes.Count; ++g) { for (int i = 0; i < inputDataMeshes._inputMeshes[g]._numSubMeshes; ++i) { int faceStart = (int)inputDataMeshes._inputMeshes[g]._indexStart[i] / 3; int faceEnd = faceStart + ((int)inputDataMeshes._inputMeshes[g]._indexCount[i] / 3); for (int m = faceStart; m < faceEnd; ++m) { primitiveNameAttr[m] = inputDataMeshes._inputMeshes[g]._meshPath; } } } if (!HEU_GeneralUtility.SetAttributeArray(displayNodeID, 0, HEU_PluginSettings.UnityInputMeshAttr, ref attrInfo, primitiveNameAttr, session.SetAttributeStringData, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry unity mesh name."); return false; } } else { return false; } // Set LOD group membership if (inputDataMeshes._hasLOD) { int[] membership = new int[partInfo.faceCount]; for (int g = 0; g < inputDataMeshes._inputMeshes.Count; ++g) { if (g > 0) { // Clear array for (int m = 0; m < partInfo.faceCount; ++m) { membership[m] = 0; } } // Set 1 for faces belonging to this group for (int s = 0; s < inputDataMeshes._inputMeshes[g]._numSubMeshes; ++s) { int faceStart = (int)inputDataMeshes._inputMeshes[g]._indexStart[s] / 3; int faceEnd = faceStart + ((int)inputDataMeshes._inputMeshes[g]._indexCount[s] / 3); for (int m = faceStart; m < faceEnd; ++m) { membership[m] = 1; } } if (!session.AddGroup(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_PRIM, inputDataMeshes._inputMeshes[g]._meshName)) { Debug.LogError("Failed to add input geometry LOD group name."); return false; } if (!session.SetGroupMembership(displayNodeID, 0, HAPI_GroupType.HAPI_GROUPTYPE_PRIM, inputDataMeshes._inputMeshes[g]._meshName, membership, 0, partInfo.faceCount)) { Debug.LogError("Failed to set input geometry LOD group name."); return false; } } } return session.CommitGeo(displayNodeID); }
/// <summary> /// Upload the base height layer into heightfield network. /// </summary> /// <param name="session"></param> /// <param name="idt"></param> /// <returns></returns> public bool UploadHeightValuesWithTransform(HEU_SessionBase session, HEU_InputDataTerrain idt, ref HAPI_VolumeInfo volumeInfo) { // Get Geo, Part, and Volume infos HAPI_GeoInfo geoInfo = new HAPI_GeoInfo(); if (!session.GetGeoInfo(idt._heightNodeID, ref geoInfo)) { HEU_Logger.LogError("Unable to get geo info from heightfield node!"); return(false); } HAPI_PartInfo partInfo = new HAPI_PartInfo(); if (!session.GetPartInfo(geoInfo.nodeId, 0, ref partInfo)) { HEU_Logger.LogError("Unable to get part info from heightfield node!"); return(false); } volumeInfo = new HAPI_VolumeInfo(); if (!session.GetVolumeInfo(idt._heightNodeID, 0, ref volumeInfo)) { HEU_Logger.LogError("Unable to get volume info from heightfield node!"); return(false); } if ((volumeInfo.xLength - 1) != Mathf.RoundToInt(idt._numPointsX / idt._voxelSize) || (volumeInfo.yLength - 1) != Mathf.RoundToInt(idt._numPointsY / idt._voxelSize) || idt._terrainData.heightmapResolution != volumeInfo.xLength || idt._terrainData.heightmapResolution != volumeInfo.yLength) { HEU_Logger.LogWarning("Created heightfield in Houdini differs in voxel size from input terrain! Terrain may require resampling."); } // Update volume infos, and set it. This is required. volumeInfo.tileSize = 1; volumeInfo.type = HAPI_VolumeType.HAPI_VOLUMETYPE_HOUDINI; volumeInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_FLOAT; volumeInfo.transform = idt._transform; volumeInfo.minX = 0; volumeInfo.minY = 0; volumeInfo.minZ = 0; volumeInfo.tupleSize = 1; volumeInfo.tileSize = 1; volumeInfo.hasTaper = false; volumeInfo.xTaper = 0f; volumeInfo.yTaper = 0f; if (!session.SetVolumeInfo(idt._heightNodeID, partInfo.id, ref volumeInfo)) { HEU_Logger.LogError("Unable to set volume info on input heightfield node!"); return(false); } // Now set the height data float[,] heights = idt._terrainData.GetHeights(0, 0, idt._terrainData.heightmapResolution, idt._terrainData.heightmapResolution); int sizeX = heights.GetLength(0); int sizeY = heights.GetLength(1); int totalSize = sizeX * sizeY; // Convert to single array float[] heightsArr = new float[totalSize]; for (int j = 0; j < sizeY; j++) { for (int i = 0; i < sizeX; i++) { // Flip for coordinate system change float h = heights[i, (sizeY - j - 1)]; heightsArr[i + j * sizeX] = h * idt._heightScale; } } if (volumeInfo.xLength != volumeInfo.yLength) { HEU_Logger.LogError("Error: Houdini heightmap must be square!"); return(false); } if (idt._terrainData.heightmapResolution != volumeInfo.xLength) { // Resize heightsArr to idt._terrainData.heightmapResolution HEU_Logger.LogWarningFormat("Attempting to resize landscape from ({0}x{1}) to ({2}x{3})", idt._terrainData.heightmapResolution, idt._terrainData.heightmapResolution, volumeInfo.xLength, volumeInfo.xLength); heightsArr = HEU_TerrainUtility.ResampleData(heightsArr, idt._terrainData.heightmapResolution, idt._terrainData.heightmapResolution, volumeInfo.xLength, volumeInfo.xLength); sizeX = volumeInfo.xLength; sizeY = volumeInfo.yLength; totalSize = sizeX * sizeY; } // Set the base height layer if (!session.SetHeightFieldData(idt._heightNodeID, 0, HEU_Defines.HAPI_HEIGHTFIELD_LAYERNAME_HEIGHT, heightsArr, 0, totalSize)) { HEU_Logger.LogError("Unable to set height values on input heightfield node!"); return(false); } SetTerrainDataAttributesToHeightField(session, geoInfo.nodeId, 0, idt._terrainData); SetTreePrototypes(session, geoInfo.nodeId, 0, idt._terrainData); if (!session.CommitGeo(idt._heightNodeID)) { HEU_Logger.LogError("Unable to commit geo on input heightfield node!"); return(false); } return(true); }