/// <summary> /// Generate the instances of instancer parts. /// </summary> internal void GeneratePartInstances(HEU_SessionBase session) { List <HEU_PartData> partsToDestroy = new List <HEU_PartData>(); int numParts = _parts.Count; for (int i = 0; i < numParts; ++i) { if (_parts[i].IsPartInstancer() && !_parts[i].HaveInstancesBeenGenerated()) { if (!_parts[i].GeneratePartInstances(session)) { partsToDestroy.Add(_parts[i]); } } } int numPartsToDestroy = partsToDestroy.Count; for (int i = 0; i < numPartsToDestroy; ++i) { HEU_GeoNode parentNode = partsToDestroy[i].ParentGeoNode; if (parentNode != null) { parentNode.RemoveAndDestroyPart(partsToDestroy[i]); } else { HEU_PartData.DestroyPart(partsToDestroy[i]); } } partsToDestroy.Clear(); }
/// <summary> /// Destroy all generated data. /// </summary> public void DestroyAllData() { HEU_PartData.DestroyParts(_parts); if (_inputNode != null) { HEU_SessionBase session = null; if (ParentAsset != null) { ParentAsset.RemoveInputNode(_inputNode); session = ParentAsset.GetAssetSession(false); } _inputNode.DestroyAllData(session); HEU_GeneralUtility.DestroyImmediate(_inputNode); _inputNode = null; } if (_geoCurve != null) { if (ParentAsset != null) { ParentAsset.RemoveCurve(_geoCurve); } _geoCurve.DestroyAllData(); HEU_GeneralUtility.DestroyImmediate(_geoCurve); _geoCurve = null; } DestroyVolumeCache(); }
private void SetupGameObjectAndTransform(HEU_PartData partData, HEU_HoudiniAsset parentAsset) { // Set a valid gameobject for this part if (partData.OutputGameObject == null) { partData.SetGameObject(new GameObject()); } // The parent is either the asset root, OR if this is instanced and not visible, then the HDA data is the parent // The parent transform is either the asset root (for a display node), // or the HDA_Data gameobject (for instanced, not visible, intermediate, editable non-display nodes) Transform partTransform = partData.OutputGameObject.transform; if (partData.IsPartInstanced() || (_containerObjectNode.IsInstanced() && !_containerObjectNode.IsVisible()) || partData.IsPartCurve() || (IsIntermediateOrEditable() && !Displayable)) { partTransform.parent = parentAsset.OwnerGameObject.transform; } else { partTransform.parent = parentAsset.RootGameObject.transform; } partData.OutputGameObject.isStatic = partTransform.parent.gameObject.isStatic; // Reset to origin partTransform.localPosition = Vector3.zero; partTransform.localRotation = Quaternion.identity; partTransform.localScale = Vector3.one; }
public void UpdateLayerFromPart(HEU_SessionBase session, HEU_PartData part) { HEU_GeoNode geoNode = part.ParentGeoNode; HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); bool bResult = session.GetVolumeInfo(geoNode.GeoID, part.PartID, ref volumeInfo); if (!bResult || volumeInfo.tupleSize != 1 || volumeInfo.zLength != 1 || volumeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT) { return; } string volumeName = HEU_SessionManager.GetString(volumeInfo.nameSH, session); part.SetVolumeLayerName(volumeName); //Debug.LogFormat("Part name: {0}, GeoName: {1}, Volume Name: {2}, Display: {3}", part.PartName, geoNode.GeoName, volumeName, geoNode.Displayable); bool bHeightPart = volumeName.Equals("height"); HEU_VolumeLayer layer = GetLayer(volumeName); if (layer == null) { layer = new HEU_VolumeLayer(); layer._layerName = volumeName; if (bHeightPart) { _layers.Insert(0, layer); } else { _layers.Add(layer); } } layer._part = part; GetPartLayerAttributes(session, geoNode.GeoID, part.PartID, layer); if (!bHeightPart) { part.DestroyAllData(); } if (!_updatedLayers.Contains(layer)) { if (bHeightPart) { _updatedLayers.Insert(0, layer); } else { _updatedLayers.Add(layer); } } }
public static void DestroyBakedGameObjectsWithEndName(List<GameObject> gameObjectsToDestroy, string endName) { int numLeft = gameObjectsToDestroy.Count; for (int i = 0; i < numLeft; ++i) { GameObject deleteGO = gameObjectsToDestroy[i]; if (string.IsNullOrEmpty(endName) || deleteGO.name.EndsWith(endName)) { gameObjectsToDestroy[i] = null; HEU_PartData.DestroyExistingGeneratedComponentsMeshData(deleteGO, true); HEU_GeneralUtility.DestroyImmediate(deleteGO); } } }
/// <summary> /// Returns the HEU_PartData with the given output gameobject. /// </summary> /// <param name="outputGameObject">The output gameobject to check</param> /// <returns>Valid HEU_PartData or null if no match</returns> public HEU_PartData GetHDAPartWithGameObject(GameObject outputGameObject) { HEU_PartData foundPart = null; foreach (HEU_PartData part in _parts) { foundPart = part.GetHDAPartWithGameObject(outputGameObject); if (foundPart != null) { return(foundPart); } } return(null); }
/// <summary> /// Returns the HEU_PartData with the given output gameobject. /// </summary> /// <param name="outputGameObject">The output gameobject to check</param> /// <returns>Valid HEU_PartData or null if no match</returns> public HEU_PartData GetHDAPartWithGameObject(GameObject outputGameObject) { HEU_PartData foundPart = null; foreach (HEU_GeoNode geoNode in _geoNodes) { foundPart = geoNode.GetHDAPartWithGameObject(outputGameObject); if (foundPart != null) { return foundPart; } } return null; }
public void GetPartsByOutputType(List <HEU_PartData> meshParts, List <HEU_PartData> volumeParts) { int numParts = _parts.Count; for (int i = 0; i < numParts; ++i) { HEU_PartData part = _parts[i]; if (part.IsPartMesh() || part.IsPartCurve()) { meshParts.Add(part); } else if (part.IsPartVolume()) { volumeParts.Add(part); } } }
private void SetupGameObjectAndTransform(HEU_PartData partData, HEU_HoudiniAsset parentAsset) { // Checking for nulls for undo safety if (partData == null || parentAsset == null || parentAsset.OwnerGameObject == null || parentAsset.RootGameObject == null) { return; } // Set a valid gameobject for this part if (partData.OutputGameObject == null) { partData.SetGameObject(HEU_GeneralUtility.CreateNewGameObject()); } // The parent is either the asset root, OR if this is instanced and not visible, then the HDA data is the parent // The parent transform is either the asset root (for a display node), // or the HDA_Data gameobject (for instanced, not visible, intermediate, editable non-display nodes) Transform partTransform = partData.OutputGameObject.transform; if (partData.IsPartInstanced() || (_containerObjectNode.IsInstanced() && !_containerObjectNode.IsVisible()) || partData.IsPartCurve() || (IsIntermediateOrEditable() && !Displayable)) { partTransform.parent = parentAsset.OwnerGameObject.transform; } else { partTransform.parent = parentAsset.RootGameObject.transform; } HEU_GeneralUtility.CopyFlags(partTransform.parent.gameObject, partData.OutputGameObject, true); // Reset to origin partTransform.localPosition = Vector3.zero; partTransform.localRotation = Quaternion.identity; partTransform.localScale = Vector3.one; // Destroy the children generated from ComposeNChildren HEU_GeneralUtility.DestroyAutoGeneratedChildren(partData.OutputGameObject); }
public static void BakeGameObjectComponents(GameObject sourceGO, GameObject targetGO, string assetName, string outputPath, bool bIsInstancer) { UnityEngine.Object assetDBObject = null; Dictionary<Mesh, Mesh> sourceToTargetMeshMap = new Dictionary<Mesh, Mesh>(); Dictionary<Material, Material> sourceToCopiedMaterials = new Dictionary<Material, Material>(); string newAssetDBObjectFileName = HEU_AssetDatabase.AppendMeshesAssetFileName(assetName); HEU_PartData.BakePartToGameObject( partData: null, srcGO: sourceGO, targetGO: targetGO, assetName: assetName, bIsInstancer: bIsInstancer, bDeleteExistingComponents: false, // Materials might be overwritten if true bDontDeletePersistantResources: false, bWriteMeshesToAssetDatabase: true, bakedAssetPath: ref outputPath, sourceToTargetMeshMap, sourceToCopiedMaterials, assetDBObject: ref assetDBObject, assetObjectFileName: newAssetDBObjectFileName, bReconnectPrefabInstances: false, bKeepPreviousTransformValues: false ); }
public void GenerateGeometry(HEU_SessionBase session) { // Volumes could come in as a geonode + part for each heightfield layer. // Otherwise the other geo types can be done individually. bool bResult = false; List<HEU_PartData> meshParts = new List<HEU_PartData>(); List<HEU_PartData> volumeParts = new List<HEU_PartData>(); List<HEU_PartData> partsToDestroy = new List<HEU_PartData>(); HEU_HoudiniAsset parentAsset = ParentAsset; foreach (HEU_GeoNode geoNode in _geoNodes) { geoNode.GetPartsByOutputType(meshParts, volumeParts); } // Meshes foreach (HEU_PartData part in meshParts) { bResult = part.GenerateMesh(session, parentAsset.GenerateUVs, parentAsset.GenerateTangents, parentAsset.GenerateNormals, parentAsset.UseLODGroups); if (!bResult) { partsToDestroy.Add(part); } } #if TERRAIN_SUPPORTED // Volumes // Each layer in the volume is retrieved as a volume part, in the display geo node. // But we need to handle all layers as 1 terrain output in Unity, with 1 height layer and // other layers as alphamaps. if (volumeParts.Count > 0) { HEU_PartData heightLayerPart = null; HEU_VolumeCache volumeCache = new HEU_VolumeCache(); volumeCache.GenerateTerrainFromParts(session, volumeParts, ParentAsset, out heightLayerPart); // Remove volume parts that are not the height layer (even if heightLayerPart is null) foreach (HEU_PartData part in volumeParts) { if (part != heightLayerPart) { partsToDestroy.Add(part); } } } #endif int numPartsToDestroy = partsToDestroy.Count; for(int i = 0; i < numPartsToDestroy; ++i) { HEU_GeoNode parentNode = partsToDestroy[i].ParentGeoNode; if (parentNode != null) { parentNode.RemoveAndDestroyPart(partsToDestroy[i]); } else { HEU_PartData.DestroyPart(partsToDestroy[i]); } } partsToDestroy.Clear(); ApplyObjectTransformToGeoNodes(); // Set visibility bool bIsVisible = IsVisible(); foreach (HEU_GeoNode geoNode in _geoNodes) { geoNode.CalculateVisiblity(bIsVisible); } // Create editable attributes. // This should happen after visibility has been calculated above // since we need to show/hide the intermediate geometry during painting. foreach (HEU_PartData part in meshParts) { if (part.ParentGeoNode.IsIntermediateOrEditable()) { part.SetupAttributeGeometry(session); } } }
/// <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 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); } }
public void RemoveAndDestroyPart(HEU_PartData part) { _parts.Remove(part); HEU_PartData.DestroyPart(part); }
public void UpdateLayerFromPart(HEU_SessionBase session, HEU_PartData part) { HEU_GeoNode geoNode = part.ParentGeoNode; HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); bool bResult = session.GetVolumeInfo(geoNode.GeoID, part.PartID, ref volumeInfo); if (!bResult || volumeInfo.tupleSize != 1 || volumeInfo.zLength != 1 || volumeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT) { return; } string volumeName = HEU_SessionManager.GetString(volumeInfo.nameSH, session); part.SetVolumeLayerName(volumeName); //Debug.LogFormat("Part name: {0}, GeoName: {1}, Volume Name: {2}, Display: {3}", part.PartName, geoNode.GeoName, volumeName, geoNode.Displayable); bool bHeightPart = volumeName.Equals(HEU_Defines.HAPI_HEIGHTFIELD_LAYERNAME_HEIGHT); bool bMaskPart = volumeName.Equals(HEU_Defines.HAPI_HEIGHTFIELD_LAYERNAME_MASK); HEU_VolumeLayer layer = GetLayer(volumeName); if (layer == null) { layer = new HEU_VolumeLayer(); layer._layerName = volumeName; if (bHeightPart) { _layers.Insert(0, layer); } else if(!bMaskPart) { _layers.Add(layer); } } layer._part = part; layer._xLength = volumeInfo.xLength; layer._yLength = volumeInfo.yLength; if (!bMaskPart) { GetPartLayerAttributes(session, geoNode.GeoID, part.PartID, layer); } if (!bHeightPart) { // Non-height parts don't have any outputs as they are simply layers carrying info part.DestroyAllData(); } else { // Height part List<HEU_TreePrototypeInfo> treePrototypeInfos = HEU_TerrainUtility.GetTreePrototypeInfosFromPart(session, geoNode.GeoID, part.PartID); if (treePrototypeInfos != null) { if (_scatterTrees == null) { _scatterTrees = new HEU_VolumeScatterTrees(); } _scatterTrees._treePrototypInfos = treePrototypeInfos; } } if (!_updatedLayers.Contains(layer)) { if (bHeightPart) { _updatedLayers.Insert(0, layer); } else if (!bMaskPart) { _updatedLayers.Add(layer); } } }
public void UpdateLayerFromPart(HEU_SessionBase session, HEU_PartData part) { HEU_GeoNode geoNode = part.ParentGeoNode; HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); bool bResult = session.GetVolumeInfo(geoNode.GeoID, part.PartID, ref volumeInfo); if (!bResult || volumeInfo.tupleSize != 1 || volumeInfo.zLength != 1 || volumeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT) { return; } string volumeName = HEU_SessionManager.GetString(volumeInfo.nameSH, session); part.SetVolumeLayerName(volumeName); //Debug.LogFormat("Part name: {0}, GeoName: {1}, Volume Name: {2}, Display: {3}", part.PartName, geoNode.GeoName, volumeName, geoNode.Displayable); HEU_VolumeLayer.HFLayerType layerType = GetHeightfieldLayerType(session, geoNode.GeoID, part.PartID, volumeName); HEU_VolumeLayer layer = GetLayer(volumeName); if (layer == null) { layer = new HEU_VolumeLayer(); layer._layerName = volumeName; if (layerType == HEU_VolumeLayer.HFLayerType.HEIGHT) { _layers.Insert(0, layer); } else if(layerType != HEU_VolumeLayer.HFLayerType.MASK) { _layers.Add(layer); } } layer._part = part; layer._xLength = volumeInfo.xLength; layer._yLength = volumeInfo.yLength; layer._layerType = layerType; if (layerType != HEU_VolumeLayer.HFLayerType.MASK) { GetPartLayerAttributes(session, geoNode.GeoID, part.PartID, layer); } if (layerType != HEU_VolumeLayer.HFLayerType.HEIGHT) { // Non-height parts don't have any outputs as they are simply layers carrying info part.DestroyAllData(); } else { // Height part // Might contain terrain properties via attributes (i.e. not layer specific, but for entire terrain) // Scatter Tree Prototypes List<HEU_TreePrototypeInfo> treePrototypeInfos = HEU_TerrainUtility.GetTreePrototypeInfosFromPart(session, geoNode.GeoID, part.PartID); if (treePrototypeInfos != null) { if (_scatterTrees == null) { _scatterTrees = new HEU_VolumeScatterTrees(); } _scatterTrees._treePrototypInfos = treePrototypeInfos; } // Detail distance HAPI_AttributeInfo detailDistanceAttrInfo = new HAPI_AttributeInfo(); int[] detailDistances = new int[0]; HEU_GeneralUtility.GetAttribute(session, geoNode.GeoID, part.PartID, HEU_Defines.HEIGHTFIELD_DETAIL_DISTANCE, ref detailDistanceAttrInfo, ref detailDistances, session.GetAttributeIntData); // Scatter Detail Resolution Per Patch (note that Detail Resolution comes from HF layer size) HAPI_AttributeInfo resolutionPatchAttrInfo = new HAPI_AttributeInfo(); int[] resolutionPatches = new int[0]; HEU_GeneralUtility.GetAttribute(session, geoNode.GeoID, part.PartID, HEU_Defines.HEIGHTFIELD_DETAIL_RESOLUTION_PER_PATCH, ref resolutionPatchAttrInfo, ref resolutionPatches, session.GetAttributeIntData); if (_detailProperties == null) { _detailProperties = new HEU_DetailProperties(); } // Unity only supports 1 set of detail resolution properties per terrain int arraySize = 1; if (detailDistanceAttrInfo.exists && detailDistances.Length >= arraySize) { _detailProperties._detailDistance = detailDistances[0]; } if (resolutionPatchAttrInfo.exists && resolutionPatches.Length >= arraySize) { _detailProperties._detailResolutionPerPatch = resolutionPatches[0]; } } if (!_updatedLayers.Contains(layer)) { if (layerType == HEU_VolumeLayer.HFLayerType.HEIGHT) { _updatedLayers.Insert(0, layer); } else if (layerType != HEU_VolumeLayer.HFLayerType.MASK) { _updatedLayers.Add(layer); } } }
public void GenerateTerrainFromParts(HEU_SessionBase session, List<HEU_PartData> volumeParts, HEU_HoudiniAsset houdiniAsset, out HEU_PartData heightLayerPart) { _heightMapVolumeData = null; _textureVolumeDatas = new List<HEU_VolumeData>(); ParseVolumeDatas(session, volumeParts); TerrainData terrainData = null; Vector3 terrainOffsetPosition = Vector3.zero; Generate(session, houdiniAsset, out terrainData, out terrainOffsetPosition); if(_heightMapVolumeData != null && _heightMapVolumeData._partData != null && terrainData != null) { UnityEngine.Object terrainDataObject = null; houdiniAsset.AddToAssetDBCache(string.Format("Asset_{0}_TerrainData", _heightMapVolumeData._partData.ParentGeoNode.GeoName), terrainData, ref terrainDataObject); _heightMapVolumeData._partData.SetTerrainPart(terrainDataObject, terrainOffsetPosition); heightLayerPart = _heightMapVolumeData._partData; } else { heightLayerPart = null; } }
public void UpdateLayerFromPart(HEU_SessionBase session, HEU_PartData part) { HEU_GeoNode geoNode = part.ParentGeoNode; HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); bool bResult = session.GetVolumeInfo(geoNode.GeoID, part.PartID, ref volumeInfo); if (!bResult || volumeInfo.tupleSize != 1 || volumeInfo.zLength != 1 || volumeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT) { return; } string volumeName = HEU_SessionManager.GetString(volumeInfo.nameSH, session); part.SetVolumeLayerName(volumeName); //Debug.LogFormat("Part name: {0}, GeoName: {1}, Volume Name: {2}, Display: {3}", part.PartName, geoNode.GeoName, volumeName, geoNode.Displayable); HFLayerType layerType = HEU_TerrainUtility.GetHeightfieldLayerType(session, geoNode.GeoID, part.PartID, volumeName); HEU_VolumeLayer layer = GetLayer(volumeName); if (layer == null) { layer = new HEU_VolumeLayer(); layer._layerName = volumeName; if (layerType == HFLayerType.HEIGHT) { _layers.Insert(0, layer); } else if (layerType != HFLayerType.MASK) { _layers.Add(layer); } } layer._part = part; layer._xLength = volumeInfo.xLength; layer._yLength = volumeInfo.yLength; layer._layerType = layerType; if (layerType != HFLayerType.MASK) { GetPartLayerAttributes(session, geoNode.GeoID, part.PartID, layer); } if (layerType != HFLayerType.HEIGHT) { // Non-height parts don't have any outputs as they are simply layers carrying info part.DestroyAllData(); } else { // Height part // Might contain terrain properties via attributes (i.e. not layer specific, but for entire terrain) // Scatter Tree Prototypes List<HEU_TreePrototypeInfo> treePrototypeInfos = HEU_TerrainUtility.GetTreePrototypeInfosFromPart(session, geoNode.GeoID, part.PartID); if (treePrototypeInfos != null) { if (_scatterTrees == null) { _scatterTrees = new HEU_VolumeScatterTrees(); } _scatterTrees._treePrototypInfos = treePrototypeInfos; } HEU_TerrainUtility.PopulateDetailProperties(session, geoNode.GeoID, part.PartID, ref _detailProperties); } if (!_updatedLayers.Contains(layer)) { if (layerType == HFLayerType.HEIGHT) { _updatedLayers.Insert(0, layer); } else if (layerType != HFLayerType.MASK) { _updatedLayers.Add(layer); } } }
public void GenerateGeometry(HEU_SessionBase session, bool bRebuild) { // Volumes could come in as a geonode + part for each heightfield layer. // Otherwise the other geo types can be done individually. bool bResult = false; List<HEU_PartData> meshParts = new List<HEU_PartData>(); List<HEU_PartData> volumeParts = new List<HEU_PartData>(); List<HEU_PartData> partsToDestroy = new List<HEU_PartData>(); HEU_HoudiniAsset parentAsset = ParentAsset; foreach (HEU_GeoNode geoNode in _geoNodes) { geoNode.GetPartsByOutputType(meshParts, volumeParts); if (volumeParts.Count > 0) { // Volumes // Each layer in the volume is retrieved as a volume part, in the display geo node. // But we need to handle all layers as 1 terrain output in Unity, with 1 height layer and // other layers as alphamaps. geoNode.ProcessVolumeParts(session, volumeParts, bRebuild); // Clear the volume parts after processing since we are done with this set volumeParts.Clear(); } } // Meshes foreach (HEU_PartData part in meshParts) { // This returns false when there is no valid geometry or is not instancing. Should remove it as otherwise // stale data sticks around on recook bResult = part.GenerateMesh(session, parentAsset.GenerateUVs, parentAsset.GenerateTangents, parentAsset.GenerateNormals, parentAsset.UseLODGroups); if (!bResult) { partsToDestroy.Add(part); } } int numPartsToDestroy = partsToDestroy.Count; for (int i = 0; i < numPartsToDestroy; ++i) { HEU_GeoNode parentNode = partsToDestroy[i].ParentGeoNode; if (parentNode != null) { parentNode.RemoveAndDestroyPart(partsToDestroy[i]); } else { HEU_PartData.DestroyPart(partsToDestroy[i]); } } partsToDestroy.Clear(); ApplyObjectTransformToGeoNodes(); // Set visibility and attribute-based tag, layer, and scripts bool bIsVisible = IsVisible(); foreach (HEU_GeoNode geoNode in _geoNodes) { geoNode.CalculateVisiblity(bIsVisible); geoNode.CalculateColliderState(); geoNode.SetAttributeModifiersOnPartOutputs(session); } // Create editable attributes. // This should happen after visibility has been calculated above // since we need to show/hide the intermediate geometry during painting. foreach (HEU_PartData part in meshParts) { if (part.ParentGeoNode.IsIntermediateOrEditable()) { part.SetupAttributeGeometry(session); } } }
/// <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 }