Beispiel #1
0
		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>
        /// 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
        }
Beispiel #3
0
		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;
		}
	/// <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 || parts[j].IsPartVolume())
			{
			    // This prevents instances being created unnecessarily (e.g. part hasn't changed since last cook).
			    // Or for volumes that might have instance attributes.
			    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];
				HEU_GeneralUtility.GetAttribute(session, _geoNodes[i].GeoID, parts[j].PartID, unityInstanceAttrName, ref unityInstanceAttrInfo, ref scriptAttr, session.GetAttributeStringData);
				if (unityInstanceAttrInfo.exists)
				{
				    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.
				// Originally had a warning here, but unnecessary as in some cases (e.g. heightfield attrbiutes) the
				// attribute owner could be changed in HAPI.
			    }
			}
			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);
			}
		    }
		}
	    }
	}
        public void ProcessVolumeParts(HEU_SessionBase session, List <HEU_PartData> volumeParts, bool bRebuild)
        {
            int numVolumeParts = volumeParts.Count;

            if (numVolumeParts == 0)
            {
                DestroyVolumeCache();
            }
            else if (_volumeCaches == null)
            {
                _volumeCaches = new List <HEU_VolumeCache>();
            }

            // First update volume caches. Each volume cache represents a set of terrain layers grouped by tile index.
            // Therefore each volume cache represents a potential Unity Terrain (containing layers)
            _volumeCaches = HEU_VolumeCache.UpdateVolumeCachesFromParts(session, this, volumeParts, _volumeCaches);

            // Heightfield scatter nodes come in as mesh-type parts with attribute instancing.
            // So process them here to get all the tree/detail instance scatter information.
            int numParts = _parts.Count;

            for (int i = 0; i < numParts; ++i)
            {
                // Find the terrain tile (use primitive attr). Assume 0 tile if not set (i.e. not split into tiles)
                int terrainTile = 0;
                HAPI_AttributeInfo tileAttrInfo = new HAPI_AttributeInfo();
                int[] tileAttrData = new int[0];
                if (HEU_GeneralUtility.GetAttribute(session, GeoID, _parts[i].PartID, HEU_Defines.HAPI_HEIGHTFIELD_TILE_ATTR, ref tileAttrInfo, ref tileAttrData, session.GetAttributeIntData))
                {
                    if (tileAttrData != null && tileAttrData.Length > 0)
                    {
                        terrainTile = tileAttrData[0];
                    }
                }

                // Find the volumecache associated with this part using the terrain tile index
                HEU_VolumeCache volumeCache = GetVolumeCacheByTileIndex(terrainTile);
                if (volumeCache == null)
                {
                    continue;
                }

                HEU_VolumeLayer volumeLayer = volumeCache.GetLayer(_parts[i].GetVolumeLayerName());
                if (volumeLayer != null && volumeLayer._layerType == HFLayerType.DETAIL)
                {
                    // Clear out outputs since it might have been created when the part was created.
                    _parts[i].DestroyAllData();

                    volumeCache.PopulateDetailPrototype(session, GeoID, _parts[i].PartID, volumeLayer);
                }
                else if (_parts[i].IsAttribInstancer())
                {
                    HAPI_AttributeInfo treeInstAttrInfo = new HAPI_AttributeInfo();
                    if (HEU_GeneralUtility.GetAttributeInfo(session, GeoID, _parts[i].PartID, HEU_Defines.HEIGHTFIELD_TREEINSTANCE_PROTOTYPEINDEX, ref treeInstAttrInfo))
                    {
                        if (treeInstAttrInfo.exists && treeInstAttrInfo.count > 0)
                        {
                            // Clear out outputs since it might have been created when the part was created.
                            _parts[i].DestroyAllData();

                            // Mark the instancers as having been created so that the object instancer step skips this.
                            _parts[i].ObjectInstancesBeenGenerated = true;

                            // Now populate scatter trees based on attributes on this part
                            volumeCache.PopulateScatterTrees(session, GeoID, _parts[i].PartID, treeInstAttrInfo.count);
                        }
                    }
                }
            }

            // Now generate the terrain for each volume cache
            foreach (HEU_VolumeCache cache in _volumeCaches)
            {
                cache.GenerateTerrainWithAlphamaps(session, ParentAsset, bRebuild);

                cache.IsDirty = false;
            }
        }
Beispiel #6
0
	/// <summary>
	/// Generates object instances.
	/// Skips parts that already have their instances generated.
	/// </summary>
	/// <param name="session">Active session to use</param>
	internal void GenerateObjectInstances(HEU_SessionBase session)
	{
	    if (ParentAsset == null)
	    {
		return;
	    }
	    
	    if (!IsInstancer())
	    {
		HEU_Logger.LogErrorFormat("Generate object instances called on a non-instancer object {0} for asset {1}!", ObjectName, ParentAsset.AssetName);
		return;
	    }

	    //HEU_Logger.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();
	    HAPI_AttributeInfo materialAttrInfo = 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]._objectInstancesGenerated || parts[j].IsPartVolume())
			{
			    // This prevents instances being created unnecessarily (e.g. part hasn't changed since last cook).
			    // Or for volumes that might have instance attributes.
			    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);
			}

			string[] instanceMaterialPaths = null;
			HEU_GeneralUtility.GetAttributeInfo(session, _geoNodes[i].GeoID, parts[j].PartID, HEU_PluginSettings.UnityMaterialAttribName, ref materialAttrInfo);
			if (materialAttrInfo.exists)
			{
			    instanceMaterialPaths = HEU_GeneralUtility.GetAttributeStringData(session, _geoNodes[i].GeoID, parts[j].PartID, HEU_PluginSettings.UnityMaterialAttribName, ref materialAttrInfo);
			}

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

			    parts[j].GenerateInstancesFromObjectIds(session, instancePrefixes, instanceMaterialPaths);
			}
			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 ||
			        unityInstanceAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL)
			    {
				parts[j].GenerateInstancesFromUnityAssetPathAttribute(session, unityInstanceAttrName);
			    }
			    else
			    {
				// Other attribute owned types are unsupported.
				// Originally had a warning here, but unnecessary as in some cases (e.g. heightfield attrbiutes) the
				// attribute owner could be changed in HAPI.
			    }
			}
			else
			{
			    // Standard object instancing via single Houdini object

			    if (_objectInfo.objectToInstanceId == HEU_Defines.HEU_INVALID_NODE_ID)
			    {
				// HEU_Logger.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);
				// Could be a part instancer
				continue;
			    }

			    parts[j].GenerateInstancesFromObjectID(session, _objectInfo.objectToInstanceId, instancePrefixes, instanceMaterialPaths);
			}
		    }
		}
	    }
	}
		/// <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)
		{
			// 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;
					// 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, nodeID, partInfo.id, HEU_PluginSettings.UnityInstanceAttr, ref instanceAttrInfo);
						if (instanceAttrInfo.exists && instanceAttrInfo.count > 0)
						{
							isAttribInstancer = true;
						}
					}

					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;
		}