public static bool SetMeshPointAttribute(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string attrName, int tupleSize, Vector3[] data, ref HAPI_PartInfo partInfo, bool bConvertToHoudiniCoordinateSystem) { HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); attrInfo.exists = true; attrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_POINT; attrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_FLOAT; attrInfo.count = partInfo.pointCount; attrInfo.tupleSize = tupleSize; attrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; float[] attrValues = new float[partInfo.pointCount * tupleSize]; if (session.AddAttribute(geoID, 0, attrName, ref attrInfo)) { float conversionMultiplier = bConvertToHoudiniCoordinateSystem ? -1f : 1f; for (int i = 0; i < partInfo.pointCount; ++i) { attrValues[i * tupleSize + 0] = conversionMultiplier * data[i][0]; for (int j = 1; j < tupleSize; ++j) { attrValues[i * tupleSize + j] = data[i][j]; } } } return HEU_GeneralUtility.SetAttributeArray(geoID, partID, attrName, ref attrInfo, attrValues, session.SetAttributeFloatData, partInfo.pointCount); }
//Set int point attributes public static bool SetMeshPointAttribute(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string attrName, int tupleSize, Vector3Int[] data, ref HAPI_PartInfo partInfo) { HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); attrInfo.exists = true; attrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_POINT; attrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_INT; attrInfo.count = partInfo.pointCount; attrInfo.tupleSize = tupleSize; attrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; int[] attrValues = new int[partInfo.pointCount * tupleSize]; if (session.AddAttribute(geoID, 0, attrName, ref attrInfo)) { for (int i = 0; i < partInfo.pointCount; ++i) { attrValues[i * tupleSize + 0] = data[i][0]; for (int j = 1; j < tupleSize; ++j) { attrValues[i * tupleSize + j] = data[i][j]; } } } return(HEU_GeneralUtility.SetAttributeArray(geoID, partID, attrName, ref attrInfo, attrValues, session.SetAttributeIntData, partInfo.pointCount)); }
public static bool SetMeshVertexFloatAttribute(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string attrName, int tupleSize, float[] data, int[] indices, ref HAPI_PartInfo partInfo) { HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); attrInfo.exists = true; attrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_VERTEX; attrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_FLOAT; attrInfo.count = partInfo.vertexCount; attrInfo.tupleSize = tupleSize; attrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; float[] attrValues = new float[partInfo.vertexCount * tupleSize]; if (session.AddAttribute(geoID, 0, attrName, ref attrInfo)) { for (int i = 0; i < partInfo.vertexCount; ++i) { for (int j = 0; j < tupleSize; ++j) { attrValues[i * tupleSize + j] = data[indices[i] * tupleSize + j]; } } } return HEU_GeneralUtility.SetAttributeArray(geoID, partID, attrName, ref attrInfo, attrValues, session.SetAttributeFloatData, partInfo.vertexCount); }
internal bool CreateInputNodeForCollider(HEU_SessionBase session, out HAPI_NodeId outNodeID, HAPI_NodeId parentNodeId, int colliderIndex, string colliderName, float[] colliderVertices, int[] colliderIndices) { outNodeID = HEU_Defines.HEU_INVALID_NODE_ID; HAPI_NodeId colliderNodeId = HEU_Defines.HEU_INVALID_NODE_ID; if (!session.CreateNode(parentNodeId, "null", colliderName, false, out colliderNodeId)) return false; HAPI_PartInfo partInfo = new HAPI_PartInfo(); partInfo.init(); partInfo.id = 0; partInfo.nameSH = 0; partInfo.attributeCounts[(int)HAPI_AttributeOwner.HAPI_ATTROWNER_POINT] = 0; partInfo.attributeCounts[(int)HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM] = 0; partInfo.attributeCounts[(int)HAPI_AttributeOwner.HAPI_ATTROWNER_VERTEX] = 0; partInfo.attributeCounts[(int)HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL] = 0; partInfo.vertexCount = colliderIndices.Length; partInfo.faceCount = colliderIndices.Length / 3; partInfo.pointCount = colliderVertices.Length / 3; partInfo.type = HAPI_PartType.HAPI_PARTTYPE_MESH; if (!session.SetPartInfo(colliderNodeId, 0, ref partInfo)) return false; HAPI_AttributeInfo attributeInfoPoint = new HAPI_AttributeInfo(); attributeInfoPoint.count = colliderVertices.Length / 3; attributeInfoPoint.tupleSize = 3; attributeInfoPoint.exists = true; attributeInfoPoint.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_POINT; attributeInfoPoint.storage = HAPI_StorageType.HAPI_STORAGETYPE_FLOAT; attributeInfoPoint.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; if (!session.AddAttribute(colliderNodeId, 0, HEU_HAPIConstants.HAPI_ATTRIB_POSITION, ref attributeInfoPoint)) return false; if (!session.SetAttributeFloatData(colliderNodeId, 0, HEU_HAPIConstants.HAPI_ATTRIB_POSITION, ref attributeInfoPoint, colliderVertices, 0, attributeInfoPoint.count)) return false; if (!session.SetVertexList(colliderNodeId, 0, colliderIndices, 0, colliderIndices.Length)) return false; int[] faceCounts = new int[partInfo.faceCount]; for (int i = 0; i < faceCounts.Length; i++) { faceCounts[i] = 3; } if (!session.SetFaceCount(colliderNodeId, 0, faceCounts, 0, faceCounts.Length)) return false; if (!session.CommitGeo(colliderNodeId)) return false; outNodeID = colliderNodeId; 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, 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); }
//Set string detail attribute public static bool SetMeshDetailAttribute(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string attrName, string data, ref HAPI_PartInfo partInfo) { HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); attrInfo.exists = true; attrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL; attrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_STRING; attrInfo.count = 1; attrInfo.tupleSize = 1; attrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; string[] values = new string[1]; values[0] = data; return(HEU_GeneralUtility.SetAttributeArray(geoID, partID, attrName, ref attrInfo, values, session.SetAttributeStringData, 1)); }
/// <summary> /// Get group membership /// </summary> /// <param name="nodeID"></param> /// <param name="partID"></param> /// <param name="groupType"></param> /// <param name="groupName"></param> /// <param name="membership">Array of ints representing the membership of this group</param> /// <returns>True if successfully queried the group membership</returns> public static bool GetGroupMembership(HEU_SessionBase session, HAPI_NodeId nodeID, HAPI_PartId partID, HAPI_GroupType groupType, string groupName, ref int[] membership) { HAPI_PartInfo partInfo = new HAPI_PartInfo(); bool bResult = session.GetPartInfo(nodeID, partID, ref partInfo); if (bResult) { int count = partInfo.getElementCountByGroupType(groupType); membership = new int[count]; if (count > 0) { bool membershipArrayAllEqual = false; session.GetGroupMembership(nodeID, partID, groupType, groupName, ref membershipArrayAllEqual, membership, 0, count); } return true; } return false; }
/// <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; }
//Set string point attributes public static bool SetMeshPointAttribute(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string attrName, string[] data, ref HAPI_PartInfo partInfo) { HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); attrInfo.exists = true; attrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_POINT; attrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_STRING; attrInfo.count = partInfo.pointCount; attrInfo.tupleSize = 1; attrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; if (!session.AddAttribute(geoID, 0, attrName, ref attrInfo)) { Debug.Log("Could not create attribute named: " + attrName); return(false); } return(HEU_GeneralUtility.SetAttributeArray(geoID, partID, attrName, ref attrInfo, data, session.SetAttributeStringData, partInfo.pointCount)); }
//Set float detail attribute public static bool SetMeshDetailAttribute(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string attrName, int tupleSize, Vector3 data, ref HAPI_PartInfo partInfo) { HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); attrInfo.exists = true; attrInfo.owner = HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL; attrInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_FLOAT; attrInfo.count = 1; attrInfo.tupleSize = tupleSize; attrInfo.originalOwner = HAPI_AttributeOwner.HAPI_ATTROWNER_INVALID; float[] attrValues = new float[tupleSize]; if (session.AddAttribute(geoID, 0, attrName, ref attrInfo)) { for (int j = 0; j < tupleSize; ++j) { attrValues[j] = data[j]; } } return(HEU_GeneralUtility.SetAttributeArray(geoID, partID, attrName, ref attrInfo, attrValues, session.SetAttributeFloatData, 1)); }
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> /// Uploads given mesh geometry into Houdini. /// Creates a new part for given geo node, and uploads vertices, indices, UVs, Normals, and Colors. /// </summary> /// <param name="session"></param> /// <param name="assetNodeID"></param> /// <param name="objectID"></param> /// <param name="geoID"></param> /// <param name="mesh"></param> /// <returns>True if successfully uploaded all required data.</returns> public static bool UploadMeshIntoHoudiniNode(HEU_SessionBase session, HAPI_NodeId assetNodeID, HAPI_NodeId objectID, HAPI_NodeId geoID, ref Mesh mesh) { bool bSuccess = false; Vector3[] vertices = mesh.vertices; int[] triIndices = mesh.triangles; Vector2[] uvs = mesh.uv; Vector3[] normals = mesh.normals; Color[] colors = mesh.colors; HAPI_PartInfo partInfo = new HAPI_PartInfo(); partInfo.faceCount = triIndices.Length / 3; partInfo.vertexCount = triIndices.Length; partInfo.pointCount = vertices.Length; partInfo.pointAttributeCount = 1; partInfo.vertexAttributeCount = 0; partInfo.primitiveAttributeCount = 0; partInfo.detailAttributeCount = 0; if (uvs != null && uvs.Length > 0) { partInfo.pointAttributeCount++; } if (normals != null && normals.Length > 0) { partInfo.pointAttributeCount++; } if (colors != null && colors.Length > 0) { partInfo.pointAttributeCount++; } bSuccess = session.SetPartInfo(geoID, 0, ref partInfo); if (!bSuccess) { return false; } int[] faceCounts = new int[partInfo.faceCount]; for (int i = 0; i < partInfo.faceCount; ++i) { faceCounts[i] = 3; } bSuccess = HEU_GeneralUtility.SetArray2Arg(geoID, 0, session.SetFaceCount, faceCounts, 0, partInfo.faceCount); if (!bSuccess) { return false; } int[] vertexList = new int[partInfo.vertexCount]; for (int i = 0; i < partInfo.faceCount; ++i) { for (int j = 0; j < 3; ++j) { vertexList[i * 3 + j] = triIndices[i * 3 + j]; } } bSuccess = HEU_GeneralUtility.SetArray2Arg(geoID, 0, session.SetVertexList, vertexList, 0, partInfo.vertexCount); if (!bSuccess) { return false; } bSuccess = HEU_InputMeshUtility.SetMeshPointAttribute(session, geoID, 0, HEU_Defines.HAPI_ATTRIB_POSITION, 3, vertices, ref partInfo, true); if (!bSuccess) { return false; } bSuccess = HEU_InputMeshUtility.SetMeshPointAttribute(session, geoID, 0, HEU_Defines.HAPI_ATTRIB_NORMAL, 3, normals, ref partInfo, true); if (!bSuccess) { return false; } if (uvs != null && uvs.Length > 0) { Vector3[] uvs3 = new Vector3[uvs.Length]; for (int i = 0; i < uvs.Length; ++i) { uvs3[i][0] = uvs[i][0]; uvs3[i][1] = uvs[i][1]; uvs3[i][2] = 0; } bSuccess = HEU_InputMeshUtility.SetMeshPointAttribute(session, geoID, 0, HEU_Defines.HAPI_ATTRIB_UV, 3, uvs3, ref partInfo, false); if (!bSuccess) { return false; } } if (colors != null && colors.Length > 0) { Vector3[] rgb = new Vector3[colors.Length]; Vector3[] alpha = new Vector3[colors.Length]; for (int i = 0; i < colors.Length; ++i) { rgb[i][0] = colors[i].r; rgb[i][1] = colors[i].g; rgb[i][2] = colors[i].b; alpha[i][0] = colors[i].a; } bSuccess = HEU_InputMeshUtility.SetMeshPointAttribute(session, geoID, 0, HEU_Defines.HAPI_ATTRIB_COLOR, 3, rgb, ref partInfo, false); if (!bSuccess) { return false; } bSuccess = HEU_InputMeshUtility.SetMeshPointAttribute(session, geoID, 0, HEU_Defines.HAPI_ATTRIB_ALPHA, 1, alpha, ref partInfo, false); if (!bSuccess) { return false; } } // TODO: additional attributes (for painting) return session.CommitGeo(geoID); }
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)); }
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> /// 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); }
private HEU_LoadBufferInstancer GeneratePointAttributeInstancerBuffer(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string partName, HAPI_PartInfo partInfo) { int numInstances = partInfo.pointCount; if (numInstances <= 0) { return null; } // Find type of instancer string instanceAttrName = HEU_PluginSettings.InstanceAttr; string unityInstanceAttrName = HEU_PluginSettings.UnityInstanceAttr; string instancePrefixAttrName = HEU_Defines.DEFAULT_INSTANCE_PREFIX_ATTR; HAPI_AttributeInfo instanceAttrInfo = new HAPI_AttributeInfo(); HAPI_AttributeInfo unityInstanceAttrInfo = new HAPI_AttributeInfo(); HAPI_AttributeInfo instancePrefixAttrInfo = new HAPI_AttributeInfo(); HEU_GeneralUtility.GetAttributeInfo(session, geoID, partID, instanceAttrName, ref instanceAttrInfo); HEU_GeneralUtility.GetAttributeInfo(session, geoID, partID, unityInstanceAttrName, ref unityInstanceAttrInfo); if (unityInstanceAttrInfo.exists) { // Object instancing via existing Unity object (path from point attribute) HAPI_Transform[] instanceTransforms = new HAPI_Transform[numInstances]; if (!HEU_GeneralUtility.GetArray3Arg(geoID, partID, HAPI_RSTOrder.HAPI_SRT, session.GetInstanceTransformsOnPart, instanceTransforms, 0, numInstances)) { return null; } string[] instancePrefixes = null; HEU_GeneralUtility.GetAttributeInfo(session, geoID, partID, instancePrefixAttrName, ref instancePrefixAttrInfo); if (instancePrefixAttrInfo.exists) { instancePrefixes = HEU_GeneralUtility.GetAttributeStringData(session, geoID, partID, instancePrefixAttrName, ref instancePrefixAttrInfo); } string[] assetPaths = null; // Attribute owner type determines whether to use single (detail) or multiple (point) asset(s) as source if (unityInstanceAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_POINT || unityInstanceAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL) { assetPaths = HEU_GeneralUtility.GetAttributeStringData(session, geoID, partID, unityInstanceAttrName, ref unityInstanceAttrInfo); } else { // Other attribute owned types are unsupported SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Unsupported attribute owner {0} for attribute {1}", unityInstanceAttrInfo.owner, unityInstanceAttrName)); return null; } if (assetPaths == null) { SetLog(HEU_LoadData.LoadStatus.ERROR, "Unable to get instanced asset path from attribute!"); return null; } HEU_LoadBufferInstancer instancerBuffer = new HEU_LoadBufferInstancer(); instancerBuffer.InitializeBuffer(partID, partName, partInfo.isInstanced, true); instancerBuffer._instanceTransforms = instanceTransforms; instancerBuffer._instancePrefixes = instancePrefixes; instancerBuffer._assetPaths = assetPaths; return instancerBuffer; } else if (instanceAttrInfo.exists) { // Object instancing via internal object path is not supported SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Object instancing is not supported (part {0})!", partName)); } else { // Standard object instancing via single Houdini object is not supported SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Object instancing is not supported (part {0})!", partName)); } return null; }
private HEU_LoadBufferInstancer GeneratePartsInstancerBuffer(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string partName, HAPI_PartInfo partInfo) { // Get the instance node IDs to get the geometry to be instanced. // Get the instanced count to all the instances. These will end up being mesh references to the mesh from instance node IDs. // Get each instance's transform HAPI_Transform[] instanceTransforms = new HAPI_Transform[partInfo.instanceCount]; if (!HEU_GeneralUtility.GetArray3Arg(geoID, partID, HAPI_RSTOrder.HAPI_SRT, session.GetInstancerPartTransforms, instanceTransforms, 0, partInfo.instanceCount)) { SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Unable to get instance transforms for part {0}", partName)); return null; } // Get part IDs for the parts being instanced HAPI_NodeId[] instanceNodeIDs = new HAPI_NodeId[partInfo.instancedPartCount]; if (!HEU_GeneralUtility.GetArray2Arg(geoID, partID, session.GetInstancedPartIds, instanceNodeIDs, 0, partInfo.instancedPartCount)) { SetLog(HEU_LoadData.LoadStatus.ERROR, string.Format("Unable to get instance node IDs for part {0}", partName)); return null; } // Get instance names if set string[] instancePrefixes = null; HAPI_AttributeInfo instancePrefixAttrInfo = new HAPI_AttributeInfo(); HEU_GeneralUtility.GetAttributeInfo(session, geoID, partID, HEU_Defines.DEFAULT_INSTANCE_PREFIX_ATTR, ref instancePrefixAttrInfo); if (instancePrefixAttrInfo.exists) { instancePrefixes = HEU_GeneralUtility.GetAttributeStringData(session, geoID, partID, HEU_Defines.DEFAULT_INSTANCE_PREFIX_ATTR, ref instancePrefixAttrInfo); } HEU_LoadBufferInstancer instancerBuffer = new HEU_LoadBufferInstancer(); instancerBuffer.InitializeBuffer(partID, partName, partInfo.isInstanced, true); instancerBuffer._instanceTransforms = instanceTransforms; instancerBuffer._instanceNodeIDs = instanceNodeIDs; instancerBuffer._instancePrefixes = instancePrefixes; return instancerBuffer; }
/// <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 SETTERS ------------------------------------------------------------------------------------------- public virtual bool SetPartInfo(HAPI_NodeId nodeID, HAPI_PartId partID, ref HAPI_PartInfo partInfo) { return false; }
/// <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); }
/// <summary> /// Process the part at the given index, creating its data (geometry), /// and adding it to the list of parts. /// </summary> /// <param name="session"></param> /// <param name="partID"></param> /// <returns>A valid HEU_PartData if it has been successfully processed.</returns> private void ProcessPart(HEU_SessionBase session, int partID, ref HAPI_PartInfo partInfo, ref HEU_PartData partData) { HEU_HoudiniAsset parentAsset = ParentAsset; bool bResult = true; //Debug.LogFormat("Part: name={0}, id={1}, type={2}, instanced={3}, instance count={4}, instance part count={5}", HEU_SessionManager.GetString(partInfo.nameSH, session), partID, partInfo.type, partInfo.isInstanced, partInfo.instanceCount, partInfo.instancedPartCount); #if HEU_PROFILER_ON float processPartStartTime = Time.realtimeSinceStartup; #endif bool isPartEditable = IsIntermediateOrEditable(); bool isAttribInstancer = false; if (IsGeoInputType()) { // Setup for input node to accept inputs if (_inputNode == null) { string partName = HEU_SessionManager.GetString(partInfo.nameSH, session); _inputNode = HEU_InputNode.CreateSetupInput(GeoID, 0, partName, HEU_InputNode.InputNodeType.NODE, ParentAsset); if (_inputNode != null) { ParentAsset.AddInputNode(_inputNode); } } if (HEU_HAPIUtility.IsSupportedPolygonType(partInfo.type) && partInfo.vertexCount == 0) { // No geometry for input asset if (partData != null) { // Clean up existing part HEU_PartData.DestroyPart(partData); partData = null; } // No need to process further since we don't have geometry return; } } else { // 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) { HAPI_AttributeInfo instanceAttrInfo = new HAPI_AttributeInfo(); HEU_GeneralUtility.GetAttributeInfo(session, GeoID, partID, HEU_PluginSettings.UnityInstanceAttr, ref instanceAttrInfo); if (instanceAttrInfo.exists && instanceAttrInfo.count > 0) { isAttribInstancer = true; } } } if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_INVALID) { // Clean up invalid parts if (partData != null) { HEU_PartData.DestroyPart(partData); partData = null; } } else if (partInfo.type < HAPI_PartType.HAPI_PARTTYPE_MAX) { // Process the part based on type. Keep or ignore. // We treat parts of type curve as curves, along with geo nodes that are editable and type curves if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_CURVE) { if (partData == null) { partData = ScriptableObject.CreateInstance <HEU_PartData>(); } partData.Initialize(session, partID, GeoID, _containerObjectNode.ObjectID, this, ref partInfo, HEU_PartData.PartOutputType.CURVE, isPartEditable, _containerObjectNode.IsInstancer(), false); SetupGameObjectAndTransform(partData, parentAsset); partData.ProcessCurvePart(session); } else if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_VOLUME) { // We only process "height" volume parts. Other volume parts are ignored for now. #if TERRAIN_SUPPORTED HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); bResult = session.GetVolumeInfo(GeoID, partID, ref volumeInfo); if (!bResult) { Debug.LogErrorFormat("Unable to get volume info for geo node {0} and part {1} ", GeoID, partID); } else { if (Displayable && !IsIntermediateOrEditable()) { if (partData == null) { partData = ScriptableObject.CreateInstance <HEU_PartData>(); } else { // Clear volume data (case where switching from polygonal mesh to volume output) partData.ClearGeneratedMeshOutput(); } partData.Initialize(session, partID, GeoID, _containerObjectNode.ObjectID, this, ref partInfo, HEU_PartData.PartOutputType.VOLUME, isPartEditable, _containerObjectNode.IsInstancer(), false); SetupGameObjectAndTransform(partData, ParentAsset); } } #else Debug.LogWarningFormat("Terrain (heightfield volume) is not yet supported."); #endif } else if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_INSTANCER || isAttribInstancer) { if (partData == null) { partData = ScriptableObject.CreateInstance <HEU_PartData>(); } else { partData.ClearGeneratedMeshOutput(); partData.ClearGeneratedVolumeOutput(); } partData.Initialize(session, partID, GeoID, _containerObjectNode.ObjectID, this, ref partInfo, HEU_PartData.PartOutputType.INSTANCER, isPartEditable, _containerObjectNode.IsInstancer(), isAttribInstancer); SetupGameObjectAndTransform(partData, parentAsset); } else if (HEU_HAPIUtility.IsSupportedPolygonType(partInfo.type)) { if (partData == null) { partData = ScriptableObject.CreateInstance <HEU_PartData>(); } else { // Clear volume data (case where switching from something other output to mesh) partData.ClearGeneratedVolumeOutput(); } partData.Initialize(session, partID, GeoID, _containerObjectNode.ObjectID, this, ref partInfo, HEU_PartData.PartOutputType.MESH, isPartEditable, _containerObjectNode.IsInstancer(), false); // This check allows to ignore editable non-display nodes by default, but commented out to allow // them for now. Users can also ignore them by turning on IgnoreNonDisplayNodes //if (Displayable || (Editable && ParentAsset.EditableNodesToolsEnabled)) { SetupGameObjectAndTransform(partData, parentAsset); } } else { Debug.LogWarningFormat("Unsupported part type {0}", partInfo.type); } if (partData != null) { // Success! _parts.Add(partData); // Set unique name for the part string partName = HEU_PluginSettings.UseFullPathNamesForOutput ? GeneratePartFullName(partData.PartName) : partData.PartName; partData.SetGameObjectName(partName); // For intermediate or default-type editable nodes, setup the HEU_AttributeStore if (isPartEditable) { partData.SyncAttributesStore(session, _geoInfo.nodeId, ref partInfo); } else { // Remove attributes store if it has it partData.DestroyAttributesStore(); } } } #if HEU_PROFILER_ON Debug.LogFormat("PART PROCESS TIME:: NAME={0}, TIME={1}", HEU_SessionManager.GetString(partInfo.nameSH, session), (Time.realtimeSinceStartup - processPartStartTime)); #endif }
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); }
/// <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); }
public void UpdateGeo(HEU_SessionBase session) { // Create or recreate parts. bool bObjectInstancer = _containerObjectNode.IsInstancer(); // Save list of old parts. We'll destroy these after creating new parts. // The reason for temporarily keeping these is to transfer data (eg. instance overrides, attribute data) List <HEU_PartData> oldParts = new List <HEU_PartData>(_parts); _parts.Clear(); try { if (!_geoInfo.isDisplayGeo) { if (ParentAsset.IgnoreNonDisplayNodes) { return; } else if (!_geoInfo.isEditable || (_geoInfo.type != HAPI_GeoType.HAPI_GEOTYPE_DEFAULT && _geoInfo.type != HAPI_GeoType.HAPI_GEOTYPE_INTERMEDIATE && _geoInfo.type != HAPI_GeoType.HAPI_GEOTYPE_CURVE)) { return; } } if (IsGeoCurveType()) { ProcessGeoCurve(session); } else { int numParts = _geoInfo.partCount; //Debug.Log("Number of parts: " + numParts); //Debug.LogFormat("GeoNode type {0}, isTemplated: {1}, isDisplayGeo: {2}, isEditable: {3}", _geoInfo.type, _geoInfo.isTemplated, _geoInfo.isDisplayGeo, _geoInfo.isEditable); for (int i = 0; i < numParts; ++i) { HAPI_PartInfo partInfo = new HAPI_PartInfo(); if (!session.GetPartInfo(GeoID, i, ref partInfo)) { Debug.LogErrorFormat("Unable to get PartInfo for geo node {0} and part {1}.", GeoID, i); continue; } // Find the old part for this new part. HEU_PartData part = null; HEU_PartData oldMatchedPart = null; foreach (HEU_PartData oldPart in oldParts) { string partName = HEU_SessionManager.GetString(partInfo.nameSH, session); if (oldPart.PartName.Equals(partName)) { oldMatchedPart = oldPart; } } if (oldMatchedPart != null) { //Debug.Log("Found matched part: " + oldMatchedPart.name); List <HEU_ObjectInstanceInfo> sourceObjectInstanceInfos = null; if (bObjectInstancer) { // ProcessPart will clear out the object instances, so hence why // we keep a copy here, then restore after processing the parts. sourceObjectInstanceInfos = oldMatchedPart.GetObjectInstanceInfos(); } // Clear out old generated data oldMatchedPart.ClearGeneratedData(); part = oldMatchedPart; oldParts.Remove(oldMatchedPart); ProcessPart(session, i, ref partInfo, ref part); if (part != null && bObjectInstancer && sourceObjectInstanceInfos != null) { // Set object instances from old part into new. This keeps the user set object inputs around. part.SetObjectInstanceInfos(sourceObjectInstanceInfos); } } else { ProcessPart(session, i, ref partInfo, ref part); } } } } finally { HEU_PartData.DestroyParts(oldParts); } }
/// <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; }
/// <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> /// Process the part at the given index, creating its data (geometry), /// and adding it to the list of parts. /// </summary> /// <param name="session"></param> /// <param name="partID"></param> /// <returns>A valid HEU_PartData if it has been successfully processed.</returns> private void ProcessPart(HEU_SessionBase session, int partID, ref HAPI_PartInfo partInfo, ref HEU_PartData partData) { HEU_HoudiniAsset parentAsset = ParentAsset; bool bResult = true; //Debug.LogFormat("Part: name={0}, id={1}, type={2}, instanced={3}, instance count={4}, instance part count={5}", HEU_SessionManager.GetString(partInfo.nameSH, session), partID, partInfo.type, partInfo.isInstanced, partInfo.instanceCount, partInfo.instancedPartCount); #if HEU_PROFILER_ON float processPartStartTime = Time.realtimeSinceStartup; #endif bool isPartEditable = IsIntermediateOrEditable(); if (IsGeoInputType()) { // Setup for input node to accept inputs if (_inputNode == null) { string partName = HEU_SessionManager.GetString(partInfo.nameSH, session); _inputNode = HEU_InputNode.CreateSetupInput(GeoID, 0, partName, HEU_InputNode.InputNodeType.NODE, ParentAsset); if (_inputNode != null) { ParentAsset.AddInputNode(_inputNode); } } if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_MESH && partInfo.vertexCount == 0) { // No geometry for input asset if (partData != null) { // Clean up existing part HEU_PartData.DestroyPart(partData); partData = null; } // No need to process further since we don't have geometry return; } } if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_INVALID) { // Clean up invalid parts if (partData != null) { HEU_PartData.DestroyPart(partData); partData = null; } } else if (partInfo.type < HAPI_PartType.HAPI_PARTTYPE_MAX) { // Process the part based on type. Keep or ignore. // We treat parts of type curve as curves, along with geo nodes that are editable and type curves if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_CURVE) { if (partData == null) { partData = ScriptableObject.CreateInstance <HEU_PartData>(); } partData.Initialize(session, partID, GeoID, _containerObjectNode.ObjectID, this, ref partInfo, HEU_PartData.PartOutputType.CURVE, isPartEditable); SetupGameObjectAndTransform(partData, parentAsset); partData.ProcessCurvePart(session); } else if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_VOLUME) { // We only process "height" volume parts. Other volume parts are ignored for now. #if TERRAIN_SUPPORTED HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); bResult = session.GetVolumeInfo(GeoID, partID, ref volumeInfo); if (!bResult) { Debug.LogErrorFormat("Unable to get volume info for geo node {0} and part {1} ", GeoID, partID); } else { if (Displayable && !IsIntermediateOrEditable()) { if (partData == null) { partData = ScriptableObject.CreateInstance <HEU_PartData>(); } partData.Initialize(session, partID, GeoID, _containerObjectNode.ObjectID, this, ref partInfo, HEU_PartData.PartOutputType.VOLUME, isPartEditable); SetupGameObjectAndTransform(partData, ParentAsset); } } #else Debug.LogWarningFormat("Terrain (heightfield volume) is not yet supported."); #endif } else if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_MESH) { if (partData == null) { partData = ScriptableObject.CreateInstance <HEU_PartData>(); } partData.Initialize(session, partID, GeoID, _containerObjectNode.ObjectID, this, ref partInfo, HEU_PartData.PartOutputType.MESH, isPartEditable); SetupGameObjectAndTransform(partData, parentAsset); } else if (partInfo.type == HAPI_PartType.HAPI_PARTTYPE_INSTANCER) { if (partData == null) { partData = ScriptableObject.CreateInstance <HEU_PartData>(); } partData.Initialize(session, partID, GeoID, _containerObjectNode.ObjectID, this, ref partInfo, HEU_PartData.PartOutputType.INSTANCER, isPartEditable); SetupGameObjectAndTransform(partData, parentAsset); } else { Debug.LogWarningFormat("Unsupported part type {0}", partInfo.type); } if (partData != null) { // Success! _parts.Add(partData); // Set unique name for the part string partFullName = GeneratePartFullName(partData.PartName); partFullName = HEU_EditorUtility.GetUniqueNameForSibling(ParentAsset.RootGameObject.transform, partFullName); partData.SetGameObjectName(partFullName); // For intermediate or default-type editable nodes, setup the HEU_AttributeStore if (isPartEditable) { partData.SyncAttributesStore(session, _geoInfo.nodeId, ref partInfo); } else { // Remove attributes store if it has it partData.DestroyAttributesStore(); } HEU_GeneralUtility.AssignUnityTag(session, GeoID, partData.PartID, partData._gameObject); HEU_GeneralUtility.MakeStaticIfHasAttribute(session, GeoID, partData.PartID, partData._gameObject); } } #if HEU_PROFILER_ON Debug.LogFormat("PART PROCESS TIME:: NAME={0}, TIME={1}", HEU_SessionManager.GetString(partInfo.nameSH, session), (Time.realtimeSinceStartup - processPartStartTime)); #endif }