private bool LoadLayerFloatFromAttribute(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_NodeId partID, string attrName, ref float floatValue) { HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); float[] attrValues = new float[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, attrName, ref attrInfo, ref attrValues, session.GetAttributeFloatData); if (attrValues != null && attrValues.Length > 0) { floatValue = attrValues[0]; return true; } return false; }
private void LoadLayerVector2FromAttribute(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_NodeId partID, string attrName, ref Vector2 vectorValue) { HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); float[] attrValues = new float[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, attrName, ref attrInfo, ref attrValues, session.GetAttributeFloatData); if (attrValues != null && attrValues.Length == 2) { if (attrInfo.tupleSize == 2) { vectorValue[0] = attrValues[0]; vectorValue[1] = attrValues[1]; } } }
/// <summary> /// Retrieve and set the detail properties from the specified heightfield. /// This includes detail distance, density, and resolution per patch. /// </summary> /// <param name="session">Current Houdini Engine session</param> /// <param name="geoID">Heightfield object</param> /// <param name="partID">Heightfield layer</param> /// <param name="detailProperties">Reference to a HEU_DetailProperties which will /// be populated.</param> public static void PopulateDetailProperties(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, ref HEU_DetailProperties detailProperties) { // Detail distance HAPI_AttributeInfo detailDistanceAttrInfo = new HAPI_AttributeInfo(); int[] detailDistances = new int[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HEIGHTFIELD_DETAIL_DISTANCE, ref detailDistanceAttrInfo, ref detailDistances, session.GetAttributeIntData); // Detail density HAPI_AttributeInfo detailDensityAttrInfo = new HAPI_AttributeInfo(); float[] detailDensity = new float[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HEIGHTFIELD_DETAIL_DENSITY, ref detailDensityAttrInfo, ref detailDensity, session.GetAttributeFloatData); // 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, geoID, 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 (detailDensityAttrInfo.exists && detailDensity.Length >= arraySize) { detailProperties._detailDensity = detailDensity[0]; } if (resolutionPatchAttrInfo.exists && resolutionPatches.Length >= arraySize) { detailProperties._detailResolutionPerPatch = resolutionPatches[0]; } }
/// <summary> /// Returns the Unity script attribute value, if found, on the specified geo's part. /// The attribute must be of string type, and owned by detail. /// </summary> /// <param name="session">Session that the asset resides in</param> /// <param name="geoID">The geo node's ID</param> /// <param name="partID">The part's ID</param> /// <returns>The name of the Unity script, or null if not found</returns> public static string GetUnityScriptAttributeValue(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID) { HAPI_AttributeInfo scriptAttributeInfo = new HAPI_AttributeInfo(); int[] scriptAttr = new int[0]; string scriptString = null; HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_PluginSettings.UnityScriptAttributeName, ref scriptAttributeInfo, ref scriptAttr, session.GetAttributeStringData); if (scriptAttributeInfo.exists) { if (scriptAttr.Length > 0) { scriptString = HEU_SessionManager.GetString(scriptAttr[0]); } } return scriptString; }
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, 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; } } }
/// <summary> /// Returns the Unity script attribute value, if found, on the specified geo's part. /// The attribute must be of string type, and owned by detail. /// </summary> /// <param name="session">Session that the asset resides in</param> /// <param name="geoID">The geo node's ID</param> /// <param name="partID">The part's ID</param> /// <returns>The name of the Unity script, or null if not found</returns> public static string GetUnityScriptAttributeValue(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID) { HAPI_AttributeInfo scriptAttributeInfo = new HAPI_AttributeInfo(); int[] scriptAttr = new int[0]; string scriptString = null; HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_PluginSettings.UnityScriptAttributeName, ref scriptAttributeInfo, ref scriptAttr, session.GetAttributeStringData); if (scriptAttributeInfo.exists) { if (scriptAttributeInfo.owner != HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL) { Debug.LogWarningFormat("Houdini Engine for Unity only supports {0} as detail attributes!", HEU_PluginSettings.UnityScriptAttributeName); } else if (scriptAttr.Length > 0) { scriptString = HEU_SessionManager.GetString(scriptAttr[0]); } } return scriptString; }
/// <summary> /// Assign Unity layer to the GameObject if found on the part as attribute. /// </summary> /// <param name="session"></param> /// <param name="geoID"></param> /// <param name="partID"></param> /// <param name="gameObject"></param> public static void AssignUnityLayer(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, GameObject gameObject) { HAPI_AttributeInfo layerAttrInfo = new HAPI_AttributeInfo(); int[] layerAttr = new int[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_PluginSettings.UnityLayerAttributeName, ref layerAttrInfo, ref layerAttr, session.GetAttributeStringData); if (layerAttrInfo.exists) { string layerStr = HEU_SessionManager.GetString(layerAttr[0]); if (layerStr.Length > 0) { int layer = LayerMask.NameToLayer(layerStr); if (layer < 0) { Debug.LogWarningFormat("Unity layer '{0}' does not exist for current project. Add the layer in order to use it!", layerStr); } else { HEU_GeneralUtility.SetLayer(gameObject, layer, true); } } } }
/// <summary> /// Assign the Unity tag to the GameObject if found on the part as attribute. /// </summary> public static void AssignUnityTag(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, GameObject gameObject) { HAPI_AttributeInfo tagAttrInfo = new HAPI_AttributeInfo(); int[] tagAttr = new int[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_PluginSettings.UnityTagAttributeName, ref tagAttrInfo, ref tagAttr, session.GetAttributeStringData); if (tagAttrInfo.exists) { string tag = HEU_SessionManager.GetString(tagAttr[0]); if (tag.Length > 0) { try { SetTag(gameObject, tag, true); } catch (Exception ex) { Debug.LogWarning("Tag exception: " + ex.ToString()); Debug.LogWarningFormat("Unity tag '{0}' does not exist for current project. Add the tag in order to use it!", tag); } } } }
/// <summary> /// Returns the single string value from Attribute with given name and owner type, or null if failed. /// </summary> /// <param name="session">Houdini Engine session to query</param> /// <param name="geoID">The geometry ID in Houdini</param> /// <param name="partID">The part ID in Houdini</param> /// <param name="attrName">Name of the attribute to query</param> /// <param name="attrOwner">Owner type of the attribute</param> /// <returns>Valid string if successful, otherwise returns null</returns> public static string GetAttributeStringValueSingle(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, string attrName, HAPI_AttributeOwner attrOwner) { if (string.IsNullOrEmpty(attrName)) { return null; } HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); int[] stringHandle = new int[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, attrName, ref attrInfo, ref stringHandle, session.GetAttributeStringData); if (attrInfo.exists) { if (attrInfo.owner != attrOwner) { Debug.LogWarningFormat("Expected {0} attribute owner for attribute {1} but got {2}!", attrOwner, attrName, attrInfo.owner); } else if (stringHandle.Length > 0) { return HEU_SessionManager.GetString(stringHandle[0]); } } return null; }
private bool LoadLayerColorFromAttribute(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_NodeId partID, string attrName, ref Color colorValue) { HAPI_AttributeInfo attrInfo = new HAPI_AttributeInfo(); float[] attrValues = new float[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, attrName, ref attrInfo, ref attrValues, session.GetAttributeFloatData); if (attrValues != null && attrValues.Length >= 3 && attrInfo.tupleSize >= 3) { colorValue[0] = attrValues[0]; colorValue[1] = attrValues[1]; colorValue[2] = attrValues[2]; if (attrInfo.tupleSize == 4 && attrValues.Length == 4) { colorValue[3] = attrValues[3]; } else { colorValue[3] = 1f; } return true; } return false; }
/// <summary> /// Grab the scatter data for the given part. /// This finds the properties of TreeInstances via attributes. /// </summary> /// <param name="session">Houdini session</param> /// <param name="geoID">Geometry ID</param> /// <param name="partID">Part (volume layer) ID</param> /// <param name="pointCount">Number of expected scatter points</param> public static void PopulateScatterTrees(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, int pointCount, ref HEU_VolumeScatterTrees scatterTrees) { // The HEU_VolumeScatterTrees might already have been created when the volumecache was queried. // The "height" layer might have had prototype data which is set in _scatterTrees. if (scatterTrees == null) { scatterTrees = new HEU_VolumeScatterTrees(); } // Get prototype indices. These indices refer to _scatterTrees._treePrototypes. HAPI_AttributeInfo indicesAttrInfo = new HAPI_AttributeInfo(); int[] indices = new int[0]; if (HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HEIGHTFIELD_TREEINSTANCE_PROTOTYPEINDEX, ref indicesAttrInfo, ref indices, session.GetAttributeIntData)) { if (indices != null && indices.Length == pointCount) { scatterTrees._prototypeIndices = indices; } else { Debug.LogWarningFormat("Scatter instance index count for attribute {0} is not valid. Expected {1} but got {2}", HEU_Defines.HEIGHTFIELD_TREEINSTANCE_PROTOTYPEINDEX, pointCount, (indices != null ? indices.Length : 0)); } } // Using the UVs as position of the instances, since they are properly mapped to the terrain tile. // Also getting other attributes for the TreeInstances, if they are set. HAPI_AttributeInfo uvAttrInfo = new HAPI_AttributeInfo(); float[] uvs = new float[0]; if (!HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HAPI_ATTRIB_UV, ref uvAttrInfo, ref uvs, session.GetAttributeFloatData)) { Debug.LogWarning("UVs for scatter instances not found or valid."); } if (uvs != null && uvs.Length == (pointCount * uvAttrInfo.tupleSize)) { // Get height scales HAPI_AttributeInfo heightAttrInfo = new HAPI_AttributeInfo(); float[] heightscales = new float[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HEIGHTFIELD_TREEINSTANCE_HEIGHTSCALE, ref heightAttrInfo, ref heightscales, session.GetAttributeFloatData); // Get width scales HAPI_AttributeInfo widthAttrInfo = new HAPI_AttributeInfo(); float[] widthscales = new float[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HEIGHTFIELD_TREEINSTANCE_WIDTHSCALE, ref widthAttrInfo, ref widthscales, session.GetAttributeFloatData); // Get orientation HAPI_AttributeInfo orientAttrInfo = new HAPI_AttributeInfo(); float[] orients = new float[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HAPI_ATTRIB_ORIENT, ref orientAttrInfo, ref orients, session.GetAttributeFloatData); // Get color HAPI_AttributeInfo colorAttrInfo = new HAPI_AttributeInfo(); float[] colors = new float[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HAPI_ATTRIB_COLOR, ref colorAttrInfo, ref colors, session.GetAttributeFloatData); // Get lightmap color HAPI_AttributeInfo lightmapColorAttrInfo = new HAPI_AttributeInfo(); float[] lightmapColors = new float[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, HEU_Defines.HEIGHTFIELD_TREEINSTANCE_LIGHTMAPCOLOR, ref lightmapColorAttrInfo, ref lightmapColors, session.GetAttributeFloatData); scatterTrees._positions = new Vector3[pointCount]; if (heightAttrInfo.exists && (heightscales.Length == pointCount)) { scatterTrees._heightScales = heightscales; } if (widthAttrInfo.exists && (widthscales.Length == pointCount)) { scatterTrees._widthScales = widthscales; } if (orientAttrInfo.exists && (orients.Length == orientAttrInfo.tupleSize * pointCount)) { scatterTrees._rotations = new float[pointCount]; } if (colorAttrInfo.exists && (colors.Length == colorAttrInfo.tupleSize * pointCount)) { scatterTrees._colors = new Color32[pointCount]; } if (lightmapColorAttrInfo.exists && (lightmapColors.Length == lightmapColorAttrInfo.tupleSize * pointCount)) { scatterTrees._lightmapColors = new Color32[pointCount]; } for (int i = 0; i < pointCount; ++i) { scatterTrees._positions[i] = new Vector3(1.0f - uvs[i * uvAttrInfo.tupleSize + 1], 0, uvs[i * uvAttrInfo.tupleSize + 0]); if (scatterTrees._colors != null) { scatterTrees._colors[i] = new Color32((byte)(colors[i * colorAttrInfo.tupleSize + 0] * 255), (byte)(colors[i * colorAttrInfo.tupleSize + 1] * 255), (byte)(colors[i * colorAttrInfo.tupleSize + 2] * 255), (byte)(colors[i * colorAttrInfo.tupleSize + 3] * 255)); } if (scatterTrees._lightmapColors != null) { scatterTrees._lightmapColors[i] = new Color32((byte)(lightmapColors[i * lightmapColorAttrInfo.tupleSize + 0] * 255), (byte)(lightmapColors[i * lightmapColorAttrInfo.tupleSize + 1] * 255), (byte)(lightmapColors[i * lightmapColorAttrInfo.tupleSize + 2] * 255), (byte)(lightmapColors[i * lightmapColorAttrInfo.tupleSize + 3] * 255)); } if (scatterTrees._rotations != null) { Quaternion quaternion = new Quaternion( orients[i * orientAttrInfo.tupleSize + 0], orients[i * orientAttrInfo.tupleSize + 1], orients[i * orientAttrInfo.tupleSize + 2], orients[i * orientAttrInfo.tupleSize + 3]); Vector3 euler = quaternion.eulerAngles; euler.y = -euler.y; euler.z = -euler.z; scatterTrees._rotations[i] = euler.y * Mathf.Deg2Rad; } } } }
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; }
public void UpdateLayerFromPart(HEU_SessionBase session, HEU_PartData part) { HEU_GeoNode geoNode = part.ParentGeoNode; HAPI_VolumeInfo volumeInfo = new HAPI_VolumeInfo(); bool bResult = session.GetVolumeInfo(geoNode.GeoID, part.PartID, ref volumeInfo); if (!bResult || volumeInfo.tupleSize != 1 || volumeInfo.zLength != 1 || volumeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT) { return; } string volumeName = HEU_SessionManager.GetString(volumeInfo.nameSH, session); part.SetVolumeLayerName(volumeName); //Debug.LogFormat("Part name: {0}, GeoName: {1}, Volume Name: {2}, Display: {3}", part.PartName, geoNode.GeoName, volumeName, geoNode.Displayable); HEU_VolumeLayer.HFLayerType layerType = GetHeightfieldLayerType(session, geoNode.GeoID, part.PartID, volumeName); HEU_VolumeLayer layer = GetLayer(volumeName); if (layer == null) { layer = new HEU_VolumeLayer(); layer._layerName = volumeName; if (layerType == HEU_VolumeLayer.HFLayerType.HEIGHT) { _layers.Insert(0, layer); } else if(layerType != HEU_VolumeLayer.HFLayerType.MASK) { _layers.Add(layer); } } layer._part = part; layer._xLength = volumeInfo.xLength; layer._yLength = volumeInfo.yLength; layer._layerType = layerType; if (layerType != HEU_VolumeLayer.HFLayerType.MASK) { GetPartLayerAttributes(session, geoNode.GeoID, part.PartID, layer); } if (layerType != HEU_VolumeLayer.HFLayerType.HEIGHT) { // Non-height parts don't have any outputs as they are simply layers carrying info part.DestroyAllData(); } else { // Height part // Might contain terrain properties via attributes (i.e. not layer specific, but for entire terrain) // Scatter Tree Prototypes List<HEU_TreePrototypeInfo> treePrototypeInfos = HEU_TerrainUtility.GetTreePrototypeInfosFromPart(session, geoNode.GeoID, part.PartID); if (treePrototypeInfos != null) { if (_scatterTrees == null) { _scatterTrees = new HEU_VolumeScatterTrees(); } _scatterTrees._treePrototypInfos = treePrototypeInfos; } // Detail distance HAPI_AttributeInfo detailDistanceAttrInfo = new HAPI_AttributeInfo(); int[] detailDistances = new int[0]; HEU_GeneralUtility.GetAttribute(session, geoNode.GeoID, part.PartID, HEU_Defines.HEIGHTFIELD_DETAIL_DISTANCE, ref detailDistanceAttrInfo, ref detailDistances, session.GetAttributeIntData); // Scatter Detail Resolution Per Patch (note that Detail Resolution comes from HF layer size) HAPI_AttributeInfo resolutionPatchAttrInfo = new HAPI_AttributeInfo(); int[] resolutionPatches = new int[0]; HEU_GeneralUtility.GetAttribute(session, geoNode.GeoID, part.PartID, HEU_Defines.HEIGHTFIELD_DETAIL_RESOLUTION_PER_PATCH, ref resolutionPatchAttrInfo, ref resolutionPatches, session.GetAttributeIntData); if (_detailProperties == null) { _detailProperties = new HEU_DetailProperties(); } // Unity only supports 1 set of detail resolution properties per terrain int arraySize = 1; if (detailDistanceAttrInfo.exists && detailDistances.Length >= arraySize) { _detailProperties._detailDistance = detailDistances[0]; } if (resolutionPatchAttrInfo.exists && resolutionPatches.Length >= arraySize) { _detailProperties._detailResolutionPerPatch = resolutionPatches[0]; } } if (!_updatedLayers.Contains(layer)) { if (layerType == HEU_VolumeLayer.HFLayerType.HEIGHT) { _updatedLayers.Insert(0, layer); } else if (layerType != HEU_VolumeLayer.HFLayerType.MASK) { _updatedLayers.Add(layer); } } }
public void 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; } }
// LOGIC ----------------------------------------------------------------------------------------------------- public static List<HEU_VolumeCache> UpdateVolumeCachesFromParts(HEU_SessionBase session, HEU_GeoNode ownerNode, List<HEU_PartData> volumeParts, List<HEU_VolumeCache> volumeCaches) { HEU_HoudiniAsset parentAsset = ownerNode.ParentAsset; foreach (HEU_VolumeCache cache in volumeCaches) { // Remove current volume caches from parent asset. // These get added back in below. parentAsset.RemoveVolumeCache(cache); // Mark the cache for updating cache.StartUpdateLayers(); } // This will keep track of volume caches still in use List<HEU_VolumeCache> updatedCaches = new List<HEU_VolumeCache>(); int numParts = volumeParts.Count; for (int i = 0; i < numParts; ++i) { // 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, ownerNode.GeoID, volumeParts[i].PartID, HEU_Defines.HAPI_HEIGHTFIELD_TILE_ATTR, ref tileAttrInfo, ref tileAttrData, session.GetAttributeIntData); if (tileAttrData != null && tileAttrData.Length > 0) { //Debug.LogFormat("Tile: {0}", tileAttrData[0]); int tile = tileAttrData[0]; HEU_VolumeCache volumeCache = null; // Find cache in updated list for (int j = 0; j < updatedCaches.Count; ++j) { if (updatedCaches[j] != null && updatedCaches[j].TileIndex == tile) { volumeCache = updatedCaches[j]; break; } } if (volumeCache != null) { volumeCache.UpdateLayerFromPart(session, volumeParts[i]); // Skip adding new cache since already found in updated list continue; } // Find existing cache in old list if (volumeCaches != null && volumeCaches.Count > 0) { for(int j = 0; j < volumeCaches.Count; ++j) { if (volumeCaches[j] != null && volumeCaches[j].TileIndex == tile) { volumeCache = volumeCaches[j]; break; } } } // Create new cache for this tile if not found if (volumeCache == null) { volumeCache = ScriptableObject.CreateInstance<HEU_VolumeCache>(); volumeCache.Initialize(ownerNode, tile); volumeCache.StartUpdateLayers(); } volumeCache.UpdateLayerFromPart(session, volumeParts[i]); if (!updatedCaches.Contains(volumeCache)) { updatedCaches.Add(volumeCache); } } else { // No tile index. Most likely a single terrain tile. HEU_VolumeCache volumeCache = null; if (updatedCaches.Count == 0) { // Create a single volume cache, or use existing if it was just 1. // If more than 1 volume cache exists, this will recreate a single one if (volumeCaches == null || volumeCaches.Count != 1) { volumeCache = ScriptableObject.CreateInstance<HEU_VolumeCache>(); volumeCache.Initialize(ownerNode, 0); volumeCache.StartUpdateLayers(); } else if (volumeCaches.Count == 1) { // Keep the single volumecache volumeCache = volumeCaches[0]; } if (!updatedCaches.Contains(volumeCache)) { updatedCaches.Add(volumeCache); } } else { // Reuse the updated cache volumeCache = updatedCaches[0]; } volumeCache.UpdateLayerFromPart(session, volumeParts[i]); } } foreach (HEU_VolumeCache cache in updatedCaches) { // Add to parent for UI and preset parentAsset.AddVolumeCache(cache); // Finish update by keeping just the layers in use for each volume cache. cache.FinishUpdateLayers(); } return updatedCaches; }
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; }
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); } }
/// <summary> /// Generates object instances. /// Skips parts that already have their instances generated. /// </summary> /// <param name="session">Active session to use</param> public void GenerateObjectInstances(HEU_SessionBase session) { if (!IsInstancer()) { Debug.LogErrorFormat("Generate object instances called on a non-instancer object {0} for asset {1}!", ObjectName, ParentAsset.AssetName); return; } //Debug.LogFormat("Generate Object Instances:: id={5}, name={0}, isInstancer={1}, isInstanced={2}, instancePath={3}, instanceId={4}", HEU_SessionManager.GetString(_objectInfo.nameSH, session), // _objectInfo.isInstancer, _objectInfo.isInstanced, HEU_SessionManager.GetString(_objectInfo.objectInstancePathSH, session), _objectInfo.objectToInstanceId, _objectInfo.nodeId); // Is this a Houdini attribute instancer? string instanceAttrName = HEU_PluginSettings.InstanceAttr; string unityInstanceAttrName = HEU_PluginSettings.UnityInstanceAttr; string instancePrefixAttrName = HEU_Defines.DEFAULT_INSTANCE_PREFIX_ATTR; HAPI_AttributeInfo instanceAttrInfo = new HAPI_AttributeInfo(); HAPI_AttributeInfo unityInstanceAttrInfo = new HAPI_AttributeInfo(); HAPI_AttributeInfo instancePrefixAttrInfo = new HAPI_AttributeInfo(); int numGeos = _geoNodes.Count; for (int i = 0; i < numGeos; ++i) { if (_geoNodes[i].Displayable) { List<HEU_PartData> parts = _geoNodes[i].GetParts(); int numParts = parts.Count; for (int j = 0; j < numParts; ++j) { if (parts[j].ObjectInstancesBeenGenerated || parts[j].IsPartVolume()) { // This prevents instances being created unnecessarily (e.g. part hasn't changed since last cook). // Or for volumes that might have instance attributes. continue; } HEU_GeneralUtility.GetAttributeInfo(session, _geoNodes[i].GeoID, parts[j].PartID, instanceAttrName, ref instanceAttrInfo); HEU_GeneralUtility.GetAttributeInfo(session, _geoNodes[i].GeoID, parts[j].PartID, unityInstanceAttrName, ref unityInstanceAttrInfo); string[] instancePrefixes = null; HEU_GeneralUtility.GetAttributeInfo(session, _geoNodes[i].GeoID, parts[j].PartID, instancePrefixAttrName, ref instancePrefixAttrInfo); if (instancePrefixAttrInfo.exists) { instancePrefixes = HEU_GeneralUtility.GetAttributeStringData(session, _geoNodes[i].GeoID, parts[j].PartID, instancePrefixAttrName, ref instancePrefixAttrInfo); } // Must clear out instances, as otherwise we get duplicates parts[j].ClearInstances(); // Clear out invalid object instance infos that no longer have any valid parts parts[j].ClearInvalidObjectInstanceInfos(); if (instanceAttrInfo.exists) { // Object instancing via Houdini instance attribute parts[j].GenerateInstancesFromObjectIds(session, instancePrefixes); } else if (unityInstanceAttrInfo.exists) { // Object instancing via existing Unity object (path from point attribute) // Attribute owner type determines whether to use single instanced object (detail) or multiple (point) if (unityInstanceAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_POINT) { parts[j].GenerateInstancesFromUnityAssetPathAttribute(session, unityInstanceAttrName); } else if (unityInstanceAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL) { bool bInstanced = false; int[] scriptAttr = new int[unityInstanceAttrInfo.count]; HEU_GeneralUtility.GetAttribute(session, _geoNodes[i].GeoID, parts[j].PartID, unityInstanceAttrName, ref unityInstanceAttrInfo, ref scriptAttr, session.GetAttributeStringData); if (unityInstanceAttrInfo.exists) { string assetPath = HEU_SessionManager.GetString(scriptAttr[0]); if (!string.IsNullOrEmpty(assetPath)) { parts[j].GenerateInstancesFromUnityAssetPath(session, assetPath, instancePrefixes); bInstanced = true; } } if (!bInstanced) { Debug.LogWarningFormat("Unable to get instanced object path from detail instance attribute!"); } } else { // Other attribute owned types are unsupported. // Originally had a warning here, but unnecessary as in some cases (e.g. heightfield attrbiutes) the // attribute owner could be changed in HAPI. } } else { // Standard object instancing via single Houdini object if (_objectInfo.objectToInstanceId == HEU_Defines.HEU_INVALID_NODE_ID) { Debug.LogAssertionFormat("Invalid object ID {0} used for object instancing. " + "Make sure to turn on Full point instancing and set the correct Instance Object.", _objectInfo.objectToInstanceId); continue; } parts[j].GenerateInstancesFromObjectID(session, _objectInfo.objectToInstanceId, instancePrefixes); } } } } }
public void SyncAllAttributesFrom(HEU_SessionBase session, HEU_HoudiniAsset asset, HAPI_NodeId geoID, ref HAPI_PartInfo partInfo, GameObject outputGameObject) { _geoID = geoID; _partID = partInfo.id; HAPI_GeoInfo geoInfo = new HAPI_GeoInfo(); if(session.GetGeoInfo(_geoID, ref geoInfo)) { _geoName = HEU_SessionManager.GetString(geoInfo.nameSH, session); } if (outputGameObject != null) { _outputTransform = outputGameObject.transform; } // Need the vertex list of indices to map the positions to vertex colors _vertexIndices = new int[partInfo.vertexCount]; if (!HEU_GeneralUtility.GetArray2Arg(geoID, partInfo.id, session.GetVertexList, _vertexIndices, 0, partInfo.vertexCount)) { return; } // Note that this currently only supports point attributes int attributePointCount = partInfo.attributeCounts[(int)HAPI_AttributeOwner.HAPI_ATTROWNER_POINT]; string[] pointAttributeNames = new string[attributePointCount]; if(!session.GetAttributeNames(geoID, partInfo.id, HAPI_AttributeOwner.HAPI_ATTROWNER_POINT, ref pointAttributeNames, attributePointCount)) { Debug.LogErrorFormat("Failed to sync attributes. Unable to retrieve attribute names."); return; } // Create new list of attributes. We'll move existing attributes that are still in use as we find them. List<HEU_AttributeData> newAttributeDatas = new List<HEU_AttributeData>(); foreach (string pointAttributeName in pointAttributeNames) { if(string.IsNullOrEmpty(pointAttributeName)) { continue; } // Get position attribute values separately. Used for painting and editing points in 3D scene. HAPI_AttributeInfo pointAttributeInfo = new HAPI_AttributeInfo(); if(session.GetAttributeInfo(geoID, partInfo.id, pointAttributeName, HAPI_AttributeOwner.HAPI_ATTROWNER_POINT, ref pointAttributeInfo)) { if (pointAttributeName.Equals(HEU_Defines.HAPI_ATTRIB_POSITION)) { if (pointAttributeInfo.storage != HAPI_StorageType.HAPI_STORAGETYPE_FLOAT) { Debug.LogErrorFormat("Expected float type for position attribute, but got {0}", pointAttributeInfo.storage); return; } _positionAttributeValues = new Vector3[pointAttributeInfo.count]; float[] data = new float[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partInfo.id, pointAttributeName, ref pointAttributeInfo, ref data, session.GetAttributeFloatData); for (int i = 0; i < pointAttributeInfo.count; ++i) { _positionAttributeValues[i] = new Vector3(-data[i * pointAttributeInfo.tupleSize + 0], data[i * pointAttributeInfo.tupleSize + 1], data[i * pointAttributeInfo.tupleSize + 2]); } // We don't let position attributes be editted (for now anyway) continue; } HEU_AttributeData attrData = GetAttributeData(pointAttributeName); if (attrData == null) { // Attribute data not found. Create it. attrData = CreateAttribute(pointAttributeName, ref pointAttributeInfo); //Debug.LogFormat("Created attribute data: {0}", pointAttributeName); } // Add to new list. newAttributeDatas.Add(attrData); // Sync the attribute info to data. PopulateAttributeData(session, geoID, partInfo.id, attrData, ref pointAttributeInfo); if(pointAttributeName.Equals(HEU_Defines.HAPI_ATTRIB_COLOR) || pointAttributeInfo.typeInfo == HAPI_AttributeTypeInfo.HAPI_ATTRIBUTE_TYPE_COLOR) { _hasColorAttribute = true; } } else { // Failed to get point attribute info! } } // Overwriting the old list with the new should automatically remove unused attribute datas. _attributeDatas = newAttributeDatas; }
public bool PopulateGeometryData(HEU_SessionBase session) { // Get vertex position HAPI_AttributeInfo posAttrInfo = new HAPI_AttributeInfo(); _posAttr = new float[0]; HEU_GeneralUtility.GetAttribute(session, GeoID, PartID, HEU_Defines.HAPI_ATTRIB_POSITION, ref posAttrInfo, ref _posAttr, session.GetAttributeFloatData); if (!posAttrInfo.exists) { return false; } else if (posAttrInfo.owner != HAPI_AttributeOwner.HAPI_ATTROWNER_POINT) { Debug.LogErrorFormat("{0} only supports position as POINT attribute. Position attribute of {1} type not supported!", HEU_Defines.HEU_PRODUCT_NAME, posAttrInfo.owner); return false; } // Get UV attributes _uvAttrInfo = new HAPI_AttributeInfo(); _uvAttrInfo.tupleSize = 2; _uvAttr = new float[0]; HEU_GeneralUtility.GetAttribute(session, GeoID, PartID, HEU_Defines.HAPI_ATTRIB_UV, ref _uvAttrInfo, ref _uvAttr, session.GetAttributeFloatData); // Get UV2 attributes _uv2AttrInfo = new HAPI_AttributeInfo(); _uv2AttrInfo.tupleSize = 2; _uv2Attr = new float[0]; HEU_GeneralUtility.GetAttribute(session, GeoID, PartID, HEU_Defines.HAPI_ATTRIB_UV2, ref _uv2AttrInfo, ref _uv2Attr, session.GetAttributeFloatData); // Get UV3 attributes _uv3AttrInfo = new HAPI_AttributeInfo(); _uv3AttrInfo.tupleSize = 2; _uv3Attr = new float[0]; HEU_GeneralUtility.GetAttribute(session, GeoID, PartID, HEU_Defines.HAPI_ATTRIB_UV3, ref _uv3AttrInfo, ref _uv3Attr, session.GetAttributeFloatData); // Get normal attributes _normalAttrInfo = new HAPI_AttributeInfo(); _normalAttr = new float[0]; HEU_GeneralUtility.GetAttribute(session, GeoID, PartID, HEU_Defines.HAPI_ATTRIB_NORMAL, ref _normalAttrInfo, ref _normalAttr, session.GetAttributeFloatData); // Get colour attributes _colorAttrInfo = new HAPI_AttributeInfo(); _colorAttr = new float[0]; HEU_GeneralUtility.GetAttribute(session, GeoID, PartID, HEU_Defines.HAPI_ATTRIB_COLOR, ref _colorAttrInfo, ref _colorAttr, session.GetAttributeFloatData); // Get alpha attributes _alphaAttrInfo = new HAPI_AttributeInfo(); _alphaAttr = new float[0]; HEU_GeneralUtility.GetAttribute(session, GeoID, PartID, HEU_Defines.HAPI_ATTRIB_ALPHA, ref _alphaAttrInfo, ref _alphaAttr, session.GetAttributeFloatData); // Get tangent attributes _tangentAttrInfo = new HAPI_AttributeInfo(); _tangentAttr = new float[0]; HEU_GeneralUtility.GetAttribute(session, GeoID, PartID, HEU_Defines.HAPI_ATTRIB_TANGENT, ref _tangentAttrInfo, ref _tangentAttr, session.GetAttributeFloatData); // Warn user since we are splitting points by attributes, might prevent some attrributes // to be transferred over properly if (_normalAttrInfo.exists && _normalAttrInfo.owner != HAPI_AttributeOwner.HAPI_ATTROWNER_POINT && _normalAttrInfo.owner != HAPI_AttributeOwner.HAPI_ATTROWNER_VERTEX) { Debug.LogWarningFormat("{0}: Normals are not declared as point or vertex attributes.\nSet them as per point or vertices in HDA.", _partName); } if (_tangentAttrInfo.exists && _tangentAttrInfo.owner != HAPI_AttributeOwner.HAPI_ATTROWNER_POINT && _tangentAttrInfo.owner != HAPI_AttributeOwner.HAPI_ATTROWNER_VERTEX) { Debug.LogWarningFormat("{0}: Tangents are not declared as point or vertex attributes.\nSet them as per point or vertices in HDA.", _partName); } if (_colorAttrInfo.exists && _colorAttrInfo.owner != HAPI_AttributeOwner.HAPI_ATTROWNER_POINT && _colorAttrInfo.owner != HAPI_AttributeOwner.HAPI_ATTROWNER_VERTEX) { Debug.LogWarningFormat("{0}: Colours are not declared as point or vertex attributes." + "\nCurrently set as owner type {1}. Set them as per point or vertices in HDA.", _partName, _colorAttrInfo.owner); } _groups = HEU_SessionManager.GetGroupNames(GeoID, HAPI_GroupType.HAPI_GROUPTYPE_PRIM); _allCollisionVertexList = new int[_vertexList.Length]; _allCollisionFaceIndices = new int[_partInfo.faceCount]; _hasGroupGeometry = false; // We go through each group, building up a triangle list of indices that belong to it // For strictly colliders (ie. non-rendering), we only create geometry colliders for (int g = 0; g < _groups.Length; ++g) { string groupName = _groups[g]; // Query HAPI to get the group membership. // This is returned as an array of 1s for vertices that belong to this group. int[] membership = null; HEU_SessionManager.GetGroupMembership(session, GeoID, PartID, HAPI_GroupType.HAPI_GROUPTYPE_PRIM, groupName, ref membership); bool bIsCollidable = groupName.Contains(HEU_PluginSettings.CollisionGroupName); bool bIsRenderCollidable = groupName.Contains(HEU_PluginSettings.RenderedCollisionGroupName); if (bIsCollidable || bIsRenderCollidable) { // Extract vertex indices for this collision group int[] groupVertexList = new int[_vertexList.Length]; groupVertexList.Init<int>(-1); int groupVertexListCount = 0; List<int> allFaceList = new List<int>(); for (int f = 0; f < membership.Length; ++f) { if (membership[f] > 0) { // This face is a member of the specified group allFaceList.Add(f); groupVertexList[f * 3 + 0] = _vertexList[f * 3 + 0]; groupVertexList[f * 3 + 1] = _vertexList[f * 3 + 1]; groupVertexList[f * 3 + 2] = _vertexList[f * 3 + 2]; // Mark vertices as used _allCollisionVertexList[f * 3 + 0] = 1; _allCollisionVertexList[f * 3 + 1] = 1; _allCollisionVertexList[f * 3 + 2] = 1; // Mark face as used _allCollisionFaceIndices[f] = 1; groupVertexListCount += 3; } } if (groupVertexListCount > 0) { _groupSplitVertexIndices.Add(groupName, groupVertexList); _groupSplitFaceIndices.Add(groupName, allFaceList); _hasGroupGeometry = true; //Debug.Log("Adding collision group: " + groupName + " with index count: " + _groupVertexList.Length); } } } if (_hasGroupGeometry) { // Construct vertex list for all other vertices that are not part of collision geometry int[] remainingGroupSplitFaces = new int[_vertexList.Length]; remainingGroupSplitFaces.Init<int>(-1); bool bMainSplitGroup = false; List<int> remainingGroupSplitFaceIndices = new List<int>(); for (int cv = 0; cv < _allCollisionVertexList.Length; ++cv) { if (_allCollisionVertexList[cv] == 0) { // Unused index, so add it to unused vertex list remainingGroupSplitFaces[cv] = _vertexList[cv]; bMainSplitGroup = true; } } for (int cf = 0; cf < _allCollisionFaceIndices.Length; ++cf) { if (_allCollisionFaceIndices[cf] == 0) { remainingGroupSplitFaceIndices.Add(cf); } } if (bMainSplitGroup) { _groupSplitVertexIndices.Add(HEU_Defines.HEU_DEFAULT_GEO_GROUP_NAME, remainingGroupSplitFaces); _groupSplitFaceIndices.Add(HEU_Defines.HEU_DEFAULT_GEO_GROUP_NAME, remainingGroupSplitFaceIndices); //Debug.Log("Adding remaining group with index count: " + remainingGroupSplitFaces.Length); } } else { _groupSplitVertexIndices.Add(HEU_Defines.HEU_DEFAULT_GEO_GROUP_NAME, _vertexList); List<int> allFaces = new List<int>(); for (int f = 0; f < _partInfo.faceCount; ++f) { allFaces.Add(f); } _groupSplitFaceIndices.Add(HEU_Defines.HEU_DEFAULT_GEO_GROUP_NAME, allFaces); //Debug.Log("Adding single non-group with index count: " + _vertexList.Length); } // We'll be generating the normals if they're not provided by Houdini // For every vertex, we'll hold list of other vertices that it shares faces with (ie. connected to) _sharedNormalIndices = null; _cosineThreshold = 0f; if (!_normalAttrInfo.exists) { _sharedNormalIndices = new List<VertexEntry>[_vertexList.Length]; _cosineThreshold = Mathf.Cos(HEU_PluginSettings.NormalGenerationThresholdAngle * Mathf.Deg2Rad); for (int i = 0; i < _vertexList.Length; ++i) { _sharedNormalIndices[i] = new List<VertexEntry>(); } } return true; }
private void PopulateAttributeData(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, HEU_AttributeData attributeData, ref HAPI_AttributeInfo attributeInfo) { attributeData._attributeInfo = attributeInfo; int tupleSize = attributeInfo.tupleSize; int attributeCount = attributeInfo.count; int arraySize = attributeCount * tupleSize; // First reset arrays if the type had changed since last sync if ((attributeInfo.storage == HAPI_StorageType.HAPI_STORAGETYPE_INT && attributeData._attributeType != HEU_AttributeData.AttributeType.INT) || (attributeInfo.storage == HAPI_StorageType.HAPI_STORAGETYPE_FLOAT && attributeData._attributeType != HEU_AttributeData.AttributeType.FLOAT) || (attributeInfo.storage == HAPI_StorageType.HAPI_STORAGETYPE_STRING && attributeData._attributeType != HEU_AttributeData.AttributeType.STRING)) { // Reset arrays if type is different attributeData._floatValues = null; attributeData._stringValues = null; attributeData._intValues = null; attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID; if(attributeInfo.storage == HAPI_StorageType.HAPI_STORAGETYPE_INT) { attributeData._attributeType = HEU_AttributeData.AttributeType.INT; } else if (attributeInfo.storage == HAPI_StorageType.HAPI_STORAGETYPE_FLOAT) { attributeData._attributeType = HEU_AttributeData.AttributeType.FLOAT; } else if (attributeInfo.storage == HAPI_StorageType.HAPI_STORAGETYPE_STRING) { attributeData._attributeType = HEU_AttributeData.AttributeType.STRING; } } // Make sure the internal array is correctly sized for syncing. if (attributeData._attributeType == HEU_AttributeData.AttributeType.INT) { if (attributeData._intValues == null) { attributeData._intValues = new int[arraySize]; attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID; } else if (attributeData._intValues.Length != arraySize) { System.Array.Resize<int>(ref attributeData._intValues, arraySize); attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID; } attributeData._floatValues = null; attributeData._stringValues = null; if (attributeData._attributeState == HEU_AttributeData.AttributeState.INVALID) { int[] data = new int[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, attributeData._name, ref attributeInfo, ref data, session.GetAttributeIntData); for (int i = 0; i < attributeCount; ++i) { for (int tuple = 0; tuple < tupleSize; ++tuple) { attributeData._intValues[i * tupleSize + tuple] = data[i * tupleSize + tuple]; } } } } else if (attributeData._attributeType == HEU_AttributeData.AttributeType.FLOAT) { if (attributeData._floatValues == null) { attributeData._floatValues = new float[arraySize]; attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID; } else if (attributeData._floatValues.Length != arraySize) { System.Array.Resize<float>(ref attributeData._floatValues, arraySize); attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID; } attributeData._intValues = null; attributeData._stringValues = null; if (attributeData._attributeState == HEU_AttributeData.AttributeState.INVALID) { float[] data = new float[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, attributeData._name, ref attributeInfo, ref data, session.GetAttributeFloatData); for (int i = 0; i < attributeCount; ++i) { for (int tuple = 0; tuple < tupleSize; ++tuple) { attributeData._floatValues[i * tupleSize + tuple] = data[i * tupleSize + tuple]; } } } } else if (attributeData._attributeType == HEU_AttributeData.AttributeType.STRING) { if (attributeData._stringValues == null) { attributeData._stringValues = new string[arraySize]; attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID; } else if (attributeData._stringValues.Length != arraySize) { System.Array.Resize<string>(ref attributeData._stringValues, arraySize); attributeData._attributeState = HEU_AttributeData.AttributeState.INVALID; } attributeData._intValues = null; attributeData._floatValues = null; if (attributeData._attributeState == HEU_AttributeData.AttributeState.INVALID) { HAPI_StringHandle[] data = new HAPI_StringHandle[0]; HEU_GeneralUtility.GetAttribute(session, geoID, partID, attributeData._name, ref attributeInfo, ref data, session.GetAttributeStringData); for (int i = 0; i < attributeCount; ++i) { for (int tuple = 0; tuple < tupleSize; ++tuple) { HAPI_StringHandle stringHandle = data[i * tupleSize + tuple]; attributeData._stringValues[i * tupleSize + tuple] = HEU_SessionManager.GetString(stringHandle, session); } } } } SetAttributeDataSyncd(attributeData); }
public void PopulateUnityMaterialData(HEU_SessionBase session) { // First we look for Unity and Substance material attributes on faces. // We fill up the following dictionary with unique Unity + Substance material information _unityMaterialInfos = new Dictionary<int, HEU_UnityMaterialInfo>(); _unityMaterialAttrInfo = new HAPI_AttributeInfo(); _unityMaterialAttrName = new HAPI_StringHandle[0]; HEU_GeneralUtility.GetAttribute(session, GeoID, PartID, HEU_PluginSettings.UnityMaterialAttribName, ref _unityMaterialAttrInfo, ref _unityMaterialAttrName, session.GetAttributeStringData); // Store a local copy of the actual string values since the indices get overwritten by the next call to session.GetAttributeStringData. // Using a dictionary to only query the unique strings, as doing all of them is very slow and unnecessary. _unityMaterialAttrStringsMap = new Dictionary<HAPI_StringHandle, string>(); foreach (HAPI_StringHandle strHandle in _unityMaterialAttrName) { if (!_unityMaterialAttrStringsMap.ContainsKey(strHandle)) { string materialName = HEU_SessionManager.GetString(strHandle, session); if (string.IsNullOrEmpty(materialName)) { // Warn user of empty string, but add it anyway to our map so we don't keep trying to parse it Debug.LogWarningFormat("Found empty material attribute value for part {0}.", _partName); } _unityMaterialAttrStringsMap.Add(strHandle, materialName); //Debug.LogFormat("Added Unity material: " + materialName); } } _substanceMaterialAttrNameInfo = new HAPI_AttributeInfo(); _substanceMaterialAttrName = new HAPI_StringHandle[0]; HEU_GeneralUtility.GetAttribute(session, GeoID, PartID, HEU_PluginSettings.UnitySubMaterialAttribName, ref _substanceMaterialAttrNameInfo, ref _substanceMaterialAttrName, session.GetAttributeStringData); _substanceMaterialAttrStringsMap = new Dictionary<HAPI_StringHandle, string>(); foreach (HAPI_StringHandle strHandle in _substanceMaterialAttrName) { if (!_substanceMaterialAttrStringsMap.ContainsKey(strHandle)) { string substanceName = HEU_SessionManager.GetString(strHandle, session); if (string.IsNullOrEmpty(substanceName)) { // Warn user of empty string, but add it anyway to our map so we don't keep trying to parse it Debug.LogWarningFormat("Found invalid substance material attribute value ({0}) for part {1}.", _partName, substanceName); } _substanceMaterialAttrStringsMap.Add(strHandle, substanceName); //Debug.LogFormat("Added Substance material: " + substanceName); } } _substanceMaterialAttrIndexInfo = new HAPI_AttributeInfo(); _substanceMaterialAttrIndex = new int[0]; HEU_GeneralUtility.GetAttribute(session, GeoID, PartID, HEU_PluginSettings.UnitySubMaterialIndexAttribName, ref _substanceMaterialAttrIndexInfo, ref _substanceMaterialAttrIndex, session.GetAttributeIntData); if (_unityMaterialAttrInfo.exists) { if (_unityMaterialAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL && _unityMaterialAttrName.Length > 0) { CreateMaterialInfoEntryFromAttributeIndex(this, 0); // Detail unity material attribute means we can treat it as single material _singleFaceUnityMaterial = true; } else { for(HAPI_StringHandle i = 0; i < _unityMaterialAttrName.Length; ++i) { CreateMaterialInfoEntryFromAttributeIndex(this, i); } } } }