public static Material LoadSubstanceMaterialWithName(string materialPath, string substanceName) { Material material = LoadUnityMaterial(materialPath); #if UNITY_2017_4_OR_NEWER || UNITY_2018_1_OR_NEWER HEU_Logger.LogErrorFormat("Houdini Engine for Unity does not support the new Substance plugin as of yet!"); #elif UNITY_EDITOR if (material != null) { string assetPath = HEU_AssetDatabase.GetAssetPath(material); SubstanceImporter substanceImporter = AssetImporter.GetAtPath(assetPath) as SubstanceImporter; ProceduralMaterial[] proceduralMaterials = substanceImporter.GetMaterials(); for(int i = 0; i < proceduralMaterials.Length; ++i) { if(proceduralMaterials[i].name.Equals(substanceName)) { material = proceduralMaterials[i]; break; } } } #endif if (material != null) { HEU_Logger.LogFormat("Loaded Substance material with name {0} from path {1}.", substanceName, materialPath); } else { HEU_Logger.LogWarningFormat("Failed to load Substance material with name {0} from path {1}.", substanceName, materialPath); } return material; }
public static Material LoadSubstanceMaterialWithIndex(string materialPath, int substanceMaterialIndex) { Material material = LoadUnityMaterial(materialPath); #if UNITY_2017_4_OR_NEWER || UNITY_2018_1_OR_NEWER HEU_Logger.LogErrorFormat("Houdini Engine for Unity does not support the new Substance plugin as of yet!"); #elif UNITY_EDITOR if (material != null) { string assetPath = HEU_AssetDatabase.GetAssetPath(material); SubstanceImporter substanceImporter = AssetImporter.GetAtPath(assetPath) as SubstanceImporter; if(substanceMaterialIndex >= 0 && substanceMaterialIndex < substanceImporter.GetMaterialCount()) { material = substanceImporter.GetMaterials()[substanceMaterialIndex]; } } #endif if (material != null) { HEU_Logger.LogFormat("Loaded Substance material with index {0} from path {1}.", substanceMaterialIndex, materialPath); } else { HEU_Logger.LogWarningFormat("Failed to load Substance material with index {0} from path {1}.", substanceMaterialIndex, materialPath); } return material; }
public static Material GetNewMaterialWithShader(string assetCacheFolderPath, string shaderName, string materialName = "", bool bWriteToFile = true) { Material material = null; Shader shader = FindShader(shaderName); if (shader != null) { material = new Material(shader); if (materialName == null || materialName.Length == 0) { material.name = shaderName; } else { material.name = materialName; } if (bWriteToFile && !string.IsNullOrEmpty(assetCacheFolderPath)) { string materialFileName = materialName + HEU_Defines.HEU_EXT_MAT; HEU_AssetDatabase.CreateObjectInAssetCacheFolder(material, assetCacheFolderPath, HEU_Defines.HEU_FOLDER_MATERIALS, materialFileName, typeof(Material), bOverwriteExisting: true); } } else { HEU_Logger.LogWarningFormat("Shader with name {0} not found!", shaderName); } return material; }
/// <summary> /// Add the given attribute to the internal map by name. /// </summary> /// <param name="attribute">Attribute data to store</param> public void SetAttribute(HEU_OutputAttribute attribute) { if (string.IsNullOrEmpty(attribute._name)) { HEU_Logger.LogWarningFormat("Unable to store attribute with empty name!", attribute._name); return; } _attributes.Add(attribute._name, attribute); }
public static HEU_MaterialData CreateUnitySubstanceMaterialData(int materialKey, string materialPath, string substanceName, int substanceIndex, List<HEU_MaterialData> materialCache, string assetCacheFolderPath) { // Let's make sure we can find the Unity or Substance material first Material material = null; HEU_MaterialData.Source sourceType = HEU_MaterialData.Source.UNITY; if (!string.IsNullOrEmpty(substanceName)) { sourceType = HEU_MaterialData.Source.SUBSTANCE; material = HEU_MaterialFactory.LoadSubstanceMaterialWithName(materialPath, substanceName); } else if (substanceIndex >= 0) { sourceType = HEU_MaterialData.Source.SUBSTANCE; material = HEU_MaterialFactory.LoadSubstanceMaterialWithIndex(materialPath, substanceIndex); } else if (!string.IsNullOrEmpty(materialPath)) { material = HEU_MaterialFactory.LoadUnityMaterial(materialPath); } if (material != null) { HEU_MaterialData materialData = ScriptableObject.CreateInstance<HEU_MaterialData>(); materialData._materialSource = sourceType; materialData._materialKey = materialKey; materialData._material = material; materialCache.Add(materialData); return materialData; } else { // We can't find the material in Unity, so notify user and use a default one which allows to at least get the geometry in. if (string.IsNullOrEmpty(materialPath)) { HEU_Logger.LogWarningFormat("Empty material name found. Using default material."); } else if (materialPath.Contains("Resources/unity_builtin_extra")) { // Built in material. Don't display error. } else { HEU_Logger.LogErrorFormat("Unable to find {0} material {1}. Using a default material instead. Please check material exists in project and reload asset!", sourceType, materialPath); } // The materialKey helps uniquely identify this material for further look ups. But we also need to get a valid file name // to create the default material, so strip out just the file name. string strippedFileName = HEU_Platform.GetFileName(materialPath); return CreateMaterialInCache(materialKey, strippedFileName, HEU_MaterialData.Source.UNITY, false, materialCache, assetCacheFolderPath); } }
public static void ExecuteToolNoInput(string toolName, string toolPath) { GameObject go = HEU_HAPIUtility.InstantiateHDA(toolPath, Vector3.zero, HEU_SessionManager.GetOrCreateDefaultSession(), false); if (go == null) { HEU_Logger.LogWarningFormat("Failed to instantiate tool: {0}", toolName); } else { HEU_EditorUtility.SelectObject(go); } }
public static void ExecuteToolGenerator(string toolName, string toolPath, Vector3 targetPosition, Quaternion targetRotation, Vector3 targetScale) { GameObject go = HEU_HAPIUtility.InstantiateHDA(toolPath, targetPosition, HEU_SessionManager.GetOrCreateDefaultSession(), true); if (go != null) { go.transform.rotation = targetRotation; go.transform.localScale = targetScale; HEU_EditorUtility.SelectObject(go); } else { HEU_Logger.LogWarningFormat("Failed to instantiate tool: {0}", toolName); } }
/// <summary> /// Create a unique asset cache folder for the given asset path. /// The given asset path should be the HDA's path in the project. /// </summary> /// <param name="suggestedAssetPath">A suggested path to try. Will use default if empty or null./param> /// <returns>Unique asset cache folder for given asset path</returns> public static string CreateAssetCacheFolder(string suggestedAssetPath, int hash = 0) { #if UNITY_EDITOR // We create a unique folder inside our plugin's asset database cache folder. string assetDBPath = GetAssetCachePath(); string assetWorkingPath = HEU_Platform.BuildPath(assetDBPath, HEU_Defines.HEU_WORKING_PATH); if (!AssetDatabase.IsValidFolder(assetWorkingPath)) { AssetDatabase.CreateFolder(assetDBPath, HEU_Defines.HEU_WORKING_PATH); } string fileName = HEU_Platform.GetFileNameWithoutExtension(suggestedAssetPath); if (string.IsNullOrEmpty(fileName)) { fileName = "AssetCache"; HEU_Logger.LogWarningFormat("Unable to get file name from {0}. Using default value: {1}.", suggestedAssetPath, fileName); } if (HEU_PluginSettings.ShortenFolderPaths && fileName.Length >= 3 && hash != 0) { fileName = fileName.Substring(0, 3) + hash; } string fullPath = HEU_Platform.BuildPath(assetWorkingPath, fileName); // Gives us the unique folder path, which we then separate out to create this folder fullPath = AssetDatabase.GenerateUniqueAssetPath(fullPath); CreatePathWithFolders(fullPath); if (!AssetDatabase.IsValidFolder(fullPath)) { HEU_Logger.LogErrorFormat("Unable to create a valid asset cache folder: {0}! Check directory permission or that enough space is available!", fullPath); fullPath = null; } return fullPath; #else // TODO RUNTIME: AssetDatabase is not supported at runtime. Do we need to support this for runtime? HEU_Logger.LogWarning(HEU_Defines.HEU_USERMSG_NONEDITOR_NOT_SUPPORTED); return null; #endif }
public static Dictionary<int, HEU_MaterialData> GetMaterialDataMapFromCache(List<HEU_MaterialData> materialCache) { Dictionary<int, HEU_MaterialData> materialMap = new Dictionary<int, HEU_MaterialData>(); if (materialCache != null) { foreach (HEU_MaterialData materialData in materialCache) { if (materialData._materialKey == HEU_Defines.HEU_INVALID_MATERIAL) { HEU_Logger.LogWarningFormat("Invalid material key found! Recommend to reload HDA!"); } else { materialMap.Add(materialData._materialKey, materialData); } } } return materialMap; }
/// <summary> /// Import the asset at the given path. /// </summary> /// <param name="assetPath"></param> /// <param name="options"></param> public static void ImportAsset(string assetPath, HEU_ImportAssetOptions heuOptions) { #if UNITY_EDITOR ImportAssetOptions unityOptions = ImportAssetOptions.Default; switch (heuOptions) { case HEU_ImportAssetOptions.Default: unityOptions = ImportAssetOptions.Default; break; case HEU_ImportAssetOptions.ForceUpdate: unityOptions = ImportAssetOptions.ForceUpdate; break; case HEU_ImportAssetOptions.ForceSynchronousImport: unityOptions = ImportAssetOptions.ForceSynchronousImport; break; case HEU_ImportAssetOptions.ImportRecursive: unityOptions = ImportAssetOptions.ImportRecursive; break; case HEU_ImportAssetOptions.DontDownloadFromCacheServer: unityOptions = ImportAssetOptions.DontDownloadFromCacheServer; break; case HEU_ImportAssetOptions.ForceUncompressedImport: unityOptions = ImportAssetOptions.ForceUncompressedImport; break; default: HEU_Logger.LogWarningFormat("Unsupported import options: {0}", heuOptions); break; } AssetDatabase.ImportAsset(assetPath, unityOptions); #else HEU_Logger.LogWarning(HEU_Defines.HEU_USERMSG_NONEDITOR_NOT_SUPPORTED); #endif }
/// <summary> /// Load stored session data and recreate the session objects. /// </summary> public static void LoadAllSessionData() { // Clear existing sessions, and load session data from storage. // Then create session for each session data. _sessionMap.Clear(); List<HEU_SessionData> sessionDatas = HEU_PluginStorage.LoadAllSessionData(); foreach (HEU_SessionData sessionData in sessionDatas) { if (sessionData != null) { try { // Create session based on type HEU_SessionBase sessionBase = CreateSessionFromType(sessionData.SessionClassType); if (sessionBase != null) { sessionBase.SetSessionData(sessionData); _sessionMap.Add(sessionData.SessionID, sessionBase); if (sessionData.IsDefaultSession) { _defaultSession = sessionBase; } // Find assets in scene with session ID. Check if valid and reset those that aren't. } } catch (System.Exception ex) { HEU_Logger.LogWarningFormat("Loading session with ID {0} failed with {1}. Ignoring the session.", sessionData.SessionID, ex.ToString()); } } } InternalValidateSceneAssets(); }
/// <summary> /// Load the saved plugin settings from disk. /// </summary> /// <returns>True if successfully loaded.</returns> public bool LoadPluginData() { // First check if settings pref file exists string settingsFilePath = SettingsFilePath(); if (!HEU_Platform.DoesFileExist(settingsFilePath)) { // Try reading from EditorPrefs to see if this is still using the old method return ReadFromEditorPrefs(); } // Open file and read each line to create the settings entry using (StreamReader reader = new StreamReader(settingsFilePath)) { // Must match first line string line = reader.ReadLine(); if (string.IsNullOrEmpty(line) || !line.Equals(PluginSettingsLine1)) { HEU_Logger.LogWarningFormat("Unable to load Plugin settings file. {0} should have line 1: {1}", settingsFilePath, PluginSettingsLine1); return false; } // Must match 2nd line line = reader.ReadLine(); if (string.IsNullOrEmpty(line) || !line.StartsWith(PluginSettingsLine2)) { HEU_Logger.LogWarningFormat("Unable to load Plugin settings file. {0} should start line 2 with: {1}", settingsFilePath, PluginSettingsLine2); return false; } Dictionary<string, StoreData> storeMap = new Dictionary<string, StoreData>(); string keyStr; string typeStr; string valueStr; DataType dataType; // "key(type)=value" System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(@"^(\w+)\((\w+)\)=(.*)"); while ((line = reader.ReadLine()) != null) { System.Text.RegularExpressions.Match match = regex.Match(line); if (match.Success && match.Groups.Count >= 4) { keyStr = match.Groups[1].Value; typeStr = match.Groups[2].Value; valueStr = match.Groups[3].Value; if (!string.IsNullOrEmpty(keyStr) && !string.IsNullOrEmpty(typeStr) && !string.IsNullOrEmpty(valueStr)) { try { dataType = (DataType)System.Enum.Parse(typeof(DataType), typeStr); StoreData store = new StoreData(); store._type = dataType; store._valueStr = valueStr; storeMap.Add(keyStr, store); } catch (System.Exception ex) { HEU_Logger.LogErrorFormat("Invalid data type found in settings: {0}. Exception: {1}", typeStr, ex.ToString()); } } } } _dataMap = storeMap; } return true; }
/// <summary> /// Process the part at the given index, creating its data (geometry), /// and adding it to the list of parts. /// </summary> /// <param name="session"></param> /// <param name="partID"></param> /// <returns>A valid HEU_PartData if it has been successfully processed.</returns> private void ProcessPart(HEU_SessionBase session, int partID, ref HAPI_PartInfo partInfo, ref HEU_PartData partData) { HEU_HoudiniAsset parentAsset = ParentAsset; if (parentAsset == null) { return; } bool bResult = true; //HEU_Logger.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, 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) { if (HEU_GeneralUtility.HasValidInstanceAttribute(session, GeoID, partID, HEU_PluginSettings.UnityInstanceAttr)) { isAttribInstancer = true; } else if (HEU_GeneralUtility.HasValidInstanceAttribute(session, GeoID, partID, HEU_Defines.HEIGHTFIELD_TREEINSTANCE_PROTOTYPEINDEX)) { 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, partID); } 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) { HEU_Logger.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 mesh data to handle 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 HEU_Logger.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 { HEU_Logger.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 HEU_Logger.LogFormat("PART PROCESS TIME:: NAME={0}, TIME={1}", HEU_SessionManager.GetString(partInfo.nameSH, session), (Time.realtimeSinceStartup - processPartStartTime)); #endif }
public static void ExecuteToolOperatorMultiple(string toolName, string toolPath, GameObject[] inputObjects) { GameObject outputObjectToSelect = null; GameObject go = HEU_HAPIUtility.InstantiateHDA(toolPath, Vector3.zero, HEU_SessionManager.GetOrCreateDefaultSession(), false); if (go == null) { HEU_Logger.LogWarningFormat("Failed to instantiate tool: {0}", toolName); return; } HEU_HoudiniAssetRoot assetRoot = go.GetComponent<HEU_HoudiniAssetRoot>(); if (assetRoot != null) { HEU_HoudiniAsset asset = assetRoot._houdiniAsset; HEU_SessionBase session = asset.GetAssetSession(true); int numInputs = inputObjects.Length; List<HEU_InputNode> inputNodes = asset.GetInputNodes(); if (inputNodes == null || inputNodes.Count == 0) { HEU_Logger.LogErrorFormat("Unable to assign input geometry due to no asset inputs on selected tool."); } else { // User could have selected any number of inputs objects, and asset could have any number of inputs. // So use minimum of either to set input object into asset input. int minInputCount = Mathf.Min(inputNodes.Count, numInputs); for (int i = 0; i < minInputCount; ++i) { bool bShouldUseHDA = IsValidInputHDA(inputObjects[i]); if (!bShouldUseHDA &&!IsValidInputMesh(inputObjects[i])) { continue; } else if (bShouldUseHDA && !IsValidInputHDA(inputObjects[i])) { continue; } GameObject inputObject = inputObjects[i]; HEU_InputNode inputNode = inputNodes[i]; inputNode.ResetInputNode(session); if (!bShouldUseHDA) { inputNode.ChangeInputType(session, HEU_InputNode.InputObjectType.UNITY_MESH); HEU_InputObjectInfo inputInfo = inputNode.AddInputEntryAtEndMesh(inputObject); if (inputInfo != null) { inputInfo._useTransformOffset = false; inputNode.KeepWorldTransform = true; inputNode.PackGeometryBeforeMerging = false; inputNode.RequiresUpload = true; } else { HEU_Logger.LogErrorFormat("Invalid input format: {0}", inputObject.gameObject.name); } } else { inputNode.ChangeInputType(session, HEU_InputNode.InputObjectType.HDA); HEU_InputHDAInfo inputHDAInfo = inputNode.AddInputEntryAtEndHDA(inputObject); if (inputHDAInfo != null) { inputNode.KeepWorldTransform = true; inputNode.PackGeometryBeforeMerging = false; inputNode.RequiresUpload = true; } else { HEU_Logger.LogErrorFormat("Invalid input format: {0}", inputObject.gameObject.name); } } } asset.RequestCook(true, true, true, true); outputObjectToSelect = assetRoot.gameObject; } } if (outputObjectToSelect != null) { HEU_EditorUtility.SelectObject(outputObjectToSelect); } }
public static void ExecuteToolOperatorSingle(string toolName, string toolPath, GameObject[] inputObjects) { // Single operator means single asset input. If multiple inputs are provided, create tool for each input. bool bShouldUseHDA = ShouldUseHDA(inputObjects); List<GameObject> outputObjectsToSelect = new List<GameObject>(); int numInputs = inputObjects.Length; for (int i = 0; i < numInputs; ++i) { if (inputObjects[i] == null) { continue; } if (!bShouldUseHDA && !IsValidInputMesh(inputObjects[i])) { HEU_Logger.LogWarningFormat("Specified object {0} does not contain a valid mesh!", inputObjects[i].name); continue; } if (bShouldUseHDA && !IsValidInputHDA(inputObjects[i])) { HEU_Logger.LogWarningFormat("Specified object {0} does not contain a valid HDA input!", inputObjects[i].name); continue; } GameObject inputObject = inputObjects[i]; GameObject go = HEU_HAPIUtility.InstantiateHDA(toolPath, Vector3.zero, HEU_SessionManager.GetOrCreateDefaultSession(), false); if (go != null) { HEU_HoudiniAssetRoot assetRoot = go.GetComponent<HEU_HoudiniAssetRoot>(); if (assetRoot != null) { HEU_HoudiniAsset asset = assetRoot._houdiniAsset; HEU_SessionBase session = asset.GetAssetSession(true); List<HEU_InputNode> inputNodes = asset.GetInputNodes(); if (inputNodes == null || inputNodes.Count == 0) { HEU_Logger.LogErrorFormat("Unable to assign input geometry due to no asset inputs on selected tool."); } else { HEU_InputNode inputNode = inputNodes[0]; inputNode.ResetInputNode(session); if (!bShouldUseHDA) { inputNode.ChangeInputType(session, HEU_InputNode.InputObjectType.UNITY_MESH); HEU_InputObjectInfo inputInfo = inputNode.AddInputEntryAtEndMesh(inputObject); if (inputInfo != null) { inputInfo._useTransformOffset = false; inputNode.KeepWorldTransform = true; inputNode.PackGeometryBeforeMerging = false; inputNode.RequiresUpload = true; asset.RequestCook(true, true, true, true); outputObjectsToSelect.Add(assetRoot.gameObject); } else { HEU_Logger.LogErrorFormat("Invalid input format: {0}", inputObject.gameObject.name); } } else { inputNode.ChangeInputType(session, HEU_InputNode.InputObjectType.HDA); HEU_InputHDAInfo inputHDAInfo = inputNode.AddInputEntryAtEndHDA(inputObject); if (inputHDAInfo != null) { inputNode.KeepWorldTransform = true; inputNode.PackGeometryBeforeMerging = false; inputNode.RequiresUpload = true; asset.RequestCook(true, true, true, true); outputObjectsToSelect.Add(assetRoot.gameObject); } else { HEU_Logger.LogErrorFormat("Invalid input format: {0}", inputObject.gameObject.name); } } } } } else { HEU_Logger.LogWarningFormat("Failed to instantiate tool: {0}", toolName); } } if (outputObjectsToSelect.Count > 0) { HEU_EditorUtility.SelectObjects(outputObjectsToSelect.ToArray()); } }
private bool DrawDetailsGeometry() { bool bChanged = false; EditorGUIUtility.labelWidth = 250; { bool oldValue = HEU_PluginSettings.Curves_ShowInSceneView; bool newValue = HEU_EditorUI.DrawToggleLeft(oldValue, "Show Curves in Scene View"); if (newValue != oldValue) { HEU_PluginSettings.Curves_ShowInSceneView = newValue; HEU_HoudiniAsset.SetCurvesVisibilityInScene(newValue); bChanged = true; } } HEU_EditorUI.DrawSeparator(); { int oldValue = HEU_PluginSettings.MaxVerticesPerPrimitive; int newValue = EditorGUILayout.DelayedIntField("Max Vertices Per Primitive", oldValue); if (newValue != oldValue) { if (newValue == 3 || newValue == 4) { HEU_PluginSettings.MaxVerticesPerPrimitive = newValue; bChanged = true; } else { HEU_Logger.LogWarningFormat("Plugin only supports 3 (triangles) or 4 (quads) max vertices values."); } } } HEU_EditorUI.DrawSeparator(); { float oldValue = HEU_PluginSettings.NormalGenerationThresholdAngle; float newValue = EditorGUILayout.DelayedFloatField("Normal Generation Threshold Angle", oldValue); if (newValue != oldValue) { HEU_PluginSettings.NormalGenerationThresholdAngle = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.DefaultTerrainMaterial; if (_terrainMaterial == null && !string.IsNullOrEmpty(oldValue)) { //HEU_Logger.Log("Loading terrain material at: " + oldValue); _terrainMaterial = HEU_MaterialFactory.LoadUnityMaterial(oldValue); } Material newMaterial = EditorGUILayout.ObjectField("Default Terrain Material", _terrainMaterial, typeof(Material), false) as Material; if (newMaterial != _terrainMaterial) { string materialPath = ""; if (newMaterial != null) { materialPath = HEU_AssetDatabase.GetAssetPathWithSubAssetSupport(newMaterial); if (!string.IsNullOrEmpty(materialPath) && (materialPath.StartsWith(HEU_Defines.DEFAULT_UNITY_BUILTIN_RESOURCES))) { // Default materials need to be specially handled materialPath = HEU_AssetDatabase.GetUniqueAssetPathForUnityAsset(newMaterial); newMaterial = HEU_AssetDatabase.LoadUnityAssetFromUniqueAssetPath<Material>(materialPath); } } HEU_PluginSettings.DefaultTerrainMaterial = materialPath; _terrainMaterial = newMaterial; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.TerrainSplatTextureDefault; string newValue = EditorGUILayout.DelayedTextField("Default Terrain Splat Texture", oldValue); if (!newValue.Equals(oldValue)) { HEU_PluginSettings.TerrainSplatTextureDefault = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.DefaultStandardShader; string newValue = EditorGUILayout.DelayedTextField("Default Standard Shader", oldValue); if (!newValue.Equals(oldValue)) { HEU_PluginSettings.DefaultStandardShader = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.DefaultTransparentShader; string newValue = EditorGUILayout.DelayedTextField("Default Transparent Shader", oldValue); if (!newValue.Equals(oldValue)) { HEU_PluginSettings.DefaultTransparentShader = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.DefaultVertexColorShader; string newValue = EditorGUILayout.DelayedTextField("Default Vertex Color Shader", oldValue); if (!newValue.Equals(oldValue)) { HEU_PluginSettings.DefaultVertexColorShader = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.DefaultCurveShader; string newValue = EditorGUILayout.DelayedTextField("Default Curve Shader", oldValue); if (!newValue.Equals(oldValue)) { HEU_PluginSettings.DefaultCurveShader = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); EditorGUIUtility.labelWidth = 0; return bChanged; }
/// <summary> /// Creates a mesh input node and uploads the mesh data from inputObject. /// </summary> /// <param name="session">Session that connectNodeID exists in</param> /// <param name="connectNodeID">The node to connect the network to. Most likely a SOP/merge node</param> /// <param name="inputObject">The gameobject containing the mesh components</param> /// <param name="inputNodeID">The created input node ID</param> /// <returns>True if created network and uploaded mesh data.</returns> public override bool CreateInputNodeWithDataUpload(HEU_SessionBase session, HAPI_NodeId connectNodeID, GameObject inputObject, out HAPI_NodeId inputNodeID) { inputNodeID = HEU_Defines.HEU_INVALID_NODE_ID; // Create input node, cook it, then upload the geometry data if (!HEU_HAPIUtility.IsNodeValidInHoudini(session, connectNodeID)) { HEU_Logger.LogError("Connection node is invalid."); return false; } bool bExportColliders = settings != null && settings.ExportColliders == true; // Get upload meshes from input object HEU_InputDataMeshes inputMeshes = GenerateMeshDatasFromGameObject(inputObject, bExportColliders); if (inputMeshes == null || inputMeshes._inputMeshes == null || inputMeshes._inputMeshes.Count == 0) { HEU_Logger.LogError("No valid meshes found on input objects."); return false; } string inputName = null; HAPI_NodeId newNodeID = HEU_Defines.HEU_INVALID_NODE_ID; session.CreateInputNode(out newNodeID, inputName); if (newNodeID == HEU_Defines.HEU_INVALID_NODE_ID || !HEU_HAPIUtility.IsNodeValidInHoudini(session, newNodeID)) { HEU_Logger.LogError("Failed to create new input node in Houdini session!"); return false; } inputNodeID = newNodeID; if (!UploadData(session, inputNodeID, inputMeshes)) { if (!session.CookNode(inputNodeID, false)) { HEU_Logger.LogError("New input node failed to cook!"); return false; } return false; } bool createMergeNode = false; HAPI_NodeId mergeNodeId = HEU_Defines.HEU_INVALID_NODE_ID; if (bExportColliders) { createMergeNode = true; } if (!createMergeNode) { return true; } HAPI_NodeId parentId = HEU_HAPIUtility.GetParentNodeID(session, newNodeID); if (!session.CreateNode(parentId, "merge", null, false, out mergeNodeId)) { HEU_Logger.LogErrorFormat("Unable to create merge SOP node for connecting input assets."); return false; } if (!session.ConnectNodeInput(mergeNodeId, 0, newNodeID)) { HEU_Logger.LogErrorFormat("Unable to connect to input node!"); return false; } if (!session.SetNodeDisplay(mergeNodeId, 1)) { HEU_Logger.LogWarningFormat("Unable to set display flag!"); } inputNodeID = mergeNodeId; if (bExportColliders) { if (!UploadColliderData(session, mergeNodeId, inputMeshes, parentId)) { return false; } } if (!session.CookNode(inputNodeID, false)) { HEU_Logger.LogError("New input node failed to cook!"); return false; } return true; }
/// <summary> /// Returns HEU_UploadMeshData with mesh data found on meshGameObject. /// </summary> /// <param name="meshGameObject">The GameObject to query mesh data from</param> /// <returns>A valid HEU_UploadMeshData if mesh data found or null</returns> public static HEU_InputDataMesh CreateSingleMeshData(GameObject meshGameObject, bool bExportColliders) { HEU_InputDataMesh meshData = new HEU_InputDataMesh(); if (meshGameObject == null) { return null; } Mesh sharedMesh = GetMeshFromObject(meshGameObject); if (sharedMesh == null) { return null; } meshData._mesh = sharedMesh; meshData._numVertices = meshData._mesh.vertexCount; meshData._numSubMeshes = meshData._mesh.subMeshCount; meshData._meshName = meshGameObject.name; // Use project path is not saved in scene, otherwise just use name if (HEU_GeneralUtility.IsGameObjectInProject(meshGameObject)) { meshData._meshPath = HEU_AssetDatabase.GetAssetOrScenePath(meshGameObject); if (string.IsNullOrEmpty(meshData._meshPath)) { meshData._meshPath = meshGameObject.name; } } else { meshData._meshPath = meshGameObject.name; } //HEU_Logger.Log("Mesh Path: " + meshData._meshPath); MeshRenderer meshRenderer = meshGameObject.GetComponent<MeshRenderer>(); if (meshRenderer != null) { meshData._materials = meshRenderer.sharedMaterials; } meshData._transform = meshGameObject.transform; if (bExportColliders && meshGameObject != null) { meshData._colliders = new List<HEU_InputDataCollider>(); Collider[] colliders = meshGameObject.GetComponents<Collider>(); if (colliders != null) { for (int i = 0; i < colliders.Length; i++) { Collider collider = colliders[i]; if (collider == null) continue; HEU_InputDataCollider newCollider = new HEU_InputDataCollider(); newCollider._collider = collider; if (collider.GetType() == typeof(BoxCollider)) { newCollider._colliderType = HEU_InputColliderType.BOX; } else if (collider.GetType() == typeof(SphereCollider)) { newCollider._colliderType = HEU_InputColliderType.SPHERE; } else if (collider.GetType() == typeof(CapsuleCollider)) { newCollider._colliderType = HEU_InputColliderType.CAPSULE; } else if (collider.GetType() == typeof(MeshCollider)) { newCollider._colliderType = HEU_InputColliderType.MESH; } else { HEU_Logger.LogWarningFormat("Collider type not supported: {0}", meshGameObject.name); newCollider._collider = null; newCollider._colliderType = HEU_InputColliderType.NONE; } if (newCollider._colliderType != HEU_InputColliderType.NONE) { meshData._colliders.Add(newCollider); } } } } return meshData; }
public static void LoadShelves() { bool bSaveShelf = false; _shelves.Clear(); // Always add the default shelf HEU_Shelf defaultShelf = AddShelf(HEU_Defines.HEU_HENGINE_SHIPPED_SHELF, HEU_Defines.HEU_HENGINE_TOOLS_SHIPPED_FOLDER); if (defaultShelf == null) { return; } defaultShelf._defaultShelf = true; List<string> shelfEntries = HEU_PluginSettings.HEngineToolsShelves; if (shelfEntries == null || shelfEntries.Count == 0) { shelfEntries = new List<string>(); } // Convert shelf path + name to actual shelf objects int numShelves = shelfEntries.Count; for (int i = 0; i < numShelves; i++) { string shelfName = ""; string shelfPath = ""; GetSplitShelfEntry(shelfEntries[i], out shelfName, out shelfPath); // Ignore default shelf because we added it already if (shelfPath.Equals(HEU_Defines.HEU_HENGINE_TOOLS_SHIPPED_FOLDER)) { continue; } if (!string.IsNullOrEmpty(shelfName) && !string.IsNullOrEmpty(shelfPath)) { HEU_Shelf newShelf = new HEU_Shelf(); newShelf._shelfName = shelfName; newShelf._shelfPath = shelfPath; _shelves.Add(newShelf); } else { HEU_Logger.LogWarningFormat("Found invalid shelf with entry: {0}", shelfEntries[i]); shelfEntries.RemoveAt(i); i--; bSaveShelf = true; } } foreach (HEU_Shelf shelf in _shelves) { string realShelfPath = HEU_HAPIUtility.GetRealPathFromHFSPath(shelf._shelfPath); if (!HEU_Platform.DoesPathExist(realShelfPath)) { HEU_Logger.LogWarningFormat("Shelf path does not exist: {0}", realShelfPath); } else { bool bShelfLoaded = LoadToolsFromDirectory(realShelfPath, out shelf._tools); if (!bShelfLoaded) { HEU_Logger.LogWarningFormat("Failed to load shelf {0} at path {1}", shelf._shelfName, realShelfPath); } } } _currentSelectedShelf = HEU_PluginSettings.HEngineShelfSelectedIndex; if (_currentSelectedShelf < 0 || _currentSelectedShelf >= _shelves.Count) { _currentSelectedShelf = 0; HEU_PluginSettings.HEngineShelfSelectedIndex = _currentSelectedShelf; } if (bSaveShelf) { SaveShelf(); } _shelvesLoaded = true; }
/// <summary> /// Create an input node network and upload the given set of input objects. /// This creates a SOP/merge node, and input nodes for each object in inputObjects /// which are then connected to the merge node. /// It finds the input interface that supports each object in inputObjects for creating /// the input node and uploading the data based on the type of data. /// </summary> /// <param name="session">Session to create the input node in</param> /// <param name="assetID">Main asset ID</param> /// <param name="connectMergeID">Created SOP/merge node ID</param> /// <param name="inputObjects">List of input objects to upload</param> /// <param name="inputObjectsConnectedAssetIDs">List of input node IDs for the input nodes created</param> /// <param name="inputNode">The specified inputNode to create the node for (used for settings)</param> /// <returns>True if successfully uploading input nodes</returns> internal static bool CreateInputNodeWithMultiObjects(HEU_SessionBase session, HAPI_NodeId assetID, ref HAPI_NodeId connectMergeID, ref List<HEU_InputObjectInfo> inputObjects, ref List<HAPI_NodeId> inputObjectsConnectedAssetIDs, HEU_InputNode inputNode) { bool bKeepWorldTransform = inputNode.KeepWorldTransform; // Create the merge SOP node that the input nodes are going to connect to. if (!session.CreateNode(-1, "SOP/merge", null, true, out connectMergeID)) { HEU_Logger.LogErrorFormat("Unable to create merge SOP node for connecting input assets."); return false; } int numObjects = inputObjects.Count; for (int i = 0; i < numObjects; ++i) { HAPI_NodeId newConnectInputID = HEU_Defines.HEU_INVALID_NODE_ID; inputObjectsConnectedAssetIDs.Add(newConnectInputID); // Skipping null gameobjects. Though if this causes issues, can always let it continue // to create input node, but not upload mesh data if (inputObjects[i]._gameObject == null) { continue; } HEU_InputInterface inputInterface = GetInputInterface(inputObjects[i]); if (inputInterface == null) { HEU_Logger.LogWarningFormat("No input interface found for gameobject: {0}. Skipping upload!", inputObjects[i]._gameObject.name); continue; } // Apply settings based on the interface type. System.Type inputInterfaceType = inputInterface.GetType(); if (inputInterfaceType == typeof(HEU_InputInterfaceMesh)) { HEU_InputInterfaceMesh meshInterface = inputInterface as HEU_InputInterfaceMesh; meshInterface.Initialize(inputNode.MeshSettings); } if (inputInterfaceType == typeof(HEU_InputInterfaceTilemap)) { HEU_InputInterfaceTilemap tilemapInterface = inputInterface as HEU_InputInterfaceTilemap; tilemapInterface.Initialize(inputNode.TilemapSettings); } bool bResult = inputInterface.CreateInputNodeWithDataUpload(session, connectMergeID, inputObjects[i]._gameObject, out newConnectInputID); if (!bResult || newConnectInputID == HEU_Defines.HEU_INVALID_NODE_ID) { HEU_Logger.LogError("Failed to upload input."); continue; } inputObjectsConnectedAssetIDs[i] = newConnectInputID; if (!session.ConnectNodeInput(connectMergeID, i, newConnectInputID)) { HEU_Logger.LogErrorFormat("Unable to connect input nodes!"); return false; } UploadInputObjectTransform(session, inputObjects[i], newConnectInputID, bKeepWorldTransform); } return true; }
/// <summary> /// Upload the base height layer into heightfield network. /// </summary> /// <param name="session"></param> /// <param name="idt"></param> /// <returns></returns> public bool UploadHeightValuesWithTransform(HEU_SessionBase session, HEU_InputDataTerrain idt, ref HAPI_VolumeInfo volumeInfo) { // Get Geo, Part, and Volume infos HAPI_GeoInfo geoInfo = new HAPI_GeoInfo(); if (!session.GetGeoInfo(idt._heightNodeID, ref geoInfo)) { HEU_Logger.LogError("Unable to get geo info from heightfield node!"); return(false); } HAPI_PartInfo partInfo = new HAPI_PartInfo(); if (!session.GetPartInfo(geoInfo.nodeId, 0, ref partInfo)) { HEU_Logger.LogError("Unable to get part info from heightfield node!"); return(false); } volumeInfo = new HAPI_VolumeInfo(); if (!session.GetVolumeInfo(idt._heightNodeID, 0, ref volumeInfo)) { HEU_Logger.LogError("Unable to get volume info from heightfield node!"); return(false); } if ((volumeInfo.xLength - 1) != Mathf.RoundToInt(idt._numPointsX / idt._voxelSize) || (volumeInfo.yLength - 1) != Mathf.RoundToInt(idt._numPointsY / idt._voxelSize) || idt._terrainData.heightmapResolution != volumeInfo.xLength || idt._terrainData.heightmapResolution != volumeInfo.yLength) { HEU_Logger.LogWarning("Created heightfield in Houdini differs in voxel size from input terrain! Terrain may require resampling."); } // Update volume infos, and set it. This is required. volumeInfo.tileSize = 1; volumeInfo.type = HAPI_VolumeType.HAPI_VOLUMETYPE_HOUDINI; volumeInfo.storage = HAPI_StorageType.HAPI_STORAGETYPE_FLOAT; volumeInfo.transform = idt._transform; volumeInfo.minX = 0; volumeInfo.minY = 0; volumeInfo.minZ = 0; volumeInfo.tupleSize = 1; volumeInfo.tileSize = 1; volumeInfo.hasTaper = false; volumeInfo.xTaper = 0f; volumeInfo.yTaper = 0f; if (!session.SetVolumeInfo(idt._heightNodeID, partInfo.id, ref volumeInfo)) { HEU_Logger.LogError("Unable to set volume info on input heightfield node!"); return(false); } // Now set the height data float[,] heights = idt._terrainData.GetHeights(0, 0, idt._terrainData.heightmapResolution, idt._terrainData.heightmapResolution); int sizeX = heights.GetLength(0); int sizeY = heights.GetLength(1); int totalSize = sizeX * sizeY; // Convert to single array float[] heightsArr = new float[totalSize]; for (int j = 0; j < sizeY; j++) { for (int i = 0; i < sizeX; i++) { // Flip for coordinate system change float h = heights[i, (sizeY - j - 1)]; heightsArr[i + j * sizeX] = h * idt._heightScale; } } if (volumeInfo.xLength != volumeInfo.yLength) { HEU_Logger.LogError("Error: Houdini heightmap must be square!"); return(false); } if (idt._terrainData.heightmapResolution != volumeInfo.xLength) { // Resize heightsArr to idt._terrainData.heightmapResolution HEU_Logger.LogWarningFormat("Attempting to resize landscape from ({0}x{1}) to ({2}x{3})", idt._terrainData.heightmapResolution, idt._terrainData.heightmapResolution, volumeInfo.xLength, volumeInfo.xLength); heightsArr = HEU_TerrainUtility.ResampleData(heightsArr, idt._terrainData.heightmapResolution, idt._terrainData.heightmapResolution, volumeInfo.xLength, volumeInfo.xLength); sizeX = volumeInfo.xLength; sizeY = volumeInfo.yLength; totalSize = sizeX * sizeY; } // Set the base height layer if (!session.SetHeightFieldData(idt._heightNodeID, 0, HEU_Defines.HAPI_HEIGHTFIELD_LAYERNAME_HEIGHT, heightsArr, 0, totalSize)) { HEU_Logger.LogError("Unable to set height values on input heightfield node!"); return(false); } SetTerrainDataAttributesToHeightField(session, geoInfo.nodeId, 0, idt._terrainData); SetTreePrototypes(session, geoInfo.nodeId, 0, idt._terrainData); if (!session.CommitGeo(idt._heightNodeID)) { HEU_Logger.LogError("Unable to commit geo on input heightfield node!"); return(false); } return(true); }