public void CopyValuesTo(HEU_VolumeCache destCache) { destCache.UIExpanded = UIExpanded; destCache._terrainData = Object.Instantiate(_terrainData); if (_detailProperties != null) { if (destCache._detailProperties == null) { destCache._detailProperties = new HEU_DetailProperties(); } CopyDetailProperties(_detailProperties, destCache._detailProperties); } else { destCache._detailProperties = null; } foreach (HEU_VolumeLayer srcLayer in _layers) { HEU_VolumeLayer destLayer = destCache.GetLayer(srcLayer._layerName); if (destLayer != null) { CopyLayer(srcLayer, destLayer); } } }
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; }
public void ResetParameters() { HEU_VolumeLayer defaultLayer = new HEU_VolumeLayer(); foreach (HEU_VolumeLayer layer in _layers) { CopyLayer(defaultLayer, layer); } }
public void UpdateLayerFromPart(HEU_SessionBase session, HEU_PartData part) { HEU_GeoNode geoNode = part.ParentGeoNode; HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); bool bResult = session.GetVolumeInfo(geoNode.GeoID, part.PartID, ref volumeInfo); if (!bResult || volumeInfo.tupleSize != 1 || volumeInfo.zLength != 1 || volumeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT) { return; } string volumeName = HEU_SessionManager.GetString(volumeInfo.nameSH, session); part.SetVolumeLayerName(volumeName); //Debug.LogFormat("Part name: {0}, GeoName: {1}, Volume Name: {2}, Display: {3}", part.PartName, geoNode.GeoName, volumeName, geoNode.Displayable); bool bHeightPart = volumeName.Equals("height"); HEU_VolumeLayer layer = GetLayer(volumeName); if (layer == null) { layer = new HEU_VolumeLayer(); layer._layerName = volumeName; if (bHeightPart) { _layers.Insert(0, layer); } else { _layers.Add(layer); } } layer._part = part; GetPartLayerAttributes(session, geoNode.GeoID, part.PartID, layer); if (!bHeightPart) { part.DestroyAllData(); } if (!_updatedLayers.Contains(layer)) { if (bHeightPart) { _updatedLayers.Insert(0, layer); } else { _updatedLayers.Add(layer); } } }
public void ResetParameters() { _terrainData = null; _scatterTrees = null; HEU_VolumeLayer defaultLayer = new HEU_VolumeLayer(); foreach (HEU_VolumeLayer layer in _layers) { CopyLayer(defaultLayer, layer); } }
public void CopyValuesTo(HEU_VolumeCache destCache) { destCache.UIExpanded = UIExpanded; foreach(HEU_VolumeLayer srcLayer in _layers) { HEU_VolumeLayer destLayer = destCache.GetLayer(srcLayer._layerName); if(destLayer != null) { CopyLayer(srcLayer, destLayer); } } }
public void CopyValuesTo(HEU_VolumeCache destCache) { destCache.UIExpanded = UIExpanded; destCache._terrainData = Object.Instantiate(_terrainData); foreach (HEU_VolumeLayer srcLayer in _layers) { HEU_VolumeLayer destLayer = destCache.GetLayer(srcLayer._layerName); if(destLayer != null) { CopyLayer(srcLayer, destLayer); } } }
private void UpdateVolumeLayers(HEU_SessionBase session, HEU_HoudiniAsset houdiniAsset, List<HEU_PartData> volumeParts) { bool bResult; foreach (HEU_PartData part in volumeParts) { HEU_GeoNode geoNode = part.ParentGeoNode; HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); bResult = session.GetVolumeInfo(geoNode.GeoID, part.PartID, ref volumeInfo); if (!bResult || volumeInfo.tupleSize != 1 || volumeInfo.zLength != 1 || volumeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT) { continue; } string volumeName = HEU_SessionManager.GetString(volumeInfo.nameSH, session); part.SetVolumeLayerName(volumeName); //Debug.LogFormat("Part name: {0}, GeoName: {1}, Volume Name: {2}, Display: {3}", part.PartName, geoNode.GeoName, volumeName, geoNode.Displayable); bool bHeightPart = volumeName.Equals("height"); HEU_VolumeLayer layer = GetLayer(volumeName); if (layer == null) { layer = new HEU_VolumeLayer(); layer._layerName = volumeName; layer._splatTexture = LoadDefaultSplatTexture(); if (bHeightPart) { _layers.Insert(0, layer); } else { _layers.Add(layer); } } layer._part = part; if (!bHeightPart) { part.DestroyAllData(); } } }
public void CopyValuesTo(HEU_VolumeCache destCache) { destCache.UIExpanded = UIExpanded; foreach(HEU_VolumeLayer srcLayer in _layers) { HEU_VolumeLayer destLayer = destCache.GetLayer(srcLayer._layerName); if(destLayer != null) { destLayer._strength = srcLayer._strength; destLayer._splatTexture = srcLayer._splatTexture; destLayer._normalTexture = srcLayer._normalTexture; destLayer._tileSize = srcLayer._tileSize; destLayer._tileOffset = srcLayer._tileOffset; destLayer._metallic = srcLayer._metallic; destLayer._smoothness = srcLayer._smoothness; destLayer._uiExpanded = srcLayer._uiExpanded; } } }
public static void CopyLayer(HEU_VolumeLayer srcLayer, HEU_VolumeLayer destLayer) { destLayer._strength = srcLayer._strength; destLayer._diffuseTexture = srcLayer._diffuseTexture; destLayer._maskTexture = srcLayer._maskTexture; destLayer._metallic = srcLayer._metallic; destLayer._normalTexture = srcLayer._normalTexture; destLayer._normalScale = srcLayer._normalScale; destLayer._smoothness = srcLayer._smoothness; destLayer._specularColor = srcLayer._specularColor; destLayer._tileSize = srcLayer._tileSize; destLayer._tileOffset = srcLayer._tileOffset; destLayer._uiExpanded = srcLayer._uiExpanded; destLayer._tile = srcLayer._tile; destLayer._overrides = srcLayer._overrides; }
public static void CopyLayer(HEU_VolumeLayer srcLayer, HEU_VolumeLayer destLayer) { destLayer._strength = srcLayer._strength; destLayer._uiExpanded = srcLayer._uiExpanded; destLayer._tile = srcLayer._tile; destLayer._xLength = srcLayer._xLength; destLayer._yLength = srcLayer._yLength; destLayer._layerType = srcLayer._layerType; if (srcLayer._detailPrototype != null) { if (destLayer._detailPrototype == null) { destLayer._detailPrototype = new HEU_DetailPrototype(); } CopyPrototype(srcLayer._detailPrototype, destLayer._detailPrototype); } else { destLayer._detailPrototype = null; } }
public static void CopyLayer(HEU_VolumeLayer srcLayer, HEU_VolumeLayer destLayer) { destLayer._strength = srcLayer._strength; destLayer._uiExpanded = srcLayer._uiExpanded; destLayer._tile = srcLayer._tile; }
public static bool IsLayerFieldOverriden(HEU_VolumeLayer layer, HEU_VolumeLayer.Overrides field) { return (layer._overrides & field) == field; }
public void GenerateTerrainWithAlphamaps(HEU_SessionBase session, HEU_HoudiniAsset houdiniAsset) { if(_layers == null || _layers.Count == 0) { Debug.LogError("Unable to generate terrain due to lack of heightfield layers!"); return; } HEU_VolumeLayer baseLayer = _layers[0]; HAPI_VolumeInfo baseVolumeInfo = new HAPI_VolumeInfo(); bool bResult = session.GetVolumeInfo(_ownerNode.GeoID, baseLayer._part.PartID, ref baseVolumeInfo); if (!bResult) { Debug.LogErrorFormat("Unable to get volume info for layer {0}!", baseLayer._layerName); return; } TerrainData terrainData = null; Vector3 terrainOffsetPosition = Vector3.zero; // Generate the terrain and terrain data from the heightmap's height layer bResult = HEU_GeometryUtility.GenerateTerrainFromVolume(session, ref baseVolumeInfo, baseLayer._part.ParentGeoNode.GeoID, baseLayer._part.PartID, baseLayer._part.OutputGameObject, out terrainData, out terrainOffsetPosition); if (!bResult) { return; } baseLayer._part.SetTerrainData(terrainData); baseLayer._part.SetTerrainOffsetPosition(terrainOffsetPosition); int terrainSize = terrainData.heightmapResolution; // Now set the alphamaps (textures with masks) for the other layers // First, preprocess all layers to get heightfield arrays, converted to proper size // Then, merge into a float[x,y,map] List<float[]> heightFields = new List<float[]>(); List<HEU_VolumeLayer> validLayers = new List<HEU_VolumeLayer>(); int numLayers = _layers.Count; for(int i = 1; i < numLayers; ++i) { float[] hf = HEU_GeometryUtility.GetHeightfieldFromPart(session, _ownerNode.GeoID, _layers[i]._part.PartID, _layers[i]._part.PartName, terrainSize); if (hf != null && hf.Length > 0) { heightFields.Add(hf); validLayers.Add(_layers[i]); } } // Total maps is masks plus base height layer int numMaps = heightFields.Count + 1; // Assign floats to alpha map float[,,] alphamap = new float[terrainSize, terrainSize, numMaps]; for (int y = 0; y < terrainSize; ++y) { for (int x = 0; x < terrainSize; ++x) { float f = 0f; for (int m = numMaps - 1; m > 0; --m) { float a = heightFields[m - 1][y + terrainSize * x]; a = Mathf.Clamp01(a - f) * validLayers[m - 1]._strength; alphamap[x, y, m] = a; f += a; } // Base layer gets leftover value alphamap[x, y, 0] = Mathf.Clamp01(1.0f - f) * baseLayer._strength; } } #if UNITY_2018_3_OR_NEWER // Create TerrainLayer for each heightfield layer // Note that at time of this implementation the new Unity terrain // is still in beta. Therefore, the following layer creation is subject // to change. TerrainLayer[] terrainLayers = new TerrainLayer[numMaps]; for (int m = 0; m < numMaps; ++m) { terrainLayers[m] = new TerrainLayer(); HEU_VolumeLayer layer = (m == 0) ? baseLayer : validLayers[m - 1]; terrainLayers[m].diffuseTexture = layer._diffuseTexture; terrainLayers[m].diffuseRemapMin = Vector4.zero; terrainLayers[m].diffuseRemapMax = Vector4.one; terrainLayers[m].maskMapTexture = layer._maskTexture; terrainLayers[m].maskMapRemapMin = Vector4.zero; terrainLayers[m].maskMapRemapMax = Vector4.one; terrainLayers[m].metallic = layer._metallic; terrainLayers[m].normalMapTexture = layer._normalTexture; terrainLayers[m].normalScale = layer._normalScale; terrainLayers[m].smoothness = layer._smoothness; terrainLayers[m].specular = layer._specularColor; terrainLayers[m].tileOffset = layer._tileOffset; if (layer._tileSize.magnitude == 0f) { // Use texture size if tile size is 0 layer._tileSize = new Vector2(layer._diffuseTexture.width, layer._diffuseTexture.height); } terrainLayers[m].tileSize = layer._tileSize; } terrainData.terrainLayers = terrainLayers; #else // Need to create SplatPrototype for each layer in heightfield, representing the textures. SplatPrototype[] splatPrototypes = new SplatPrototype[numMaps]; for (int m = 0; m < numMaps; ++m) { splatPrototypes[m] = new SplatPrototype(); HEU_VolumeLayer layer = (m == 0) ? baseLayer : validLayers[m - 1]; splatPrototypes[m].texture = layer._diffuseTexture; splatPrototypes[m].tileOffset = layer._tileOffset; if(layer._tileSize.magnitude == 0f) { // Use texture size if tile size is 0 layer._tileSize = new Vector2(layer._diffuseTexture.width, layer._diffuseTexture.height); } splatPrototypes[m].tileSize = layer._tileSize; splatPrototypes[m].metallic = layer._metallic; splatPrototypes[m].smoothness = layer._smoothness; splatPrototypes[m].normalMap = layer._normalTexture; } terrainData.splatPrototypes = splatPrototypes; #endif terrainData.SetAlphamaps(0, 0, alphamap); }
private void GetPartLayerAttributes(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_NodeId partID, HEU_VolumeLayer layer) { // Get the tile index, if it exists, for this part HAPI_AttributeInfo tileAttrInfo = new HAPI_AttributeInfo(); int[] tileAttrData = new int[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HAPI_HEIGHTFIELD_TILE_ATTR, ref tileAttrInfo, ref tileAttrData, session.GetAttributeIntData); if (tileAttrData != null && tileAttrData.Length > 0) { layer._tile = tileAttrData[0]; //Debug.LogFormat("Tile: {0}", tileAttrData[0]); } else { layer._tile = 0; } string[] layerAttrNames = { HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_DIFFUSE_ATTR, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_MASK_ATTR, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_NORMAL_ATTR, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_NORMAL_SCALE_ATTR, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_METALLIC_ATTR, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_SMOOTHNESS_ATTR, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_SPECULAR_ATTR, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TILE_OFFSET_ATTR, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TILE_SIZE_ATTR }; // Check if any of the layer attribute names show up in the existing primitive attributes layer._hasLayerAttributes = false; HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); bool bResult = false; foreach (string layerAttr in layerAttrNames) { bResult = session.GetAttributeInfo(geoID, partID, layerAttr, HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM, ref attrInfo); if (bResult && attrInfo.exists) { layer._hasLayerAttributes = true; break; } } }
public void UpdateLayerFromPart(HEU_SessionBase session, HEU_PartData part) { HEU_GeoNode geoNode = part.ParentGeoNode; HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); bool bResult = session.GetVolumeInfo(geoNode.GeoID, part.PartID, ref volumeInfo); if (!bResult || volumeInfo.tupleSize != 1 || volumeInfo.zLength != 1 || volumeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT) { return; } string volumeName = HEU_SessionManager.GetString(volumeInfo.nameSH, session); part.SetVolumeLayerName(volumeName); //Debug.LogFormat("Part name: {0}, GeoName: {1}, Volume Name: {2}, Display: {3}", part.PartName, geoNode.GeoName, volumeName, geoNode.Displayable); bool bHeightPart = volumeName.Equals(HEU_Defines.HAPI_HEIGHTFIELD_LAYERNAME_HEIGHT); bool bMaskPart = volumeName.Equals(HEU_Defines.HAPI_HEIGHTFIELD_LAYERNAME_MASK); HEU_VolumeLayer layer = GetLayer(volumeName); if (layer == null) { layer = new HEU_VolumeLayer(); layer._layerName = volumeName; if (bHeightPart) { _layers.Insert(0, layer); } else if(!bMaskPart) { _layers.Add(layer); } } layer._part = part; layer._xLength = volumeInfo.xLength; layer._yLength = volumeInfo.yLength; if (!bMaskPart) { GetPartLayerAttributes(session, geoNode.GeoID, part.PartID, layer); } if (!bHeightPart) { // Non-height parts don't have any outputs as they are simply layers carrying info part.DestroyAllData(); } else { // Height part List<HEU_TreePrototypeInfo> treePrototypeInfos = HEU_TerrainUtility.GetTreePrototypeInfosFromPart(session, geoNode.GeoID, part.PartID); if (treePrototypeInfos != null) { if (_scatterTrees == null) { _scatterTrees = new HEU_VolumeScatterTrees(); } _scatterTrees._treePrototypInfos = treePrototypeInfos; } } if (!_updatedLayers.Contains(layer)) { if (bHeightPart) { _updatedLayers.Insert(0, layer); } else if (!bMaskPart) { _updatedLayers.Add(layer); } } }
public void ProcessVolumeParts(HEU_SessionBase session, List <HEU_PartData> volumeParts, bool bRebuild) { int numVolumeParts = volumeParts.Count; if (numVolumeParts == 0) { DestroyVolumeCache(); } else if (_volumeCaches == null) { _volumeCaches = new List <HEU_VolumeCache>(); } // First update volume caches. Each volume cache represents a set of terrain layers grouped by tile index. // Therefore each volume cache represents a potential Unity Terrain (containing layers) _volumeCaches = HEU_VolumeCache.UpdateVolumeCachesFromParts(session, this, volumeParts, _volumeCaches); // Heightfield scatter nodes come in as mesh-type parts with attribute instancing. // So process them here to get all the tree/detail instance scatter information. int numParts = _parts.Count; for (int i = 0; i < numParts; ++i) { // Find the terrain tile (use primitive attr). Assume 0 tile if not set (i.e. not split into tiles) int terrainTile = 0; HAPI_AttributeInfo tileAttrInfo = new HAPI_AttributeInfo(); int[] tileAttrData = new int[0]; if (HEU_GeneralUtility.GetAttribute(session, GeoID, _parts[i].PartID, HEU_Defines.HAPI_HEIGHTFIELD_TILE_ATTR, ref tileAttrInfo, ref tileAttrData, session.GetAttributeIntData)) { if (tileAttrData != null && tileAttrData.Length > 0) { terrainTile = tileAttrData[0]; } } // Find the volumecache associated with this part using the terrain tile index HEU_VolumeCache volumeCache = GetVolumeCacheByTileIndex(terrainTile); if (volumeCache == null) { continue; } HEU_VolumeLayer volumeLayer = volumeCache.GetLayer(_parts[i].GetVolumeLayerName()); if (volumeLayer != null && volumeLayer._layerType == HFLayerType.DETAIL) { // Clear out outputs since it might have been created when the part was created. _parts[i].DestroyAllData(); volumeCache.PopulateDetailPrototype(session, GeoID, _parts[i].PartID, volumeLayer); } else if (_parts[i].IsAttribInstancer()) { HAPI_AttributeInfo treeInstAttrInfo = new HAPI_AttributeInfo(); if (HEU_GeneralUtility.GetAttributeInfo(session, GeoID, _parts[i].PartID, HEU_Defines.HEIGHTFIELD_TREEINSTANCE_PROTOTYPEINDEX, ref treeInstAttrInfo)) { if (treeInstAttrInfo.exists && treeInstAttrInfo.count > 0) { // Clear out outputs since it might have been created when the part was created. _parts[i].DestroyAllData(); // Mark the instancers as having been created so that the object instancer step skips this. _parts[i].ObjectInstancesBeenGenerated = true; // Now populate scatter trees based on attributes on this part volumeCache.PopulateScatterTrees(session, GeoID, _parts[i].PartID, treeInstAttrInfo.count); } } } } // Now generate the terrain for each volume cache foreach (HEU_VolumeCache cache in _volumeCaches) { cache.GenerateTerrainWithAlphamaps(session, ParentAsset, bRebuild); cache.IsDirty = false; } }
public void UpdateLayerFromPart(HEU_SessionBase session, HEU_PartData part) { HEU_GeoNode geoNode = part.ParentGeoNode; HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); bool bResult = session.GetVolumeInfo(geoNode.GeoID, part.PartID, ref volumeInfo); if (!bResult || volumeInfo.tupleSize != 1 || volumeInfo.zLength != 1 || volumeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT) { return; } string volumeName = HEU_SessionManager.GetString(volumeInfo.nameSH, session); part.SetVolumeLayerName(volumeName); //Debug.LogFormat("Part name: {0}, GeoName: {1}, Volume Name: {2}, Display: {3}", part.PartName, geoNode.GeoName, volumeName, geoNode.Displayable); HFLayerType layerType = HEU_TerrainUtility.GetHeightfieldLayerType(session, geoNode.GeoID, part.PartID, volumeName); HEU_VolumeLayer layer = GetLayer(volumeName); if (layer == null) { layer = new HEU_VolumeLayer(); layer._layerName = volumeName; if (layerType == HFLayerType.HEIGHT) { _layers.Insert(0, layer); } else if (layerType != HFLayerType.MASK) { _layers.Add(layer); } } layer._part = part; layer._xLength = volumeInfo.xLength; layer._yLength = volumeInfo.yLength; layer._layerType = layerType; if (layerType != HFLayerType.MASK) { GetPartLayerAttributes(session, geoNode.GeoID, part.PartID, layer); } if (layerType != HFLayerType.HEIGHT) { // Non-height parts don't have any outputs as they are simply layers carrying info part.DestroyAllData(); } else { // Height part // Might contain terrain properties via attributes (i.e. not layer specific, but for entire terrain) // Scatter Tree Prototypes List<HEU_TreePrototypeInfo> treePrototypeInfos = HEU_TerrainUtility.GetTreePrototypeInfosFromPart(session, geoNode.GeoID, part.PartID); if (treePrototypeInfos != null) { if (_scatterTrees == null) { _scatterTrees = new HEU_VolumeScatterTrees(); } _scatterTrees._treePrototypInfos = treePrototypeInfos; } HEU_TerrainUtility.PopulateDetailProperties(session, geoNode.GeoID, part.PartID, ref _detailProperties); } if (!_updatedLayers.Contains(layer)) { if (layerType == HFLayerType.HEIGHT) { _updatedLayers.Insert(0, layer); } else if (layerType != HFLayerType.MASK) { _updatedLayers.Add(layer); } } }
public void PopulateDetailPrototype(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, HEU_VolumeLayer layer) { HEU_TerrainUtility.PopulateDetailPrototype(session, geoID, partID, ref layer._detailPrototype); }
private void GetPartLayerAttributes(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_NodeId partID, HEU_VolumeLayer layer) { // Get the tile index, if it exists, for this part HAPI_AttributeInfo tileAttrInfo = new HAPI_AttributeInfo(); int[] tileAttrData = new int[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HAPI_HEIGHTFIELD_TILE_ATTR, ref tileAttrInfo, ref tileAttrData, session.GetAttributeIntData); if (tileAttrData != null && tileAttrData.Length > 0) { layer._tile = tileAttrData[0]; //Debug.LogFormat("Tile: {0}", tileAttrData[0]); } else { layer._tile = 0; } layer._hasLayerAttributes = HEU_TerrainUtility.VolumeLayerHasAttributes(session, geoID, partID); }
private void GetPartLayerAttributes(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_NodeId partID, HEU_VolumeLayer layer) { // Get the tile index, if it exists, for this part HAPI_AttributeInfo tileAttrInfo = new HAPI_AttributeInfo(); int[] tileAttrData = new int[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, "tile", ref tileAttrInfo, ref tileAttrData, session.GetAttributeIntData); if (tileAttrData != null && tileAttrData.Length > 0) { layer._tile = tileAttrData[0]; //Debug.LogFormat("Tile: {0}", tileAttrData[0]); } else { layer._tile = 0; } // Get the layer textures, and other layer values from attributes Texture2D defaultTexture = LoadDefaultSplatTexture(); if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.Diffuse) && (layer._diffuseTexture == null || layer._diffuseTexture == defaultTexture)) { layer._diffuseTexture = LoadLayerTextureFromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_DIFFUSE_ATTR); if (layer._diffuseTexture == null) { layer._diffuseTexture = defaultTexture; } } if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.Mask) && layer._maskTexture == null) { layer._maskTexture = LoadLayerTextureFromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_MASK_ATTR); } if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.Normal) && layer._normalTexture == null) { layer._normalTexture = LoadLayerTextureFromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_NORMAL_ATTR); } if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.NormalScale)) { LoadLayerFloatFromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_NORMAL_SCALE_ATTR, ref layer._normalScale); } if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.Metallic)) { LoadLayerFloatFromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_METALLIC_ATTR, ref layer._metallic); } if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.Smoothness)) { LoadLayerFloatFromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_SMOOTHNESS_ATTR, ref layer._smoothness); } if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.Specular)) { LoadLayerColorFromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_SPECULAR_ATTR, ref layer._specularColor); } if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.TileOffset)) { LoadLayerVector2FromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TILE_OFFSET_ATTR, ref layer._tileOffset); } if (!IsLayerFieldOverriden(layer, HEU_VolumeLayer.Overrides.TileSize)) { LoadLayerVector2FromAttribute(session, geoID, partID, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TILE_SIZE_ATTR, ref layer._tileSize); } }
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(); }
public void UpdateLayerFromPart(HEU_SessionBase session, HEU_PartData part) { HEU_GeoNode geoNode = part.ParentGeoNode; HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); bool bResult = session.GetVolumeInfo(geoNode.GeoID, part.PartID, ref volumeInfo); if (!bResult || volumeInfo.tupleSize != 1 || volumeInfo.zLength != 1 || volumeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT) { return; } string volumeName = HEU_SessionManager.GetString(volumeInfo.nameSH, session); part.SetVolumeLayerName(volumeName); //Debug.LogFormat("Part name: {0}, GeoName: {1}, Volume Name: {2}, Display: {3}", part.PartName, geoNode.GeoName, volumeName, geoNode.Displayable); HEU_VolumeLayer.HFLayerType layerType = GetHeightfieldLayerType(session, geoNode.GeoID, part.PartID, volumeName); HEU_VolumeLayer layer = GetLayer(volumeName); if (layer == null) { layer = new HEU_VolumeLayer(); layer._layerName = volumeName; if (layerType == HEU_VolumeLayer.HFLayerType.HEIGHT) { _layers.Insert(0, layer); } else if(layerType != HEU_VolumeLayer.HFLayerType.MASK) { _layers.Add(layer); } } layer._part = part; layer._xLength = volumeInfo.xLength; layer._yLength = volumeInfo.yLength; layer._layerType = layerType; if (layerType != HEU_VolumeLayer.HFLayerType.MASK) { GetPartLayerAttributes(session, geoNode.GeoID, part.PartID, layer); } if (layerType != HEU_VolumeLayer.HFLayerType.HEIGHT) { // Non-height parts don't have any outputs as they are simply layers carrying info part.DestroyAllData(); } else { // Height part // Might contain terrain properties via attributes (i.e. not layer specific, but for entire terrain) // Scatter Tree Prototypes List<HEU_TreePrototypeInfo> treePrototypeInfos = HEU_TerrainUtility.GetTreePrototypeInfosFromPart(session, geoNode.GeoID, part.PartID); if (treePrototypeInfos != null) { if (_scatterTrees == null) { _scatterTrees = new HEU_VolumeScatterTrees(); } _scatterTrees._treePrototypInfos = treePrototypeInfos; } // Detail distance HAPI_AttributeInfo detailDistanceAttrInfo = new HAPI_AttributeInfo(); int[] detailDistances = new int[0]; HEU_GeneralUtility.GetAttribute(session, geoNode.GeoID, part.PartID, HEU_Defines.HEIGHTFIELD_DETAIL_DISTANCE, ref detailDistanceAttrInfo, ref detailDistances, session.GetAttributeIntData); // Scatter Detail Resolution Per Patch (note that Detail Resolution comes from HF layer size) HAPI_AttributeInfo resolutionPatchAttrInfo = new HAPI_AttributeInfo(); int[] resolutionPatches = new int[0]; HEU_GeneralUtility.GetAttribute(session, geoNode.GeoID, part.PartID, HEU_Defines.HEIGHTFIELD_DETAIL_RESOLUTION_PER_PATCH, ref resolutionPatchAttrInfo, ref resolutionPatches, session.GetAttributeIntData); if (_detailProperties == null) { _detailProperties = new HEU_DetailProperties(); } // Unity only supports 1 set of detail resolution properties per terrain int arraySize = 1; if (detailDistanceAttrInfo.exists && detailDistances.Length >= arraySize) { _detailProperties._detailDistance = detailDistances[0]; } if (resolutionPatchAttrInfo.exists && resolutionPatches.Length >= arraySize) { _detailProperties._detailResolutionPerPatch = resolutionPatches[0]; } } if (!_updatedLayers.Contains(layer)) { if (layerType == HEU_VolumeLayer.HFLayerType.HEIGHT) { _updatedLayers.Insert(0, layer); } else if (layerType != HEU_VolumeLayer.HFLayerType.MASK) { _updatedLayers.Add(layer); } } }