public bool GenerateTerrainBuffers(HEU_SessionBase session, HAPI_NodeId nodeID, List<HAPI_PartInfo> volumeParts, List<HAPI_PartInfo> scatterInstancerParts, out List<HEU_LoadBufferVolume> volumeBuffers) { volumeBuffers = null; if (volumeParts.Count == 0) { return true; } volumeBuffers = new List<HEU_LoadBufferVolume>(); int numParts = volumeParts.Count; for (int i = 0; i < numParts; ++i) { HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); bool bResult = session.GetVolumeInfo(nodeID, volumeParts[i].id, ref volumeInfo); if (!bResult || volumeInfo.tupleSize != 1 || volumeInfo.zLength != 1 || volumeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT) { SetLog(HEU_LoadData.LoadStatus.ERROR, "This heightfield is not supported. Please check documentation."); return false; } if (volumeInfo.xLength != volumeInfo.yLength) { SetLog(HEU_LoadData.LoadStatus.ERROR, "Non-square sized terrain not supported."); return false; } string volumeName = HEU_SessionManager.GetString(volumeInfo.nameSH, session); bool bHeightPart = volumeName.Equals(HEU_Defines.HAPI_HEIGHTFIELD_LAYERNAME_HEIGHT); bool bMaskPart = volumeName.Equals(HEU_Defines.HAPI_HEIGHTFIELD_LAYERNAME_MASK); //Debug.LogFormat("Part name: {0}, GeoName: {1}, Volume Name: {2}, Display: {3}", part.PartName, geoNode.GeoName, volumeName, geoNode.Displayable); // Ignoring mask layer because it is Houdini-specific (same behaviour as regular HDA terrain generation) if (bMaskPart) { continue; } HEU_LoadBufferVolumeLayer layer = new HEU_LoadBufferVolumeLayer(); layer._layerName = volumeName; layer._partID = volumeParts[i].id; layer._heightMapWidth = volumeInfo.xLength; layer._heightMapHeight = volumeInfo.yLength; Matrix4x4 volumeTransformMatrix = HEU_HAPIUtility.GetMatrixFromHAPITransform(ref volumeInfo.transform, false); layer._position = HEU_HAPIUtility.GetPosition(ref volumeTransformMatrix); Vector3 scale = HEU_HAPIUtility.GetScale(ref volumeTransformMatrix); // Calculate real terrain size in both Houdini and Unity. // The height values will be mapped over this terrain size. float gridSpacingX = scale.x * 2f; float gridSpacingY = scale.y * 2f; layer._terrainSizeX = Mathf.Round((volumeInfo.xLength - 1) * gridSpacingX); layer._terrainSizeY = Mathf.Round((volumeInfo.yLength - 1) * gridSpacingY); // Get volume bounds for calculating position offset session.GetVolumeBounds(nodeID, volumeParts[i].id, out layer._minBounds.x, out layer._minBounds.y, out layer._minBounds.z, out layer._maxBounds.x, out layer._maxBounds.y, out layer._maxBounds.z, out layer._center.x, out layer._center.y, out layer._center.z); // Look up TerrainLayer file via attribute if user has set it layer._layerPath = HEU_GeneralUtility.GetAttributeStringValueSingle(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TERRAINLAYER_FILE_ATTR, HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM); LoadStringFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_DIFFUSE_ATTR, ref layer._diffuseTexturePath); LoadStringFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_MASK_ATTR, ref layer._maskTexturePath); LoadStringFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_NORMAL_ATTR, ref layer._normalTexturePath); LoadFloatFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_NORMAL_SCALE_ATTR, ref layer._normalScale); LoadFloatFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_METALLIC_ATTR, ref layer._metallic); LoadFloatFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_SMOOTHNESS_ATTR, ref layer._smoothness); LoadLayerColorFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_SPECULAR_ATTR, ref layer._specularColor); LoadLayerVector2FromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TILE_OFFSET_ATTR, ref layer._tileOffset); LoadLayerVector2FromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TILE_SIZE_ATTR, ref layer._tileSize); // Get the height values from Houdini along with the min and max height range. layer._normalizedHeights = HEU_TerrainUtility.GetNormalizedHeightmapFromPartWithMinMax(_session, nodeID, volumeParts[i].id, volumeInfo.xLength, volumeInfo.yLength, ref layer._minHeight, ref layer._maxHeight, ref layer._heightRange); // 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, nodeID, volumeParts[i].id, HEU_Defines.HAPI_HEIGHTFIELD_TILE_ATTR, ref tileAttrInfo, ref tileAttrData, session.GetAttributeIntData); int tileIndex = 0; if (tileAttrInfo.exists && tileAttrData.Length == 1) { tileIndex = tileAttrData[0]; } // Add layer based on tile index if (tileIndex >= 0) { HEU_LoadBufferVolume volumeBuffer = null; for(int j = 0; j < volumeBuffers.Count; ++j) { if (volumeBuffers[j]._tileIndex == tileIndex) { volumeBuffer = volumeBuffers[j]; break; } } if (volumeBuffer == null) { volumeBuffer = new HEU_LoadBufferVolume(); volumeBuffer.InitializeBuffer(volumeParts[i].id, volumeName, false, false); volumeBuffer._tileIndex = tileIndex; volumeBuffers.Add(volumeBuffer); } if (bHeightPart) { // Height layer always first layer volumeBuffer._layers.Insert(0, layer); volumeBuffer._heightMapWidth = layer._heightMapWidth; volumeBuffer._heightMapHeight = layer._heightMapHeight; volumeBuffer._terrainSizeX = layer._terrainSizeX; volumeBuffer._terrainSizeY = layer._terrainSizeY; volumeBuffer._heightRange = (layer._maxHeight - layer._minHeight); // Look up TerrainData file path via attribute if user has set it volumeBuffer._terrainDataPath = HEU_GeneralUtility.GetAttributeStringValueSingle(session, nodeID, volumeBuffer._id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TERRAINDATA_FILE_ATTR, HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM); // Load the TreePrototype buffers List<HEU_TreePrototypeInfo> treePrototypeInfos = HEU_TerrainUtility.GetTreePrototypeInfosFromPart(session, nodeID, volumeBuffer._id); if (treePrototypeInfos != null) { if (volumeBuffer._scatterTrees == null) { volumeBuffer._scatterTrees = new HEU_VolumeScatterTrees(); } volumeBuffer._scatterTrees._treePrototypInfos = treePrototypeInfos; } } else { volumeBuffer._layers.Add(layer); } } Sleep(); } // Each volume buffer is a self contained terrain tile foreach(HEU_LoadBufferVolume volumeBuffer in volumeBuffers) { List<HEU_LoadBufferVolumeLayer> layers = volumeBuffer._layers; //Debug.LogFormat("Heightfield: tile={0}, layers={1}", tile._tileIndex, layers.Count); int heightMapWidth = volumeBuffer._heightMapWidth; int heightMapHeight = volumeBuffer._heightMapHeight; int numLayers = layers.Count; if (numLayers > 0) { // Convert heightmap values from Houdini to Unity volumeBuffer._heightMap = HEU_TerrainUtility.ConvertHeightMapHoudiniToUnity(heightMapWidth, heightMapHeight, layers[0]._normalizedHeights); Sleep(); // Convert splatmap values from Houdini to Unity. // Start at 2nd index since height is strictly for height values (not splatmap). List<float[]> heightFields = new List<float[]>(); for(int m = 1; m < numLayers; ++m) { heightFields.Add(layers[m]._normalizedHeights); } // The number of maps are the number of splatmaps (ie. non height/mask layers) int numMaps = heightFields.Count; if (numMaps > 0) { // Using the first splatmap size for all splatmaps volumeBuffer._splatMaps = HEU_TerrainUtility.ConvertHeightFieldToAlphaMap(layers[1]._heightMapWidth, layers[1]._heightMapHeight, heightFields); } else { volumeBuffer._splatMaps = null; } volumeBuffer._position = new Vector3((volumeBuffer._terrainSizeX + volumeBuffer._layers[0]._minBounds.x), volumeBuffer._layers[0]._minHeight + volumeBuffer._layers[0]._position.y, volumeBuffer._layers[0]._minBounds.z); } } // Process the scatter instancer parts to get the scatter data for (int i = 0; i < scatterInstancerParts.Count; ++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, nodeID, scatterInstancerParts[i].id, HEU_Defines.HAPI_HEIGHTFIELD_TILE_ATTR, ref tileAttrInfo, ref tileAttrData, session.GetAttributeIntData)) { if (tileAttrData != null && tileAttrData.Length > 0) { terrainTile = tileAttrData[0]; } } // Find the volume layer associated with this part using the terrain tile index HEU_LoadBufferVolume volumeBuffer = GetLoadBufferVolumeFromTileIndex(terrainTile, volumeBuffers); if (volumeBuffer == null) { continue; } HEU_TerrainUtility.PopulateScatterInfo(session, nodeID, scatterInstancerParts[i].id, scatterInstancerParts[i].pointCount, ref volumeBuffer._scatterTrees); } return true; }
public bool GenerateTerrainBuffers(HEU_SessionBase session, HAPI_NodeId nodeID, List<HAPI_PartInfo> volumeParts, out List<HEU_LoadBufferVolume> volumeBuffers) { volumeBuffers = null; if (volumeParts.Count == 0) { return true; } volumeBuffers = new List<HEU_LoadBufferVolume>(); int numParts = volumeParts.Count; for (int i = 0; i < numParts; ++i) { HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); bool bResult = session.GetVolumeInfo(nodeID, volumeParts[i].id, ref volumeInfo); if (!bResult || volumeInfo.tupleSize != 1 || volumeInfo.zLength != 1 || volumeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT) { SetLog(HEU_LoadData.LoadStatus.ERROR, "This heightfield is not supported. Please check documentation."); return false; } if (volumeInfo.xLength != volumeInfo.yLength) { SetLog(HEU_LoadData.LoadStatus.ERROR, "Non-square sized terrain not supported."); return false; } string volumeName = HEU_SessionManager.GetString(volumeInfo.nameSH, session); bool bHeightPart = volumeName.Equals("height"); //Debug.LogFormat("Part name: {0}, GeoName: {1}, Volume Name: {2}, Display: {3}", part.PartName, geoNode.GeoName, volumeName, geoNode.Displayable); HEU_LoadBufferVolumeLayer layer = new HEU_LoadBufferVolumeLayer(); layer._layerName = volumeName; layer._partID = volumeParts[i].id; layer._heightMapSize = volumeInfo.xLength; Matrix4x4 volumeTransformMatrix = HEU_HAPIUtility.GetMatrixFromHAPITransform(ref volumeInfo.transform, false); layer._position = HEU_HAPIUtility.GetPosition(ref volumeTransformMatrix); Vector3 scale = HEU_HAPIUtility.GetScale(ref volumeTransformMatrix); // Calculate real terrain size in both Houdini and Unity. // The height values will be mapped over this terrain size. float gridSpacingX = scale.x * 2f; float gridSpacingY = scale.y * 2f; layer._terrainSizeX = Mathf.Round((volumeInfo.xLength - 1) * gridSpacingX); layer._terrainSizeY = Mathf.Round((volumeInfo.yLength - 1) * gridSpacingY); // Get volume bounds for calculating position offset session.GetVolumeBounds(nodeID, volumeParts[i].id, out layer._minBounds.x, out layer._minBounds.y, out layer._minBounds.z, out layer._maxBounds.x, out layer._maxBounds.y, out layer._maxBounds.z, out layer._center.x, out layer._center.y, out layer._center.z); LoadStringFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_DIFFUSE_ATTR, ref layer._diffuseTexturePath); LoadStringFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_MASK_ATTR, ref layer._maskTexturePath); LoadStringFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TEXTURE_NORMAL_ATTR, ref layer._normalTexturePath); LoadFloatFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_NORMAL_SCALE_ATTR, ref layer._normalScale); LoadFloatFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_METALLIC_ATTR, ref layer._metallic); LoadFloatFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_SMOOTHNESS_ATTR, ref layer._smoothness); LoadLayerColorFromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_SPECULAR_ATTR, ref layer._specularColor); LoadLayerVector2FromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TILE_OFFSET_ATTR, ref layer._tileOffset); LoadLayerVector2FromAttribute(session, nodeID, volumeParts[i].id, HEU_Defines.DEFAULT_UNITY_HEIGHTFIELD_TILE_SIZE_ATTR, ref layer._tileSize); // Get the height values from Houdini and find the min and max height range. if (!HEU_GeometryUtility.GetHeightfieldValues(session, volumeInfo.xLength, volumeInfo.yLength, nodeID, volumeParts[i].id, ref layer._rawHeights, ref layer._minHeight, ref layer._maxHeight)) { return false; } // TODO: Tried to replace above with this, but it flattens the heights //layer._rawHeights = HEU_GeometryUtility.GetHeightfieldFromPart(_session, nodeID, volumeParts[i].id, "part", volumeInfo.xLength); // 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, nodeID, volumeParts[i].id, "tile", ref tileAttrInfo, ref tileAttrData, session.GetAttributeIntData); int tileIndex = 0; if (tileAttrInfo.exists && tileAttrData.Length == 1) { tileIndex = tileAttrData[0]; } // Add layer based on tile index if (tileIndex >= 0) { HEU_LoadBufferVolume volumeBuffer = null; for(int j = 0; j < volumeBuffers.Count; ++j) { if (volumeBuffers[j]._tileIndex == tileIndex) { volumeBuffer = volumeBuffers[j]; break; } } if (volumeBuffer == null) { volumeBuffer = new HEU_LoadBufferVolume(); volumeBuffer.InitializeBuffer(volumeParts[i].id, volumeName, false, false); volumeBuffer._tileIndex = tileIndex; volumeBuffers.Add(volumeBuffer); } if (bHeightPart) { // Height layer always first layer volumeBuffer._layers.Insert(0, layer); volumeBuffer._heightMapSize = layer._heightMapSize; volumeBuffer._terrainSizeX = layer._terrainSizeX; volumeBuffer._terrainSizeY = layer._terrainSizeY; volumeBuffer._heightRange = (layer._maxHeight - layer._minHeight); } else { volumeBuffer._layers.Add(layer); } } Sleep(); } // Each volume buffer is a self contained terrain tile foreach(HEU_LoadBufferVolume volumeBuffer in volumeBuffers) { List<HEU_LoadBufferVolumeLayer> layers = volumeBuffer._layers; //Debug.LogFormat("Heightfield: tile={0}, layers={1}", tile._tileIndex, layers.Count); int heightMapSize = volumeBuffer._heightMapSize; int numLayers = layers.Count; if (numLayers > 0) { // Convert heightmap values from Houdini to Unity volumeBuffer._heightMap = HEU_GeometryUtility.ConvertHeightMapHoudiniToUnity(heightMapSize, layers[0]._rawHeights, layers[0]._minHeight, layers[0]._maxHeight); Sleep(); // Convert splatmap values from Houdini to Unity. List<float[]> heightFields = new List<float[]>(); for(int m = 1; m < numLayers; ++m) { heightFields.Add(layers[m]._rawHeights); } volumeBuffer._splatMaps = HEU_GeometryUtility.ConvertHeightSplatMapHoudiniToUnity(heightMapSize, heightFields); volumeBuffer._position = new Vector3((volumeBuffer._terrainSizeX + volumeBuffer._layers[0]._minBounds.x), volumeBuffer._layers[0]._minHeight + volumeBuffer._layers[0]._position.y, volumeBuffer._layers[0]._minBounds.z); } } return true; }