public bool ApplyPreset(HEU_VolumeCachePreset volumeCachePreset) { UIExpanded = volumeCachePreset._uiExpanded; // Load the TerrainData if the path is given //Debug.Log("Get terraindata path: " + volumeCachePreset._terrainDataPath); if (!string.IsNullOrEmpty(volumeCachePreset._terrainDataPath)) { _terrainData = HEU_AssetDatabase.LoadAssetAtPath(volumeCachePreset._terrainDataPath, typeof(TerrainData)) as TerrainData; //Debug.Log("Loaded terrain? " + (_terrainData != null ? "yes" : "no")); } foreach (HEU_VolumeLayerPreset layerPreset in volumeCachePreset._volumeLayersPresets) { HEU_VolumeLayer layer = GetLayer(layerPreset._layerName); if (layer == null) { Debug.LogWarningFormat("Volume layer with name {0} not found! Unable to set heightfield layer preset.", layerPreset._layerName); return false; } layer._strength = layerPreset._strength; layer._tile = layerPreset._tile; layer._uiExpanded = layerPreset._uiExpanded; } IsDirty = true; return true; }
/// <summary> /// Loads texture at path which should be under a Resources/ folder. /// </summary> /// <param name="path">Path to texture, must be relative to a Resources/ folder.</param> /// <returns>Loaded texture or null if failed.</returns> public static Texture2D LoadTexture(string path) { if(HEU_AssetDatabase.IsPathRelativeToAssets(path) || System.IO.Path.IsPathRooted(path)) { HEU_AssetDatabase.ImportAsset(path, HEU_AssetDatabase.HEU_ImportAssetOptions.Default); return HEU_AssetDatabase.LoadAssetAtPath(path, typeof(Texture2D)) as Texture2D; } else { // Try as relative path to Resources return Resources.Load<Texture2D>(path); } }
public static Texture2D RenderAndExtractImageToTexture(HEU_SessionBase session, HAPI_MaterialInfo materialInfo, HAPI_ParmId textureParmID, string textureName, string assetCacheFolderPath) { //Debug.LogFormat("Rendering texture {0} with name {1} for material {2} at path {3}", textureParmID, textureName, materialInfo.nodeId, assetCacheFolderPath); Texture2D texture = null; // First we get Houdini to render the texture to an image buffer, then query the buffer over HAPI // Next we convert to PNG, and write out to file in our Assets directory // The reason for querying as a buffer is to workaround a bug with ExtractHoudiniImageToTextureFile // Note: intentionly ignoring any errors as sometimes there aren't any textures if (session.RenderTextureToImage(materialInfo.nodeId, textureParmID, false)) { texture = HEU_MaterialFactory.ExtractHoudiniImageToTextureRaw(session, materialInfo, "C A"); if(texture != null) { texture.name = textureName; // Get the Textures folder in the assetCacheFolderPath. Make sure it exists. assetCacheFolderPath = HEU_AssetDatabase.AppendTexturesPathToAssetFolder(assetCacheFolderPath); HEU_AssetDatabase.CreatePathWithFolders(assetCacheFolderPath); // We are defaulting to PNG here if no extension already set. This forces it to use PNG format below. if (!textureName.EndsWith(".png") && !textureName.EndsWith(".jpg")) { textureName = textureName + ".png"; } string textureFileName = HEU_Platform.BuildPath(assetCacheFolderPath, string.Format("{0}", textureName)); byte[] encodedBytes; if(textureName.EndsWith(".jpg")) { encodedBytes = texture.EncodeToJPG(); } else // Use PNG otherwise { encodedBytes = texture.EncodeToPNG(); } HEU_Platform.WriteBytes(textureFileName, encodedBytes); // Re-import for project to recognize the new texture file HEU_AssetDatabase.ImportAsset(textureFileName, HEU_AssetDatabase.HEU_ImportAssetOptions.Default); // Load the new texture file texture = HEU_AssetDatabase.LoadAssetAtPath(textureFileName, typeof(Texture2D)) as Texture2D; } //texture = HEU_MaterialFactory.ExtractHoudiniImageToTextureFile(session, materialInfo, "C A", assetCacheFolderPath); } return texture; }
/// <summary> /// Apply the cached scatter prototypes and instances to the given TerrainData. /// </summary> public static void ApplyScatterTrees(TerrainData terrainData, HEU_VolumeScatterTrees scatterTrees) { #if UNITY_2019_1_OR_NEWER if (scatterTrees == null || scatterTrees._treePrototypInfos == null || scatterTrees._treePrototypInfos.Count == 0) { return; } // Load and set TreePrototypes GameObject prefabGO; List<TreePrototype> treePrototypes = new List<TreePrototype>(); for (int i = 0; i < scatterTrees._treePrototypInfos.Count; ++i) { prefabGO = HEU_AssetDatabase.LoadAssetAtPath(scatterTrees._treePrototypInfos[i]._prefabPath, typeof(GameObject)) as GameObject; if (prefabGO != null) { TreePrototype prototype = new TreePrototype(); prototype.prefab = prefabGO; prototype.bendFactor = scatterTrees._treePrototypInfos[i]._bendfactor; treePrototypes.Add(prototype); //Debug.LogFormat("Added Tree Prototype: {0} - {1}", scatterTrees._treePrototypInfos[i]._prefabPath, scatterTrees._treePrototypInfos[i]._bendfactor); } } terrainData.treePrototypes = treePrototypes.ToArray(); terrainData.RefreshPrototypes(); if (scatterTrees._positions != null && scatterTrees._positions.Length > 0 && scatterTrees._prototypeIndices != null && scatterTrees._prototypeIndices.Length == scatterTrees._positions.Length) { TreeInstance[] treeInstances = new TreeInstance[scatterTrees._positions.Length]; for (int i = 0; i < scatterTrees._positions.Length; ++i) { treeInstances[i] = new TreeInstance(); treeInstances[i].color = scatterTrees._colors != null ? scatterTrees._colors[i] : new Color32(255, 255, 255, 255); treeInstances[i].heightScale = scatterTrees._heightScales != null ? scatterTrees._heightScales[i] : 1f; treeInstances[i].lightmapColor = scatterTrees._lightmapColors != null ? scatterTrees._lightmapColors[i] : new Color32(255, 255, 255, 255); treeInstances[i].position = scatterTrees._positions[i]; treeInstances[i].prototypeIndex = scatterTrees._prototypeIndices[i]; treeInstances[i].rotation = scatterTrees._rotations[i]; treeInstances[i].widthScale = scatterTrees._widthScales != null ? scatterTrees._widthScales[i] : 1f; } terrainData.SetTreeInstances(treeInstances, true); } #endif }
public static Material LoadUnityMaterial(string materialPath) { if(materialPath.StartsWith(HEU_Defines.DEFAULT_UNITY_BUILTIN_RESOURCES)) { return HEU_AssetDatabase.LoadUnityAssetFromUniqueAssetPath<Material>(materialPath); } else if (materialPath.StartsWith("Packages/")) { return HEU_AssetDatabase.LoadAssetAtPath(materialPath, typeof(Material)) as Material; } string relativePath = materialPath; if (relativePath.StartsWith(Application.dataPath)) { // If absolute path, change to relative path relativePath = HEU_AssetDatabase.GetAssetRelativePath(materialPath); } Material material = null; string mainPath = null; string subPath = null; HEU_AssetDatabase.GetSubAssetPathFromPath(relativePath, out mainPath, out subPath); if(subPath != null) { // This is a subasset. Need to find it by first loading the main asset. Object subObject = HEU_AssetDatabase.LoadSubAssetAtPath(mainPath, subPath); if(subObject != null) { material = subObject as Material; } } else { // Try loading from Resources first material = Resources.Load<Material>(relativePath) as Material; if (material == null) { // If not in Resources, try loading from project HEU_AssetDatabase.ImportAsset(relativePath, HEU_AssetDatabase.HEU_ImportAssetOptions.Default); material = HEU_AssetDatabase.LoadAssetAtPath(relativePath, typeof(Material)) as Material; } } return material; }
public static Texture2D ExtractHoudiniImageToTextureFile(HEU_SessionBase session, HAPI_MaterialInfo materialInfo, string imagePlanes, string assetCacheFolderPath) { Texture2D textureResult = null; // Get the Textures folder in the assetCacheFolderPath. Make sure it exists. assetCacheFolderPath = HEU_AssetDatabase.AppendTexturesPathToAssetFolder(assetCacheFolderPath); HEU_AssetDatabase.CreatePathWithFolders(assetCacheFolderPath); // Need to pass in full path to Houdini to write out the file assetCacheFolderPath = HEU_AssetDatabase.GetAssetFullPath(assetCacheFolderPath); if (assetCacheFolderPath == null) { return textureResult; } HAPI_ImageInfo imageInfo = new HAPI_ImageInfo(); if (!session.GetImageInfo(materialInfo.nodeId, ref imageInfo)) { return textureResult; } // This will return null if the current imageInfo file format is supported by Unity, otherwise // returns a Unity supported file format. string desiredFileFormatName = HEU_MaterialData.GetSupportedFileFormat(session, ref imageInfo); // Extract image to file string writtenFilePath = null; if (!session.ExtractImageToFile(materialInfo.nodeId, desiredFileFormatName, imagePlanes, assetCacheFolderPath, out writtenFilePath)) { return textureResult; } HEU_AssetDatabase.SaveAndRefreshDatabase(); // Convert full path back to relative in order to work with AssetDatabase string assetRelativePath = HEU_AssetDatabase.GetAssetRelativePath(writtenFilePath); // Re-import to refresh the project HEU_AssetDatabase.ImportAsset(assetRelativePath, HEU_AssetDatabase.HEU_ImportAssetOptions.Default); textureResult = HEU_AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(Texture2D)) as Texture2D; //Debug.LogFormat("Loaded texture to file {0} with format {1}", writtenFilePath, textureResult != null ? textureResult.format.ToString() : "none"); return textureResult; }
public static Material LoadUnityMaterial(string materialPath) { if(materialPath.StartsWith(HEU_Defines.DEFAULT_UNITY_BUILTIN_RESOURCES)) { return HEU_AssetDatabase.LoadUnityAssetFromUniqueAssetPath<Material>(materialPath); } string relativePath = materialPath; if (relativePath.StartsWith(Application.dataPath)) { // If absolute path, change to relative path relativePath = HEU_AssetDatabase.GetAssetRelativePath(materialPath); } // Try loading from Resources first Material material = Resources.Load<Material>(relativePath) as Material; if(material == null) { // If not in Resources, try loading from project HEU_AssetDatabase.ImportAsset(relativePath, HEU_AssetDatabase.HEU_ImportAssetOptions.Default); material = HEU_AssetDatabase.LoadAssetAtPath(relativePath, typeof(Material)) as Material; } return material; }
public void LoadPreset(HEU_SessionBase session, HEU_InputPreset inputPreset) { ResetInputNode(session); ChangeInputType(session, inputPreset._inputObjectType); if (inputPreset._inputObjectType == InputObjectType.UNITY_MESH) { bool bSet = false; int numObjects = inputPreset._inputObjectPresets.Count; for (int i = 0; i < numObjects; ++i) { bSet = false; if (!string.IsNullOrEmpty(inputPreset._inputObjectPresets[i]._gameObjectName)) { GameObject inputGO = null; if (inputPreset._inputObjectPresets[i]._isSceneObject) { inputGO = HEU_GeneralUtility.GetGameObjectByNameInScene(inputPreset._inputObjectPresets[i]._gameObjectName); } else { // Use the _gameObjectName as path to find in scene inputGO = HEU_AssetDatabase.LoadAssetAtPath(inputPreset._inputObjectPresets[i]._gameObjectName, typeof(GameObject)) as GameObject; if (inputGO == null) { Debug.LogErrorFormat("Unable to find input at {0}", inputPreset._inputObjectPresets[i]._gameObjectName); } } if (inputGO != null) { HEU_InputObjectInfo inputObject = InternalAddInputObjectAtEnd(inputGO); bSet = true; inputObject._useTransformOffset = inputPreset._inputObjectPresets[i]._useTransformOffset; inputObject._translateOffset = inputPreset._inputObjectPresets[i]._translateOffset; inputObject._rotateOffset = inputPreset._inputObjectPresets[i]._rotateOffset; inputObject._scaleOffset = inputPreset._inputObjectPresets[i]._scaleOffset; } else { Debug.LogWarningFormat("Gameobject with name {0} not found. Unable to set input object.", inputPreset._inputAssetName); } } if (!bSet) { // Add dummy spot (user can replace it manually) InternalAddInputObjectAtEnd(null); } } } else if (inputPreset._inputObjectType == HEU_InputNode.InputObjectType.HDA) { bool bSet = false; int numInptus = inputPreset._inputAssetPresets.Count; for (int i = 0; i < numInptus; ++i) { bSet = false; if (!string.IsNullOrEmpty(inputPreset._inputAssetPresets[i]._gameObjectName)) { bSet = FindAddToInputHDA(inputPreset._inputAssetPresets[i]._gameObjectName); } if (!bSet) { // Couldn't add for some reason, so just add dummy spot (user can replace it manually) InternalAddInputHDAAtEnd(null); } } if (numInptus == 0 && !string.IsNullOrEmpty(inputPreset._inputAssetName)) { // Old preset. Add it to input FindAddToInputHDA(inputPreset._inputAssetName); } } KeepWorldTransform = inputPreset._keepWorldTransform; PackGeometryBeforeMerging = inputPreset._packGeometryBeforeMerging; RequiresUpload = true; ClearUICache(); }
public void GenerateTerrainWithAlphamaps(HEU_SessionBase session, HEU_HoudiniAsset houdiniAsset, bool bRebuild) { if(_layers == null || _layers.Count == 0) { Debug.LogError("Unable to generate terrain due to lack of heightfield layers!"); return; } HEU_VolumeLayer heightLayer = _layers[0]; HAPI_VolumeInfo heightVolumeInfo = new HAPI_VolumeInfo(); bool bResult = session.GetVolumeInfo(_ownerNode.GeoID, heightLayer._part.PartID, ref heightVolumeInfo); if (!bResult) { Debug.LogErrorFormat("Unable to get volume info for height layer: {0}!", heightLayer._layerName); return; } // Special handling of volume cache presets. It is applied here (if exists) because it might pertain to TerrainData that exists // in the AssetDatabase. If we don't apply here but rather create a new one, the existing file will get overwritten. // Applying the preset here for terrain ensures the TerrainData is reused. // Get the volume preset for this part HEU_VolumeCachePreset volumeCachePreset = houdiniAsset.GetVolumeCachePreset(_ownerNode.ObjectNode.ObjectName, _ownerNode.GeoName, TileIndex); if (volumeCachePreset != null) { ApplyPreset(volumeCachePreset); // Remove it so that it doesn't get applied when doing the recook step houdiniAsset.RemoveVolumeCachePreset(volumeCachePreset); } // The TerrainData and TerrainLayer files needs to be saved out if we create them. This creates the relative folder // path from the Asset's cache folder: {assetCache}/{geo name}/Terrain/Tile{tileIndex}/... string relativeFolderPath = HEU_Platform.BuildPath(_ownerNode.GeoName, HEU_Defines.HEU_FOLDER_TERRAIN, HEU_Defines.HEU_FOLDER_TILE + TileIndex); if (bRebuild) { // For full rebuild, re-create the TerrainData instead of using previous _terrainData = null; } //Debug.Log("Generating Terrain with AlphaMaps: " + (_terrainData != null ? _terrainData.name : "NONE")); TerrainData terrainData = _terrainData; Vector3 terrainOffsetPosition = Vector3.zero; // Look up TerrainData file via attribute if user has set it string terrainDataFile = HEU_GeneralUtility.GetAttributeStringValueSingle(session, _ownerNode.GeoID, heightLayer._part.PartID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TERRAINDATA_FILE_ATTR, HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM); if (!string.IsNullOrEmpty(terrainDataFile)) { TerrainData loadedTerrainData = HEU_AssetDatabase.LoadAssetAtPath(terrainDataFile, typeof(TerrainData)) as TerrainData; if (loadedTerrainData == null) { Debug.LogWarningFormat("TerrainData, set via attribute, not found at: {0}", terrainDataFile); } else { // In the case that the specified TerrainData belongs to another Terrain (i.e. input Terrain), // make a copy of it and store it in our cache. Note that this overwrites existing TerrainData in our cache // because the workflow is such that attributes will always override local setting. string bakedTerrainPath = houdiniAsset.GetValidAssetCacheFolderPath(); bakedTerrainPath = HEU_Platform.BuildPath(bakedTerrainPath, relativeFolderPath); terrainData = HEU_AssetDatabase.CopyAndLoadAssetAtAnyPath(loadedTerrainData, bakedTerrainPath, typeof(TerrainData), true) as TerrainData; if (terrainData == null) { Debug.LogErrorFormat("Unable to copy TerrainData from {0} for generating Terrain.", terrainDataFile); } } } // Generate the terrain and terrain data from the height layer. This applies height values. bResult = HEU_TerrainUtility.GenerateTerrainFromVolume(session, ref heightVolumeInfo, heightLayer._part.ParentGeoNode.GeoID, heightLayer._part.PartID, heightLayer._part.OutputGameObject, ref terrainData, out terrainOffsetPosition); if (!bResult || terrainData == null) { return; } if (_terrainData != terrainData) { _terrainData = terrainData; heightLayer._part.SetTerrainData(terrainData, relativeFolderPath); } heightLayer._part.SetTerrainOffsetPosition(terrainOffsetPosition); int terrainSize = terrainData.heightmapResolution; // Now process TerrainLayers and alpha maps // First, preprocess all layers to get heightfield arrays, converted to proper size List<float[]> heightFields = new List<float[]>(); // Corresponding list of HF volume layers to process as splatmaps List<HEU_VolumeLayer> volumeLayersToProcess = new List<HEU_VolumeLayer>(); int numLayers = _layers.Count; float minHeight = 0; float maxHeight = 0; float heightRange = 0; // This skips the height layer, and processes all other layers. // Note that mask shouldn't be part of _layers at this point. for(int i = 1; i < numLayers; ++i) { float[] normalizedHF = HEU_TerrainUtility.GetNormalizedHeightmapFromPartWithMinMax(session, _ownerNode.GeoID, _layers[i]._part.PartID, _layers[i]._xLength, _layers[i]._yLength, ref minHeight, ref maxHeight, ref heightRange); if (normalizedHF != null && normalizedHF.Length > 0) { heightFields.Add(normalizedHF); volumeLayersToProcess.Add(_layers[i]); } } int numVolumeLayers = volumeLayersToProcess.Count; HAPI_NodeId geoID; HAPI_PartId partID; Texture2D defaultTexture = LoadDefaultSplatTexture(); #if UNITY_2018_3_OR_NEWER // Create or update the terrain layers based on heightfield layers. // Keep existing TerrainLayers, and either update or append to them TerrainLayer[] existingTerrainLayers = terrainData.terrainLayers; // Total layers are existing layers + new alpha maps List<TerrainLayer> finalTerrainLayers = new List<TerrainLayer>(existingTerrainLayers); // This holds the alpha map indices for each layer that will be added to the TerrainData. // The alpha maps could be a mix of existing and new values, so need to know which to use // Initially set to use existing alpha maps, then override later on if specified via HF layers List<int> alphaMapIndices = new List<int>(); for (int a = 0; a < existingTerrainLayers.Length; ++a) { // Negative indices for existing alpha map (offset by -1) alphaMapIndices.Add(-a - 1); } bool bNewTerrainLayer = false; HEU_VolumeLayer layer = null; TerrainLayer terrainLayer = null; bool bSetTerrainLayerProperties = true; for (int m = 0; m < numVolumeLayers; ++m) { bNewTerrainLayer = false; bSetTerrainLayerProperties = true; layer = volumeLayersToProcess[m]; geoID = _ownerNode.GeoID; partID = layer._part.PartID; terrainLayer = null; int terrainLayerIndex = -1; // The TerrainLayer attribute overrides existing TerrainLayer. So if its set, load and use it. string terrainLayerFile = HEU_GeneralUtility.GetAttributeStringValueSingle(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TERRAINLAYER_FILE_ATTR, HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM); if (!string.IsNullOrEmpty(terrainLayerFile)) { terrainLayer = HEU_AssetDatabase.LoadAssetAtPath(terrainLayerFile, typeof(TerrainLayer)) as TerrainLayer; if (terrainLayer == null) { Debug.LogWarningFormat("TerrainLayer, set via attribute, not found at: {0}", terrainLayerFile); // Not earlying out or skipping this layer due to error because we want to keep proper indexing // by creating a new TerrainLayer. } else { // TerrainLayer loaded from attribute. // It could be an existing TerrainLayer that is already part of finalTerrainLayers // or could be a new one which needs to be added. // If its a different TerrainLayer than existing, update the finalTerrainLayers, and index. if (layer._terrainLayer != null && layer._terrainLayer != terrainLayer) { terrainLayerIndex = HEU_TerrainUtility.GetTerrainLayerIndex(layer._terrainLayer, existingTerrainLayers); if (terrainLayerIndex >= 0) { finalTerrainLayers[terrainLayerIndex] = terrainLayer; } } if (terrainLayerIndex == -1) { // Always check if its part of existing list so as not to add it again terrainLayerIndex = HEU_TerrainUtility.GetTerrainLayerIndex(terrainLayer, existingTerrainLayers); } } } // No terrain layer specified, so try using existing if we have it if (terrainLayer == null) { terrainLayerIndex = HEU_TerrainUtility.GetTerrainLayerIndex(layer._terrainLayer, existingTerrainLayers); if (terrainLayerIndex >= 0) { // Note the terrainLayerIndex is same for finalTerrainLayers as existingTerrainLayers terrainLayer = existingTerrainLayers[terrainLayerIndex]; } } // Still not found, so just create a new one if (terrainLayer == null) { terrainLayer = new TerrainLayer(); terrainLayer.name = layer._layerName; //Debug.LogFormat("Created new TerrainLayer with name: {0} ", terrainLayer.name); bNewTerrainLayer = true; } if (terrainLayerIndex == -1) { // Adding to the finalTerrainLayers if this is indeed a newly created or loaded TerrainLayer // (i.e. isn't already part of the TerrainLayers for this Terrain). // Save this layer's index for later on if we make a copy. terrainLayerIndex = finalTerrainLayers.Count; finalTerrainLayers.Add(terrainLayer); // Positive index for alpha map from heightfield (starting at 1) alphaMapIndices.Add(m + 1); } else { // Positive index for alpha map from heightfield (starting at 1) alphaMapIndices[terrainLayerIndex] = m + 1; } // For existing TerrainLayer, make a copy of it if it has custom layer attributes // because we don't want to change the original TerrainLayer. if (!bNewTerrainLayer && layer._hasLayerAttributes) { string bakedTerrainPath = houdiniAsset.GetValidAssetCacheFolderPath(); bakedTerrainPath = HEU_Platform.BuildPath(bakedTerrainPath, relativeFolderPath); TerrainLayer prevTerrainLayer = terrainLayer; terrainLayer = HEU_AssetDatabase.CopyAndLoadAssetAtAnyPath(terrainLayer, bakedTerrainPath, typeof(TerrainLayer), true) as TerrainLayer; if (terrainLayer != null) { // Update the TerrainLayer reference in the list with this copy finalTerrainLayers[terrainLayerIndex] = terrainLayer; } else { Debug.LogErrorFormat("Unable to copy TerrainLayer '{0}' for generating Terrain. " + "Using original TerrainLayer. Will not be able to set any TerrainLayer properties.", layer._layerName); terrainLayer = prevTerrainLayer; bSetTerrainLayerProperties = false; // Again, continuing on to keep proper indexing. } } // Now override layer properties if they have been set via attributes if (bSetTerrainLayerProperties) { LoadLayerPropertiesFromAttributes(session, geoID, partID, terrainLayer, bNewTerrainLayer, defaultTexture); } if (bNewTerrainLayer) { // In order to retain the new TerrainLayer, it must be saved to the AssetDatabase. Object savedObject = null; string layerFileNameWithExt = terrainLayer.name; if (!layerFileNameWithExt.EndsWith(HEU_Defines.HEU_EXT_TERRAINLAYER)) { layerFileNameWithExt += HEU_Defines.HEU_EXT_TERRAINLAYER; } houdiniAsset.AddToAssetDBCache(layerFileNameWithExt, terrainLayer, relativeFolderPath, ref savedObject); } // Store reference layer._terrainLayer = terrainLayer; } // Get existing alpha maps so we can reuse the values if needed float[,,] existingAlphaMaps = terrainData.GetAlphamaps(0, 0, terrainData.alphamapWidth, terrainData.alphamapHeight); terrainData.terrainLayers = finalTerrainLayers.ToArray(); int numTotalAlphaMaps = finalTerrainLayers.Count; #else // Create or update the SplatPrototype based on heightfield layers. // Need to create or reuse SplatPrototype for each layer in heightfield, representing the textures. SplatPrototype[] existingSplats = terrainData.splatPrototypes; // A full rebuild clears out existing splats, but a regular cook keeps them. List<SplatPrototype> finalSplats = new List<SplatPrototype>(existingSplats); // This holds the alpha map indices for each layer that will be added to the TerrainData // The alpha maps could be a mix of existing and new values, so need to know which to use List<int> alphaMapIndices = new List<int>(); // Initially set to use existing alpha maps, then override later on if specified via HF layers. for (int a = 0; a < existingSplats.Length; ++a) { // Negative indices for existing alpha map (offset by -1) alphaMapIndices.Add(-a - 1); } bool bNewSplat = false; HEU_VolumeLayer layer = null; SplatPrototype splatPrototype = null; for (int m = 0; m < numVolumeLayers; ++m) { bNewSplat = false; layer = volumeLayersToProcess[m]; geoID = _ownerNode.GeoID; partID = layer._part.PartID; // Try to find existing SplatPrototype for reuse. But not for full rebuild. splatPrototype = null; if (layer._splatPrototypeIndex >= 0 && layer._splatPrototypeIndex < existingSplats.Length) { splatPrototype = existingSplats[layer._splatPrototypeIndex]; // Positive index for alpha map from heightfield (starting at 1) alphaMapIndices[layer._splatPrototypeIndex] = m + 1; } if (splatPrototype == null) { splatPrototype = new SplatPrototype(); layer._splatPrototypeIndex = finalSplats.Count; finalSplats.Add(splatPrototype); // Positive index for alpha map from heightfield (starting at 1) alphaMapIndices.Add(m + 1); } // Now override splat properties if they have been set via attributes LoadLayerPropertiesFromAttributes(session, geoID, partID, splatPrototype, bNewSplat, defaultTexture); } // On regular cook, get existing alpha maps so we can reuse the values if needed. float[,,] existingAlphaMaps = terrainData.GetAlphamaps(0, 0, terrainData.alphamapWidth, terrainData.alphamapHeight); terrainData.splatPrototypes = finalSplats.ToArray(); int numTotalAlphaMaps = finalSplats.Count; #endif // Set alpha maps by combining with existing alpha maps, and appending new heightfields float[,,] alphamap = null; if (numTotalAlphaMaps > 0 && volumeLayersToProcess.Count > 0) { // Convert the heightfields into alpha maps with layer strengths float[] strengths = new float[volumeLayersToProcess.Count]; for (int m = 0; m < volumeLayersToProcess.Count; ++m) { strengths[m] = volumeLayersToProcess[m]._strength; } alphamap = HEU_TerrainUtility.AppendConvertedHeightFieldToAlphaMap( volumeLayersToProcess[0]._xLength, volumeLayersToProcess[0]._yLength, existingAlphaMaps, heightFields, strengths, alphaMapIndices); // Update the alphamap resolution to the actual size of the first // heightfield layer used for the alphamaps. // Setting the size before setting the alphamas applies proper scaling. int alphamapResolution = volumeLayersToProcess[0]._xLength; terrainData.alphamapResolution = alphamapResolution; terrainData.SetAlphamaps(0, 0, alphamap); } // Tree instances for scattering HEU_TerrainUtility.ApplyScatter(terrainData, _scatterTrees); // If the layers were writen out, this saves the asset DB. Otherwise user has to save it themselves. // Not 100% sure this is needed, but without this the editor doesn't know the terrain asset has been updated // and therefore doesn't import and show the terrain layer. HEU_AssetDatabase.SaveAssetDatabase(); }
private void GenerateInstancesFromAssetPaths(HEU_LoadBufferInstancer instancerBuffer, Transform instanceRootTransform) { // For single asset, this is set when its impoted GameObject singleAssetGO = null; // For multi assets, keep track of loaded objects so we only need to load once for each object Dictionary<string, GameObject> loadedUnityObjectMap = new Dictionary<string, GameObject>(); // Temporary empty gameobject in case the specified Unity asset is not found GameObject tempGO = null; if (instancerBuffer._assetPaths.Length == 1) { // Single asset path if (!string.IsNullOrEmpty(instancerBuffer._assetPaths[0])) { HEU_AssetDatabase.ImportAsset(instancerBuffer._assetPaths[0], HEU_AssetDatabase.HEU_ImportAssetOptions.Default); singleAssetGO = HEU_AssetDatabase.LoadAssetAtPath(instancerBuffer._assetPaths[0], typeof(GameObject)) as GameObject; } if (singleAssetGO == null) { Debug.LogErrorFormat("Asset at path {0} not found. Unable to create instances for {1}.", instancerBuffer._assetPaths[0], instancerBuffer._name); return; } } int numInstancesCreated = 0; int numInstances = instancerBuffer._instanceTransforms.Length; for (int i = 0; i < numInstances; ++i) { // Reset to the single asset for each instance allows which is null if using multi asset // therefore forcing the instance asset to be found GameObject unitySrcGO = singleAssetGO; if (unitySrcGO == null) { // If not using single asset, then there must be an asset path for each instance if (string.IsNullOrEmpty(instancerBuffer._assetPaths[i])) { continue; } if (!loadedUnityObjectMap.TryGetValue(instancerBuffer._assetPaths[i], out unitySrcGO)) { // Try loading it //HEU_AssetDatabase.ImportAsset(instancerBuffer._assetPaths[i], HEU_AssetDatabase.HEU_ImportAssetOptions.Default); unitySrcGO = HEU_AssetDatabase.LoadAssetAtPath(instancerBuffer._assetPaths[i], typeof(GameObject)) as GameObject; if (unitySrcGO == null) { Debug.LogErrorFormat("Unable to load asset at {0} for instancing!", instancerBuffer._assetPaths[i]); // Even though the source Unity object is not found, we should create an object instance info to track it if (tempGO == null) { tempGO = new GameObject(); } unitySrcGO = tempGO; } // Adding to map even if not found so we don't flood the log with the same error message loadedUnityObjectMap.Add(instancerBuffer._assetPaths[i], unitySrcGO); } } CreateNewInstanceFromObject(unitySrcGO, (numInstancesCreated + 1), instanceRootTransform, ref instancerBuffer._instanceTransforms[i], instancerBuffer._instancePrefixes, instancerBuffer._name); numInstancesCreated++; } if (tempGO != null) { HEU_GeneralUtility.DestroyImmediate(tempGO, bRegisterUndo: false); } }
private void GenerateTerrain(List<HEU_LoadBufferVolume> terrainBuffers) { Transform parent = this.gameObject.transform; // Directory to store generated terrain files. string outputTerrainpath = GetOutputCacheDirectory(); outputTerrainpath = HEU_Platform.BuildPath(outputTerrainpath, "Terrain"); int numVolumes = terrainBuffers.Count; for(int t = 0; t < numVolumes; ++t) { if (terrainBuffers[t]._heightMap != null) { GameObject newGameObject = new GameObject("heightfield_" + terrainBuffers[t]._tileIndex); Transform newTransform = newGameObject.transform; newTransform.parent = parent; HEU_GeneratedOutput generatedOutput = new HEU_GeneratedOutput(); generatedOutput._outputData._gameObject = newGameObject; Terrain terrain = HEU_GeneralUtility.GetOrCreateComponent<Terrain>(newGameObject); TerrainCollider collider = HEU_GeneralUtility.GetOrCreateComponent<TerrainCollider>(newGameObject); if (!string.IsNullOrEmpty(terrainBuffers[t]._terrainDataPath)) { // Load the source TerrainData, then make a unique copy of it in the cache folder TerrainData sourceTerrainData = HEU_AssetDatabase.LoadAssetAtPath(terrainBuffers[t]._terrainDataPath, typeof(TerrainData)) as TerrainData; if (sourceTerrainData == null) { Debug.LogWarningFormat("TerrainData, set via attribute, not found at: {0}", terrainBuffers[t]._terrainDataPath); } terrain.terrainData = HEU_AssetDatabase.CopyUniqueAndLoadAssetAtAnyPath(sourceTerrainData, outputTerrainpath, typeof(TerrainData)) as TerrainData; if (terrain.terrainData != null) { // Store path so that it can be deleted on clean up AddGeneratedOutputFilePath(HEU_AssetDatabase.GetAssetPath(terrain.terrainData)); } } if (terrain.terrainData == null) { terrain.terrainData = new TerrainData(); } TerrainData terrainData = terrain.terrainData; collider.terrainData = terrainData; HEU_TerrainUtility.SetTerrainMaterial(terrain, terrainBuffers[t]._specifiedTerrainMaterialName); #if UNITY_2018_3_OR_NEWER terrain.allowAutoConnect = true; // This has to be set after setting material terrain.drawInstanced = true; #endif int heightMapSize = terrainBuffers[t]._heightMapWidth; terrainData.heightmapResolution = heightMapSize; if (terrainData.heightmapResolution != heightMapSize) { Debug.LogErrorFormat("Unsupported terrain size: {0}", heightMapSize); continue; } // The terrainData.baseMapResolution is not set here, but rather left to whatever default Unity uses // The terrainData.alphamapResolution is set later when setting the alphamaps. // 32 is the default for resolutionPerPatch const int detailResolution = 1024; const int resolutionPerPatch = 32; terrainData.SetDetailResolution(detailResolution, resolutionPerPatch); terrainData.SetHeights(0, 0, terrainBuffers[t]._heightMap); // Note that Unity uses a default height range of 600 when a flat terrain is created. // Without a non-zero value for the height range, user isn't able to draw heights. // Therefore, set 600 as the value if height range is currently 0 (due to flat heightfield). float heightRange = terrainBuffers[t]._heightRange; if (heightRange == 0) { heightRange = 600; } terrainData.size = new Vector3(terrainBuffers[t]._terrainSizeX, heightRange, terrainBuffers[t]._terrainSizeY); terrain.Flush(); // Set position HAPI_Transform hapiTransformVolume = new HAPI_Transform(true); hapiTransformVolume.position[0] += terrainBuffers[t]._position[0]; hapiTransformVolume.position[1] += terrainBuffers[t]._position[1]; hapiTransformVolume.position[2] += terrainBuffers[t]._position[2]; HEU_HAPIUtility.ApplyLocalTransfromFromHoudiniToUnity(ref hapiTransformVolume, newTransform); // Set layers Texture2D defaultTexture = HEU_VolumeCache.LoadDefaultSplatTexture(); int numLayers = terrainBuffers[t]._splatLayers.Count; #if UNITY_2018_3_OR_NEWER // Create TerrainLayer for each heightfield layer. // Note that height and mask layers are ignored (i.e. not created as TerrainLayers). // Since height layer is first, only process layers from 2nd index onwards. if (numLayers > 1) { // Keep existing TerrainLayers, and either update or append to them TerrainLayer[] existingTerrainLayers = terrainData.terrainLayers; // Total layers are existing layers + new alpha maps List<TerrainLayer> finalTerrainLayers = new List<TerrainLayer>(existingTerrainLayers); for (int m = 1; m < numLayers; ++m) { TerrainLayer terrainlayer = null; int terrainLayerIndex = -1; bool bSetTerrainLayerProperties = true; HEU_LoadBufferVolumeLayer layer = terrainBuffers[t]._splatLayers[m]; // Look up TerrainLayer file via attribute if user has set it if (!string.IsNullOrEmpty(layer._layerPath)) { terrainlayer = HEU_AssetDatabase.LoadAssetAtPath(layer._layerPath, typeof(TerrainLayer)) as TerrainLayer; if (terrainlayer == null) { Debug.LogWarningFormat("TerrainLayer, set via attribute, not found at: {0}", layer._layerPath); continue; } else { // Always check if its part of existing list so as not to add it again terrainLayerIndex = HEU_TerrainUtility.GetTerrainLayerIndex(terrainlayer, existingTerrainLayers); } } if (terrainlayer == null) { terrainlayer = new TerrainLayer(); terrainLayerIndex = finalTerrainLayers.Count; finalTerrainLayers.Add(terrainlayer); } else { // For existing TerrainLayer, make a copy of it if it has custom layer attributes // because we don't want to change the original TerrainLayer. if (layer._hasLayerAttributes && terrainLayerIndex >= 0) { // Copy the TerrainLayer file TerrainLayer prevTerrainLayer = terrainlayer; terrainlayer = HEU_AssetDatabase.CopyAndLoadAssetAtAnyPath(terrainlayer, outputTerrainpath, typeof(TerrainLayer), true) as TerrainLayer; if (terrainlayer != null) { // Update the TerrainLayer reference in the list with this copy finalTerrainLayers[terrainLayerIndex] = terrainlayer; // Store path for clean up later AddGeneratedOutputFilePath(HEU_AssetDatabase.GetAssetPath(terrainlayer)); } else { Debug.LogErrorFormat("Unable to copy TerrainLayer '{0}' for generating Terrain. " + "Using original TerrainLayer. Will not be able to set any TerrainLayer properties.", layer._layerName); terrainlayer = prevTerrainLayer; bSetTerrainLayerProperties = false; // Again, continuing on to keep proper indexing. } } } if (bSetTerrainLayerProperties) { if (!string.IsNullOrEmpty(layer._diffuseTexturePath)) { terrainlayer.diffuseTexture = HEU_MaterialFactory.LoadTexture(layer._diffuseTexturePath); } if (terrainlayer.diffuseTexture == null) { terrainlayer.diffuseTexture = defaultTexture; } terrainlayer.diffuseRemapMin = Vector4.zero; terrainlayer.diffuseRemapMax = Vector4.one; if (!string.IsNullOrEmpty(layer._maskTexturePath)) { terrainlayer.maskMapTexture = HEU_MaterialFactory.LoadTexture(layer._maskTexturePath); } terrainlayer.maskMapRemapMin = Vector4.zero; terrainlayer.maskMapRemapMax = Vector4.one; terrainlayer.metallic = layer._metallic; if (!string.IsNullOrEmpty(layer._normalTexturePath)) { terrainlayer.normalMapTexture = HEU_MaterialFactory.LoadTexture(layer._normalTexturePath); } terrainlayer.normalScale = layer._normalScale; terrainlayer.smoothness = layer._smoothness; terrainlayer.specular = layer._specularColor; terrainlayer.tileOffset = layer._tileOffset; if (layer._tileSize.magnitude == 0f && terrainlayer.diffuseTexture != null) { // Use texture size if tile size is 0 layer._tileSize = new Vector2(terrainlayer.diffuseTexture.width, terrainlayer.diffuseTexture.height); } terrainlayer.tileSize = layer._tileSize; } } terrainData.terrainLayers = finalTerrainLayers.ToArray(); } #else // Need to create SplatPrototype for each layer in heightfield, representing the textures. SplatPrototype[] splatPrototypes = new SplatPrototype[numLayers]; for (int m = 0; m < numLayers; ++m) { splatPrototypes[m] = new SplatPrototype(); HEU_LoadBufferVolumeLayer layer = terrainBuffers[t]._splatLayers[m]; Texture2D diffuseTexture = null; if (!string.IsNullOrEmpty(layer._diffuseTexturePath)) { diffuseTexture = HEU_MaterialFactory.LoadTexture(layer._diffuseTexturePath); } if (diffuseTexture == null) { diffuseTexture = defaultTexture; } splatPrototypes[m].texture = diffuseTexture; splatPrototypes[m].tileOffset = layer._tileOffset; if (layer._tileSize.magnitude == 0f && diffuseTexture != null) { // Use texture size if tile size is 0 layer._tileSize = new Vector2(diffuseTexture.width, diffuseTexture.height); } splatPrototypes[m].tileSize = layer._tileSize; splatPrototypes[m].metallic = layer._metallic; splatPrototypes[m].smoothness = layer._smoothness; if (!string.IsNullOrEmpty(layer._normalTexturePath)) { splatPrototypes[m].normalMap = HEU_MaterialFactory.LoadTexture(layer._normalTexturePath); } } terrainData.splatPrototypes = splatPrototypes; #endif // Set the splatmaps if (terrainBuffers[t]._splatMaps != null) { // Set the alphamap size before setting the alphamaps to get correct scaling // The alphamap size comes from the first alphamap layer int alphamapResolution = terrainBuffers[t]._heightMapWidth; if (numLayers > 1) { alphamapResolution = terrainBuffers[t]._splatLayers[1]._heightMapWidth; } terrainData.alphamapResolution = alphamapResolution; terrainData.SetAlphamaps(0, 0, terrainBuffers[t]._splatMaps); } // Set the tree scattering if (terrainBuffers[t]._scatterTrees != null) { HEU_TerrainUtility.ApplyScatterTrees(terrainData, terrainBuffers[t]._scatterTrees); } // Set the detail layers if (terrainBuffers[t]._detailPrototypes != null) { HEU_TerrainUtility.ApplyDetailLayers(terrain, terrainData, terrainBuffers[t]._detailProperties, terrainBuffers[t]._detailPrototypes, terrainBuffers[t]._detailMaps); } terrainBuffers[t]._generatedOutput = generatedOutput; _generatedOutputs.Add(generatedOutput); SetOutputVisiblity(terrainBuffers[t]); } } }
private void GenerateTerrain(List<HEU_LoadBufferVolume> terrainBuffers) { Transform parent = this.gameObject.transform; int numVolumes = terrainBuffers.Count; for(int t = 0; t < numVolumes; ++t) { if (terrainBuffers[t]._heightMap != null) { GameObject newGameObject = new GameObject("heightfield_" + terrainBuffers[t]._tileIndex); Transform newTransform = newGameObject.transform; newTransform.parent = parent; HEU_GeneratedOutput generatedOutput = new HEU_GeneratedOutput(); generatedOutput._outputData._gameObject = newGameObject; Terrain terrain = HEU_GeneralUtility.GetOrCreateComponent<Terrain>(newGameObject); TerrainCollider collider = HEU_GeneralUtility.GetOrCreateComponent<TerrainCollider>(newGameObject); if (!string.IsNullOrEmpty(terrainBuffers[t]._terrainDataPath)) { terrain.terrainData = HEU_AssetDatabase.LoadAssetAtPath(terrainBuffers[t]._terrainDataPath, typeof(TerrainData)) as TerrainData; if (terrain.terrainData == null) { Debug.LogWarningFormat("TerrainData, set via attribute, not found at: {0}", terrainBuffers[t]._terrainDataPath); } } if (terrain.terrainData == null) { terrain.terrainData = new TerrainData(); } TerrainData terrainData = terrain.terrainData; collider.terrainData = terrainData; HEU_TerrainUtility.SetTerrainMaterial(terrain); #if UNITY_2018_3_OR_NEWER terrain.allowAutoConnect = true; // This has to be set after setting material terrain.drawInstanced = true; #endif int heightMapSize = terrainBuffers[t]._heightMapWidth; terrainData.heightmapResolution = heightMapSize; if (terrainData.heightmapResolution != heightMapSize) { Debug.LogErrorFormat("Unsupported terrain size: {0}", heightMapSize); continue; } // The terrainData.baseMapResolution is not set here, but rather left to whatever default Unity uses // The terrainData.alphamapResolution is set later when setting the alphamaps. // 32 is the default for resolutionPerPatch const int detailResolution = 1024; const int resolutionPerPatch = 32; terrainData.SetDetailResolution(detailResolution, resolutionPerPatch); terrainData.SetHeights(0, 0, terrainBuffers[t]._heightMap); // Note that Unity uses a default height range of 600 when a flat terrain is created. // Without a non-zero value for the height range, user isn't able to draw heights. // Therefore, set 600 as the value if height range is currently 0 (due to flat heightfield). float heightRange = terrainBuffers[t]._heightRange; if (heightRange == 0) { heightRange = 600; } terrainData.size = new Vector3(terrainBuffers[t]._terrainSizeX, heightRange, terrainBuffers[t]._terrainSizeY); terrain.Flush(); // Set position HAPI_Transform hapiTransformVolume = new HAPI_Transform(true); hapiTransformVolume.position[0] += terrainBuffers[t]._position[0]; hapiTransformVolume.position[1] += terrainBuffers[t]._position[1]; hapiTransformVolume.position[2] += terrainBuffers[t]._position[2]; HEU_HAPIUtility.ApplyLocalTransfromFromHoudiniToUnity(ref hapiTransformVolume, newTransform); // Set layers Texture2D defaultTexture = HEU_VolumeCache.LoadDefaultSplatTexture(); int numLayers = terrainBuffers[t]._layers.Count; #if UNITY_2018_3_OR_NEWER // Create TerrainLayer for each heightfield layer. // Note that height and mask layers are ignored (i.e. not created as TerrainLayers). // Since height layer is first, only process layers from 2nd index onwards. if (numLayers > 1) { TerrainLayer[] terrainLayers = new TerrainLayer[numLayers - 1]; for (int m = 1; m < numLayers; ++m) { TerrainLayer terrainlayer = null; HEU_LoadBufferVolumeLayer layer = terrainBuffers[t]._layers[m]; // Look up TerrainLayer file via attribute if user has set it if (!string.IsNullOrEmpty(layer._layerPath)) { terrainlayer = HEU_AssetDatabase.LoadAssetAtPath(layer._layerPath, typeof(TerrainLayer)) as TerrainLayer; if (terrainlayer == null) { Debug.LogWarningFormat("TerrainLayer, set via attribute, not found at: {0}", layer._layerPath); continue; } } if (terrainlayer == null) { terrainlayer = new TerrainLayer(); } if (!string.IsNullOrEmpty(layer._diffuseTexturePath)) { terrainlayer.diffuseTexture = HEU_MaterialFactory.LoadTexture(layer._diffuseTexturePath); } if (terrainlayer.diffuseTexture == null) { terrainlayer.diffuseTexture = defaultTexture; } terrainlayer.diffuseRemapMin = Vector4.zero; terrainlayer.diffuseRemapMax = Vector4.one; if (!string.IsNullOrEmpty(layer._maskTexturePath)) { terrainlayer.maskMapTexture = HEU_MaterialFactory.LoadTexture(layer._maskTexturePath); } terrainlayer.maskMapRemapMin = Vector4.zero; terrainlayer.maskMapRemapMax = Vector4.one; terrainlayer.metallic = layer._metallic; if (!string.IsNullOrEmpty(layer._normalTexturePath)) { terrainlayer.normalMapTexture = HEU_MaterialFactory.LoadTexture(layer._normalTexturePath); } terrainlayer.normalScale = layer._normalScale; terrainlayer.smoothness = layer._smoothness; terrainlayer.specular = layer._specularColor; terrainlayer.tileOffset = layer._tileOffset; if (layer._tileSize.magnitude == 0f && terrainlayer.diffuseTexture != null) { // Use texture size if tile size is 0 layer._tileSize = new Vector2(terrainlayer.diffuseTexture.width, terrainlayer.diffuseTexture.height); } terrainlayer.tileSize = layer._tileSize; // Note index is m - 1 due to skipping height layer terrainLayers[m - 1] = terrainlayer; } terrainData.terrainLayers = terrainLayers; } #else // Need to create SplatPrototype for each layer in heightfield, representing the textures. SplatPrototype[] splatPrototypes = new SplatPrototype[numLayers]; for (int m = 0; m < numLayers; ++m) { splatPrototypes[m] = new SplatPrototype(); HEU_LoadBufferVolumeLayer layer = terrainBuffers[t]._layers[m]; Texture2D diffuseTexture = null; if (!string.IsNullOrEmpty(layer._diffuseTexturePath)) { diffuseTexture = HEU_MaterialFactory.LoadTexture(layer._diffuseTexturePath); } if (diffuseTexture == null) { diffuseTexture = defaultTexture; } splatPrototypes[m].texture = diffuseTexture; splatPrototypes[m].tileOffset = layer._tileOffset; if (layer._tileSize.magnitude == 0f && diffuseTexture != null) { // Use texture size if tile size is 0 layer._tileSize = new Vector2(diffuseTexture.width, diffuseTexture.height); } splatPrototypes[m].tileSize = layer._tileSize; splatPrototypes[m].metallic = layer._metallic; splatPrototypes[m].smoothness = layer._smoothness; if (!string.IsNullOrEmpty(layer._normalTexturePath)) { splatPrototypes[m].normalMap = HEU_MaterialFactory.LoadTexture(layer._normalTexturePath); } } terrainData.splatPrototypes = splatPrototypes; #endif // Set the splatmaps if (terrainBuffers[t]._splatMaps != null) { // Set the alphamap size before setting the alphamaps to get correct scaling // The alphamap size comes from the first alphamap layer int alphamapResolution = terrainBuffers[t]._heightMapWidth; if (numLayers > 1) { alphamapResolution = terrainBuffers[t]._layers[1]._heightMapWidth; } terrainData.alphamapResolution = alphamapResolution; terrainData.SetAlphamaps(0, 0, terrainBuffers[t]._splatMaps); } // Set the tree scattering if (terrainBuffers[t]._scatterTrees != null) { HEU_TerrainUtility.ApplyScatter(terrainData, terrainBuffers[t]._scatterTrees); } terrainBuffers[t]._generatedOutput = generatedOutput; _generatedOutputs.Add(generatedOutput); SetOutputVisiblity(terrainBuffers[t]); } } }
/// <summary> /// Apply the given detail layers and properties to the given terrain. /// The detail distance and resolution will be set, along with detail prototypes, and layers. /// </summary> /// <param name="terrain">The Terrain to set the detail properies on</param> /// <param name="terrainData">The TerrainData to apply the layers to</param> /// <param name="detailProperties">Container for detail distance and resolution</param> /// <param name="heuDetailPrototypes">Data for creating DetailPrototypes</param> /// <param name="convertedDetailMaps">The detail maps to set for the detail layers</param> public static void ApplyDetailLayers(Terrain terrain, TerrainData terrainData, HEU_DetailProperties detailProperties, List<HEU_DetailPrototype> heuDetailPrototypes, List<int[,]> convertedDetailMaps) { #if UNITY_2018_3_OR_NEWER if (detailProperties != null) { if (detailProperties._detailDistance >= 0) { terrain.detailObjectDistance = detailProperties._detailDistance; } if (detailProperties._detailDensity >= 0) { terrain.detailObjectDensity = detailProperties._detailDensity; } int resPerPath = detailProperties._detailResolutionPerPatch > 0 ? detailProperties._detailResolutionPerPatch : terrainData.detailResolutionPerPatch; int detailRes = detailProperties._detailResolution > 0 ? detailProperties._detailResolution : terrainData.detailResolutionPerPatch; if (resPerPath > 0 && detailRes > 0) { // This should match with half the terrain size terrainData.SetDetailResolution(detailRes, resPerPath); } } if (heuDetailPrototypes.Count != convertedDetailMaps.Count) { Debug.LogError("Number of volume detail layers differs from converted detail maps. Unable to apply detail layers."); return; } // For now, just override existing detail prototypes and layers // If user asks for appending/overwriting them, then can use a new index attribute to map them List<DetailPrototype> detailPrototypes = new List<DetailPrototype>(); int numDetailLayers = heuDetailPrototypes.Count; for(int i = 0; i < numDetailLayers; ++i) { DetailPrototype detailPrototype = new DetailPrototype(); HEU_DetailPrototype heuDetail = heuDetailPrototypes[i]; if (!string.IsNullOrEmpty(heuDetail._prototypePrefab)) { detailPrototype.prototype = HEU_AssetDatabase.LoadAssetAtPath(heuDetail._prototypePrefab, typeof(GameObject)) as GameObject; detailPrototype.usePrototypeMesh = true; } else if (!string.IsNullOrEmpty(heuDetail._prototypeTexture)) { detailPrototype.prototypeTexture = HEU_MaterialFactory.LoadTexture(heuDetail._prototypeTexture); detailPrototype.usePrototypeMesh = false; } detailPrototype.bendFactor = heuDetail._bendFactor; detailPrototype.dryColor = heuDetail._dryColor; detailPrototype.healthyColor = heuDetail._healthyColor; detailPrototype.maxHeight = heuDetail._maxHeight; detailPrototype.maxWidth = heuDetail._maxWidth; detailPrototype.minHeight = heuDetail._minHeight; detailPrototype.minWidth = heuDetail._minWidth; detailPrototype.noiseSpread = heuDetail._noiseSpread; detailPrototype.renderMode = (DetailRenderMode)heuDetail._renderMode; detailPrototypes.Add(detailPrototype); } // Set the DetailPrototypes if (detailPrototypes.Count > 0) { terrainData.detailPrototypes = detailPrototypes.ToArray(); } // Set the DetailLayers for(int i = 0; i < numDetailLayers; ++i) { terrainData.SetDetailLayer(0, 0, i, convertedDetailMaps[i]); } #endif }
public void LoadPreset(HEU_SessionBase session, HEU_InputPreset inputPreset) { ResetInputNode(session); ChangeInputType(session, inputPreset._inputObjectType); if (inputPreset._inputObjectType == HEU_InputNode.InputObjectType.UNITY_MESH) { int numObjects = inputPreset._inputObjectPresets.Count; for (int i = 0; i < numObjects; ++i) { if (!string.IsNullOrEmpty(inputPreset._inputObjectPresets[i]._gameObjectName)) { GameObject inputGO = null; if (inputPreset._inputObjectPresets[i]._isSceneObject) { inputGO = HEU_GeneralUtility.GetGameObjectByNameInScene(inputPreset._inputObjectPresets[i]._gameObjectName); } else { // Use the _gameObjectName as path to find in scene inputGO = HEU_AssetDatabase.LoadAssetAtPath(inputPreset._inputObjectPresets[i]._gameObjectName, typeof(GameObject)) as GameObject; if(inputGO == null) { Debug.LogErrorFormat("Unable to find input at {0}", inputPreset._inputObjectPresets[i]._gameObjectName); } } if (inputGO != null) { HEU_InputObjectInfo inputObject = AddInputObjectAtEnd(inputGO); inputObject._useTransformOffset = inputPreset._inputObjectPresets[i]._useTransformOffset; inputObject._translateOffset = inputPreset._inputObjectPresets[i]._translateOffset; inputObject._rotateOffset = inputPreset._inputObjectPresets[i]._rotateOffset; inputObject._scaleOffset = inputPreset._inputObjectPresets[i]._scaleOffset; } else { Debug.LogWarningFormat("Gameobject with name {0} not found. Unable to set input object.", inputPreset._inputAssetName); } } } } else if (inputPreset._inputObjectType == HEU_InputNode.InputObjectType.HDA) { if (!string.IsNullOrEmpty(inputPreset._inputAssetName)) { GameObject inputAsset = GameObject.Find(inputPreset._inputAssetName); if (inputAsset != null) { HEU_HoudiniAssetRoot inputAssetRoot = inputAsset != null ? inputAsset.GetComponent<HEU_HoudiniAssetRoot>() : null; if (inputAssetRoot != null && inputAssetRoot._houdiniAsset != null) { // Need to reconnect and make sure connected asset is in this session _inputAsset = inputAsset; if (!inputAssetRoot._houdiniAsset.IsAssetValidInHoudini(session)) { inputAssetRoot._houdiniAsset.RequestCook(true, false, true, true); } _connectedNodeID = inputAssetRoot._houdiniAsset.AssetID; _parentAsset.ConnectToUpstream(inputAssetRoot._houdiniAsset); _connectedInputAsset = _inputAsset; } else { Debug.LogErrorFormat("Input HDA with name {0} is not a valid asset (missing components). Unable to set input asset.", inputPreset._inputAssetName); } } else { Debug.LogWarningFormat("Game with name {0} not found. Unable to set input asset.", inputPreset._inputAssetName); } } } KeepWorldTransform = inputPreset._keepWorldTransform; PackGeometryBeforeMerging = inputPreset._packGeometryBeforeMerging; RequiresUpload = true; ClearUICache(); }
private void GenerateInstancesFromNodeIDs(HAPI_NodeId cookNodeId, HEU_LoadBufferInstancer instancerBuffer, Dictionary <HAPI_NodeId, HEU_LoadBufferBase> idBuffersMap, Transform instanceRootTransform) { // For single collision geo override GameObject singleCollisionGO = null; // For multi collision geo overrides, keep track of loaded objects Dictionary <string, GameObject> loadedCollisionObjectMap = new Dictionary <string, GameObject>(); if (instancerBuffer._collisionAssetPaths != null && instancerBuffer._collisionAssetPaths.Length == 1) { // Single collision override if (!string.IsNullOrEmpty(instancerBuffer._collisionAssetPaths[0])) { HEU_AssetDatabase.ImportAsset(instancerBuffer._collisionAssetPaths[0], HEU_AssetDatabase.HEU_ImportAssetOptions.Default); singleCollisionGO = HEU_AssetDatabase.LoadAssetAtPath(instancerBuffer._collisionAssetPaths[0], typeof(GameObject)) as GameObject; } if (singleCollisionGO == null) { // Continue on but log error Debug.LogErrorFormat("Collision asset at path {0} not found for instance {1}.", instancerBuffer._collisionAssetPaths[0], instancerBuffer._name); } } int numInstances = instancerBuffer._instanceNodeIDs.Length; for (int i = 0; i < numInstances; ++i) { HEU_LoadBufferBase sourceBuffer = null; if (!idBuffersMap.TryGetValue(instancerBuffer._instanceNodeIDs[i], out sourceBuffer) || sourceBuffer == null) { Debug.LogErrorFormat("Part with id {0} is missing. Unable to setup instancer!", instancerBuffer._instanceNodeIDs[i]); return; } // If the part we're instancing is itself an instancer, make sure it has generated its instances if (sourceBuffer._bInstanced && sourceBuffer._generatedOutput == null) { HEU_LoadBufferInstancer sourceBufferInstancer = instancerBuffer as HEU_LoadBufferInstancer; if (sourceBufferInstancer != null) { GenerateInstancer(cookNodeId, sourceBufferInstancer, idBuffersMap); } } GameObject sourceGameObject = sourceBuffer._generatedOutput._outputData._gameObject; if (sourceGameObject == null) { Debug.LogErrorFormat("Output gameobject is null for source {0}. Unable to instance for {1}.", sourceBuffer._name, instancerBuffer._name); continue; } GameObject collisionSrcGO = null; if (singleCollisionGO != null) { // Single collision geo collisionSrcGO = singleCollisionGO; } else if (instancerBuffer._collisionAssetPaths != null && (i < instancerBuffer._collisionAssetPaths.Length) && !string.IsNullOrEmpty(instancerBuffer._collisionAssetPaths[i])) { // Mutliple collision geo (one per instance). if (!loadedCollisionObjectMap.TryGetValue(instancerBuffer._collisionAssetPaths[i], out collisionSrcGO)) { collisionSrcGO = HEU_AssetDatabase.LoadAssetAtPath(instancerBuffer._collisionAssetPaths[i], typeof(GameObject)) as GameObject; if (collisionSrcGO == null) { Debug.LogErrorFormat("Unable to load collision asset at {0} for instancing!", instancerBuffer._collisionAssetPaths[i]); } else { loadedCollisionObjectMap.Add(instancerBuffer._collisionAssetPaths[i], collisionSrcGO); } } } int numTransforms = instancerBuffer._instanceTransforms.Length; for (int j = 0; j < numTransforms; ++j) { CreateNewInstanceFromObject(sourceGameObject, (j + 1), instanceRootTransform, ref instancerBuffer._instanceTransforms[j], instancerBuffer._instancePrefixes, instancerBuffer._name, collisionSrcGO); } } }