public static void GetAttributeStringDataHelper(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string name, ref HAPI_AttributeInfo info, ref HAPI_StringHandle[] data)
		{
			int originalTupleSize = info.tupleSize;
			bool bResult = false;
			for (HAPI_AttributeOwner type = 0; type < HAPI_AttributeOwner.HAPI_ATTROWNER_MAX; ++type)
			{
				bResult = session.GetAttributeInfo(geoID, partID, name, type, ref info);
				if (bResult && info.exists)
				{
					break;
				}
			}

			if (!bResult || !info.exists)
			{
				return;
			}

			if (originalTupleSize > 0)
			{
				info.tupleSize = originalTupleSize;
			}

			data = new HAPI_StringHandle[info.count * info.tupleSize];
			bResult = session.GetAttributeStringData(geoID, partID, name, ref info, data, 0, info.count);
			if(!bResult)
			{
				Debug.LogErrorFormat("Failed to get string IDs for attribute {0}", name);
			}
		}
		/// <summary>
		/// Generates object instances.
		/// Skips parts that already have their instances generated.
		/// </summary>
		/// <param name="session">Active session to use</param>
		public void GenerateObjectInstances(HEU_SessionBase session)
		{
			if (!IsInstancer())
			{
				Debug.LogErrorFormat("Generate object instances called on a non-instancer object {0} for asset {1}!", ObjectName, ParentAsset.AssetName);
				return;
			}

			//Debug.LogFormat("Generate Object Instances:: id={5}, name={0}, isInstancer={1}, isInstanced={2}, instancePath={3}, instanceId={4}", HEU_SessionManager.GetString(_objectInfo.nameSH, session), 
			//	_objectInfo.isInstancer, _objectInfo.isInstanced, HEU_SessionManager.GetString(_objectInfo.objectInstancePathSH, session), _objectInfo.objectToInstanceId, _objectInfo.nodeId);

			// Is this a Houdini attribute 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();

			int numGeos = _geoNodes.Count;
			for(int i = 0; i < numGeos; ++i)
			{
				if(_geoNodes[i].Displayable)
				{
					List<HEU_PartData> parts = _geoNodes[i].GetParts();
					int numParts = parts.Count;
					for(int j = 0; j < numParts; ++j)
					{
						if(parts[j].ObjectInstancesBeenGenerated)
						{
							// This prevents instances being created unnecessarily (e.g. part hasn't changed since last cook)
							continue;
						}

						HEU_GeneralUtility.GetAttributeInfo(session, _geoNodes[i].GeoID, parts[j].PartID, instanceAttrName, ref instanceAttrInfo);
						HEU_GeneralUtility.GetAttributeInfo(session, _geoNodes[i].GeoID, parts[j].PartID, unityInstanceAttrName, ref unityInstanceAttrInfo);

						string[] instancePrefixes = null;
						HEU_GeneralUtility.GetAttributeInfo(session, _geoNodes[i].GeoID, parts[j].PartID, instancePrefixAttrName, ref instancePrefixAttrInfo);
						if(instancePrefixAttrInfo.exists)
						{
							instancePrefixes = HEU_GeneralUtility.GetAttributeStringData(session, _geoNodes[i].GeoID, parts[j].PartID, instancePrefixAttrName, ref instancePrefixAttrInfo);
						}

						// Must clear out instances, as otherwise we get duplicates
						parts[j].ClearInstances();

						// Clear out invalid object instance infos that no longer have any valid parts
						parts[j].ClearInvalidObjectInstanceInfos();

						if (instanceAttrInfo.exists)
						{
							// Object instancing via Houdini instance attribute

							parts[j].GenerateInstancesFromObjectIds(session, instancePrefixes);
						}
						else if (unityInstanceAttrInfo.exists)
						{
							// Object instancing via existing Unity object (path from point attribute)

							// Attribute owner type determines whether to use single instanced object (detail) or multiple (point)
							if (unityInstanceAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_POINT)
							{
								parts[j].GenerateInstancesFromUnityAssetPathAttribute(session, unityInstanceAttrName);
							}
							else if(unityInstanceAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL)
							{
								bool bInstanced = false;
								int[] scriptAttr = new int[unityInstanceAttrInfo.count];
								if(session.GetAttributeStringData(_geoNodes[i].GeoID, parts[j].PartID, unityInstanceAttrName, ref unityInstanceAttrInfo, scriptAttr, 0, unityInstanceAttrInfo.count))
								{
									string assetPath = HEU_SessionManager.GetString(scriptAttr[0]);
									if (!string.IsNullOrEmpty(assetPath))
									{
										parts[j].GenerateInstancesFromUnityAssetPath(session, assetPath, instancePrefixes);
										bInstanced = true;
									}
								}

								if (!bInstanced)
								{
									Debug.LogWarningFormat("Unable to get instanced object path from detail instance attribute!");
								}
							}
							else
							{
								// Other attribute owned types are unsupported
								Debug.LogWarningFormat("Unsupported attribute owner {0} for attribute {1}", unityInstanceAttrInfo.owner, unityInstanceAttrName);
							}
						}
						else
						{
							// Standard object instancing via single Houdini object

							if (_objectInfo.objectToInstanceId == HEU_Defines.HEU_INVALID_NODE_ID)
							{
								Debug.LogAssertionFormat("Invalid object ID {0} used for object instancing. " 
									+ "Make sure to turn on Full point instancing and set the correct Instance Object.", _objectInfo.objectToInstanceId);
								continue;
							}

							parts[j].GenerateInstancesFromObjectID(session, _objectInfo.objectToInstanceId, instancePrefixes);
						}
					}
				}
			}
		}