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; }
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 = pointAttributeDatas.Count; newPartInfo.vertexAttributeCount = vertexAttributeDatas.Count; newPartInfo.primitiveAttributeCount = primitiveAttributeDatas.Count; newPartInfo.detailAttributeCount = detailAttributeDatas.Count; 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> /// Fill in array of HAPI_Transform list. Use for large arrays where marshalling is done in chunks. /// </summary> /// <param name="nodeID">The parent node ID</param> /// <param name="rstOrder">Transform order</param> /// <param name="transforms">Array to fill. Should at least be size of length</param> /// <param name="start">At least 0 and at most object count returned by ComposeObjectList</param> /// <param name="length">Object count returned by ComposeObjectList. Should be at least 0 and at most object count - start</param> /// <returns>True if successfully queuried the transform list</returns> public static bool GetComposedObjectTransformsMemorySafe(HEU_SessionBase session, HAPI_NodeId nodeID, HAPI_RSTOrder rstOrder, [Out] HAPI_Transform[] transforms, int start, int length) { return HEU_GeneralUtility.GetArray2Arg(nodeID, rstOrder, session.GetComposedObjectTransforms, transforms, start, length); }
private float[] GetHeightfield(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string partName, int terrainSize) { HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); bool bResult = session.GetVolumeInfo(geoID, partID, ref volumeInfo); if (!bResult) { return null; } int volumeXLength = volumeInfo.xLength; int volumeYLength = volumeInfo.yLength; // Number of heightfield values int totalHeightValues = volumeXLength * volumeYLength; float[] heightValues = new float[totalHeightValues]; bResult = HEU_GeneralUtility.GetArray2Arg(geoID, partID, session.GetHeightFieldData, heightValues, 0, totalHeightValues); if (!bResult) { Debug.LogErrorFormat("Unable to get heightfield data from part {0}", partName); return null; } float minHeight = heightValues[0]; float maxHeight = minHeight; for (int i = 0; i < totalHeightValues; ++i) { float f = heightValues[i]; if (f > maxHeight) { maxHeight = f; } else if (f < minHeight) { minHeight = f; } } float heightRange = (maxHeight - minHeight); if(heightRange == 0f) { heightRange = 1f; } //Debug.LogFormat("{0} : {1}", HEU_SessionManager.GetString(volumeInfo.nameSH, session), heightRange); // Remap height values to fit terrain size int paddingWidth = terrainSize - volumeXLength; int paddingLeft = Mathf.CeilToInt(paddingWidth * 0.5f); int paddingRight = terrainSize - paddingLeft; //Debug.LogFormat("Padding: Width={0}, Left={1}, Right={2}", paddingWidth, paddingLeft, paddingRight); int paddingHeight = terrainSize - volumeYLength; int paddingTop = Mathf.CeilToInt(paddingHeight * 0.5f); int paddingBottom = terrainSize - paddingTop; //Debug.LogFormat("Padding: Height={0}, Top={1}, Bottom={2}", paddingHeight, paddingTop, paddingBottom); // Set height values at centre of the terrain, with padding on the sides if we resized float[] resizedHeightValues = new float[terrainSize * terrainSize]; for (int y = 0; y < terrainSize; ++y) { for (int x = 0; x < terrainSize; ++x) { if (y >= paddingTop && y < (paddingBottom) && x >= paddingLeft && x < (paddingRight)) { int ay = x - paddingLeft; int ax = y - paddingTop; float f = heightValues[ay + ax * volumeXLength] - minHeight; f /= heightRange; // Flip for right-hand to left-handed coordinate system int ix = x; int iy = terrainSize - (y + 1); // Unity expects height array indexing to be [y, x]. resizedHeightValues[iy + ix * terrainSize] = f; } } } return resizedHeightValues; }
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.GetArray2Arg(geoID, HAPI_RSTOrder.HAPI_SRT, session.GetInstanceTransforms, 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; }
public static bool GenerateTerrainFromVolume(HEU_SessionBase session, ref HAPI_VolumeInfo volumeInfo, HAPI_NodeId geoID, HAPI_PartId partID, GameObject gameObject, out TerrainData terrainData, out Vector3 volumePositionOffset) { terrainData = null; volumePositionOffset = Vector3.zero; if (volumeInfo.zLength == 1 && volumeInfo.tupleSize == 1) { // Heightfields will be converted to terrain in Unity. // Unity requires terrainData.heightmapResolution to be square power of two plus 1 (eg. 513, 257, 129, 65). // Houdini gives volumeInfo.xLength and volumeInfo.yLength which are the number of height values per dimension. // Note that volumeInfo.xLength and volumeInfo.yLength is equal to Houdini heightfield size / grid spacing. // The heightfield grid spacing is given as volumeTransformMatrix.scale but divided by 2 (grid spacing / 2 = volumeTransformMatrix.scale). // It is recommended to use grid spacing of 2. // Use the volumeInfo.transform to get the actual heightfield position and size. Matrix4x4 volumeTransformMatrix = HEU_HAPIUtility.GetMatrixFromHAPITransform(ref volumeInfo.transform, false); Vector3 position = HEU_HAPIUtility.GetPosition(ref volumeTransformMatrix); Vector3 scale = HEU_HAPIUtility.GetScale(ref volumeTransformMatrix); // Calculate real terrain size in both Houdini and Unity. // The height values will be mapped over this terrain size. float gridSpacingX = scale.x * 2f; float gridSpacingY = scale.y * 2f; //float gridSpacingZ = scale.z * 2f; float multiplierOffsetX = Mathf.Round(scale.x); float multiplierOffsetY = Mathf.Round(scale.y); //float multiplierOffsetZ = Mathf.Round(scale.z); float terrainSizeX = Mathf.Round(volumeInfo.xLength * gridSpacingX - multiplierOffsetX); float terrainSizeY = Mathf.Round(volumeInfo.yLength * gridSpacingY - multiplierOffsetY); //Debug.LogFormat("GS = {0},{1},{2}. SX = {1}. SY = {2}", gridSpacingX, gridSpacingY, gridSpacingZ, terrainSizeX, terrainSizeY); //Debug.LogFormat("HeightField Pos:{0}, Scale:{1}", position, scale.ToString("{0.00}")); //Debug.LogFormat("HeightField tileSize:{0}, xLength:{1}, yLength:{2}", volumeInfo.tileSize.ToString("{0.00}"), volumeInfo.xLength.ToString("{0.00}"), volumeInfo.yLength.ToString("{0.00}")); //Debug.LogFormat("HeightField Terrain Size x:{0}, y:{1}", terrainSizeX.ToString("{0.00}"), terrainSizeY.ToString("{0.00}")); //Debug.LogFormat("HeightField minX={0}, minY={1}, minZ={2}", volumeInfo.minX.ToString("{0.00}"), volumeInfo.minY.ToString("{0.00}"), volumeInfo.minZ.ToString("{0.00}")); const int UNITY_MINIMUM_HEIGHTMAP_RESOLUTION = 33; if (terrainSizeX < UNITY_MINIMUM_HEIGHTMAP_RESOLUTION || terrainSizeY < UNITY_MINIMUM_HEIGHTMAP_RESOLUTION) { Debug.LogWarningFormat("Unity Terrain has a minimum heightmap resolution of {0}. This HDA heightmap size is {1}x{2}." + "\nPlease resize the terrain to a value higher than this.", UNITY_MINIMUM_HEIGHTMAP_RESOLUTION, terrainSizeX, terrainSizeY); return false; } Terrain terrain = HEU_GeneralUtility.GetOrCreateComponent<Terrain>(gameObject); TerrainCollider collider = HEU_GeneralUtility.GetOrCreateComponent<TerrainCollider>(gameObject); if (terrain.terrainData == null) { terrain.terrainData = new TerrainData(); } terrainData = terrain.terrainData; collider.terrainData = terrainData; // Heightmap resolution must be square power-of-two plus 1. // Unity will automatically resize terrainData.heightmapResolution so need to handle the changed size (if Unity changed it). int heightMapResolution = volumeInfo.xLength; terrainData.heightmapResolution = heightMapResolution; int terrainResizedDelta = terrainData.heightmapResolution - heightMapResolution; if (terrainResizedDelta < 0) { Debug.LogWarningFormat("Note that Unity automatically resized terrain resolution to {0} from {1}. Use terrain size of power of two plus 1, and grid spacing of 2.", heightMapResolution, terrainData.heightmapResolution); heightMapResolution = terrainData.heightmapResolution; } else if(terrainResizedDelta > 0) { Debug.LogErrorFormat("Unsupported terrain size. Use terrain size of power of two plus 1, and grid spacing of 2. (delta = {0})", terrainResizedDelta); return false; } // Get the height values from Houdini and find the min and max height range. int totalHeightValues = volumeInfo.xLength * volumeInfo.yLength; float[] heightValues = new float[totalHeightValues]; bool bResult = HEU_GeneralUtility.GetArray2Arg(geoID, partID, session.GetHeightFieldData, heightValues, 0, totalHeightValues); if (!bResult) { return false; } float minHeight = heightValues[0]; float maxHeight = minHeight; for (int i = 0; i < totalHeightValues; ++i) { float f = heightValues[i]; if (f > maxHeight) { maxHeight = f; } else if (f < minHeight) { minHeight = f; } } const int UNITY_MAX_HEIGHT_RANGE = 65536; float heightRange = (maxHeight - minHeight); if (Mathf.RoundToInt(heightRange) > UNITY_MAX_HEIGHT_RANGE) { Debug.LogWarningFormat("Unity Terrain has maximum height range of {0}. This HDA height range is {1}, so it will be maxed out at {0}.\nPlease resize to within valid range!", UNITY_MAX_HEIGHT_RANGE, Mathf.RoundToInt(heightRange)); heightRange = UNITY_MAX_HEIGHT_RANGE; } int mapWidth = volumeInfo.xLength; int mapHeight = volumeInfo.yLength; int paddingWidth = heightMapResolution - mapWidth; int paddingLeft = Mathf.CeilToInt(paddingWidth * 0.5f); int paddingRight = heightMapResolution - paddingLeft; //Debug.LogFormat("Padding: Width={0}, Left={1}, Right={2}", paddingWidth, paddingLeft, paddingRight); int paddingHeight = heightMapResolution - mapHeight; int paddingTop = Mathf.CeilToInt(paddingHeight * 0.5f); int paddingBottom = heightMapResolution - paddingTop; //Debug.LogFormat("Padding: Height={0}, Top={1}, Bottom={2}", paddingHeight, paddingTop, paddingBottom); // Set height values at centre of the terrain, with padding on the sides if we resized float[,] unityHeights = new float[heightMapResolution, heightMapResolution]; for (int y = 0; y < heightMapResolution; ++y) { for (int x = 0; x < heightMapResolution; ++x) { if (y >= paddingTop && y < (paddingBottom) && x >= paddingLeft && x < (paddingRight)) { int ay = x - paddingLeft; int ax = y - paddingTop; // Unity expects normalized height values float h = heightValues[ay + ax * mapWidth] - minHeight; float f = h / heightRange; // Flip for right-hand to left-handed coordinate system int ix = x; int iy = heightMapResolution - (y + 1); // Unity expects height array indexing to be [y, x]. unityHeights[ix, iy] = f; } } } terrainData.baseMapResolution = heightMapResolution; terrainData.alphamapResolution = heightMapResolution; //int detailResolution = heightMapResolution; // 128 is the maximum for resolutionPerPatch const int resolutionPerPatch = 128; terrainData.SetDetailResolution(resolutionPerPatch, resolutionPerPatch); // Note SetHeights must be called before setting size in next line, as otherwise // the internal terrain size will not change after setting the size. terrainData.SetHeights(0, 0, unityHeights); terrainData.size = new Vector3(terrainSizeX, heightRange, terrainSizeY); terrain.Flush(); // Unity Terrain has origin at bottom left, whereas Houdini uses centre of terrain. // Use volume bounds to set position offset when using split tiles float xmin, xmax, zmin, zmax, ymin, ymax, xcenter, ycenter, zcenter; session.GetVolumeBounds(geoID, partID, out xmin, out ymin, out zmin, out xmax, out ymax, out zmax, out xcenter, out ycenter, out zcenter); //Debug.LogFormat("xmin: {0}, xmax: {1}, ymin: {2}, ymax: {3}, zmin: {4}, zmax: {5}, xc: {6}, yc: {7}, zc: {8}", // xmin, xmax, ymin, ymax, zmin, zmax, xcenter, ycenter, zcenter); // Offset position is based on size of heightfield float offsetX = (float)heightMapResolution / (float)mapWidth; float offsetZ = (float)heightMapResolution / (float)mapHeight; //Debug.LogFormat("offsetX: {0}, offsetZ: {1}", offsetX, offsetZ); //Debug.LogFormat("position.x: {0}, position.z: {1}", position.x, position.z); //volumePositionOffset = new Vector3(-position.x * offsetX, minHeight + position.y, position.z * offsetZ); volumePositionOffset = new Vector3((terrainSizeX + xmin) * offsetX, minHeight + position.y, zmin * offsetZ); return true; } else { Debug.LogWarning("Non-heightfield volume type not supported!"); } return false; }
public static HEU_GenerateGeoCache GetPopulatedGeoCache(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID) { #if HEU_PROFILER_ON float generateGeoCacheStartTime = Time.realtimeSinceStartup; #endif HEU_GenerateGeoCache geoCache = new HEU_GenerateGeoCache(); Debug.Assert(geoID != HEU_Defines.HEU_INVALID_NODE_ID, "Invalid Geo ID! Unable to update materials!"); Debug.Assert(partID != HEU_Defines.HEU_INVALID_NODE_ID, "Invalid Part ID! Unable to update materials!"); geoCache._geoInfo = new HAPI_GeoInfo(); if (!session.GetGeoInfo(geoID, ref geoCache._geoInfo)) { return null; } geoCache._partInfo = new HAPI_PartInfo(); if (!session.GetPartInfo(geoID, partID, ref geoCache._partInfo)) { return null; } geoCache._partName = HEU_SessionManager.GetString(geoCache._partInfo.nameSH, session); // Unity max indices limitation! #if UNITY_2017_3_OR_NEWER uint UNITY_MAX_VERTS = uint.MaxValue; #else uint UNITY_MAX_VERTS = ushort.MaxValue; #endif uint maxVertexCount = Convert.ToUInt32(geoCache._partInfo.vertexCount); if (maxVertexCount > UNITY_MAX_VERTS) { Debug.LogErrorFormat("Part {0} has vertex count of {1} which is above Unity maximum of {2}.\nUse Unity 2017.3+ or reduce this in Houdini.", geoCache._partName, maxVertexCount, UNITY_MAX_VERTS); return null; } geoCache._vertexList = new int[geoCache._partInfo.vertexCount]; if (!HEU_GeneralUtility.GetArray2Arg(geoID, partID, session.GetVertexList, geoCache._vertexList, 0, geoCache._partInfo.vertexCount)) { return null; } geoCache._houdiniMaterialIDs = new HAPI_NodeId[geoCache._partInfo.faceCount]; if (!session.GetMaterialNodeIDsOnFaces(geoID, partID, ref geoCache._singleFaceHoudiniMaterial, geoCache._houdiniMaterialIDs, geoCache._partInfo.faceCount)) { return null; } geoCache.PopulateUnityMaterialData(session); if (!geoCache.PopulateGeometryData(session)) { return null; } #if HEU_PROFILER_ON Debug.LogFormat("GENERATE GEO CACHE TIME:: {0}", (Time.realtimeSinceStartup - generateGeoCacheStartTime)); #endif return geoCache; }