public static Texture2D LoadAssetTexture(string path) { Texture2D texture = HEU_MaterialFactory.LoadTexture(path); if (texture == null) { Debug.LogErrorFormat("Unable to find the default Terrain texture at {0}. Make sure this default texture exists.", path); } return texture; }
public static Texture2D LoadDefaultSplatTexture() { Texture2D texture = LoadAssetTexture(HEU_PluginSettings.TerrainSplatTextureDefault); if (texture == null) { texture = HEU_MaterialFactory.WhiteTexture(); } return texture; }
public void GenerateMesh(GameObject inGameObject) { _targetGameObject = inGameObject; MeshFilter meshFilter = _targetGameObject.GetComponent<MeshFilter>(); if(meshFilter == null) { meshFilter = _targetGameObject.AddComponent<MeshFilter>(); } MeshRenderer meshRenderer = _targetGameObject.GetComponent<MeshRenderer>(); if(meshRenderer == null) { meshRenderer = _targetGameObject.AddComponent<MeshRenderer>(); Shader shader = HEU_MaterialFactory.FindPluginShader("LineShader"); meshRenderer.sharedMaterial = new Material(shader); meshRenderer.sharedMaterial.SetColor("_Color", HEU_PluginSettings.LineColor); } Mesh mesh = meshFilter.sharedMesh; if(_points.Count <= 1) { if (mesh != null) { mesh.Clear(); mesh = null; } } else { if (mesh == null) { mesh = new Mesh(); mesh.name = "Curve"; } int[] indices = new int[_vertices.Length]; for(int i = 0; i < _vertices.Length; ++i) { indices[i] = i; } mesh.Clear(); mesh.vertices = _vertices; mesh.SetIndices(indices, MeshTopology.LineStrip, 0); mesh.RecalculateBounds(); mesh.UploadMeshData(false); } meshFilter.sharedMesh = mesh; meshRenderer.enabled = HEU_PluginSettings.Curves_ShowInSceneView; SetEditState(CurveEditState.GENERATED); }
public static Texture2D LoadDefaultSplatTexture() { string defaultSplatTexturePath = HEU_PluginSettings.TerrainSplatTextureDefault; Texture2D texture = HEU_MaterialFactory.LoadTexture(defaultSplatTexturePath); if (texture == null) { Debug.LogErrorFormat("Unable to find the default Terrain texture at {0}. Make sure this default texture exists. Using default white texture instead.", defaultSplatTexturePath); texture = HEU_MaterialFactory.WhiteTexture(); } return texture; }
public static HEU_MaterialData CreateUnitySubstanceMaterialData(int materialKey, string materialPath, string substanceName, int substanceIndex, List<HEU_MaterialData> materialCache, string assetCacheFolderPath) { // Let's make sure we can find the Unity or Substance material first Material material = null; HEU_MaterialData.Source sourceType = HEU_MaterialData.Source.UNITY; if (!string.IsNullOrEmpty(substanceName)) { sourceType = HEU_MaterialData.Source.SUBSTANCE; material = HEU_MaterialFactory.LoadSubstanceMaterialWithName(materialPath, substanceName); } else if (substanceIndex >= 0) { sourceType = HEU_MaterialData.Source.SUBSTANCE; material = HEU_MaterialFactory.LoadSubstanceMaterialWithIndex(materialPath, substanceIndex); } else if (!string.IsNullOrEmpty(materialPath)) { material = HEU_MaterialFactory.LoadUnityMaterial(materialPath); } if (material != null) { HEU_MaterialData materialData = ScriptableObject.CreateInstance<HEU_MaterialData>(); materialData._materialSource = sourceType; materialData._materialKey = materialKey; materialData._material = material; materialCache.Add(materialData); return materialData; } else { // We can't find the material in Unity, so notify user and use a default one which allows to at least get the geometry in. if (string.IsNullOrEmpty(materialPath)) { HEU_Logger.LogWarningFormat("Empty material name found. Using default material."); } else if (materialPath.Contains("Resources/unity_builtin_extra")) { // Built in material. Don't display error. } else { HEU_Logger.LogErrorFormat("Unable to find {0} material {1}. Using a default material instead. Please check material exists in project and reload asset!", sourceType, materialPath); } // The materialKey helps uniquely identify this material for further look ups. But we also need to get a valid file name // to create the default material, so strip out just the file name. string strippedFileName = HEU_Platform.GetFileName(materialPath); return CreateMaterialInCache(materialKey, strippedFileName, HEU_MaterialData.Source.UNITY, false, materialCache, assetCacheFolderPath); } }
public static HEU_MaterialData GetOrCreateDefaultMaterialInCache(HEU_SessionBase session, HAPI_NodeId geoID, HAPI_PartId partID, bool bWriteToFile, List<HEU_MaterialData> materialCache, string assetCacheFolderPath) { string materialName = GenerateDefaultMaterialName(geoID, partID); int materialKey = HEU_MaterialFactory.MaterialNameToKey(materialName); HEU_MaterialData materialData = GetMaterialDataFromCache(materialKey, materialCache); if (materialData == null) { materialData = CreateMaterialInCache(materialKey, materialName, HEU_MaterialData.Source.DEFAULT, bWriteToFile, materialCache, assetCacheFolderPath); } return materialData; }
public static HEU_MaterialData CreateMaterialInCache(int materialKey, string materialName, HEU_MaterialData.Source sourceType, bool bWriteToFile, List<HEU_MaterialData> materialCache, string assetCacheFolderPath) { HEU_MaterialData materialData = ScriptableObject.CreateInstance<HEU_MaterialData>(); materialData._materialSource = sourceType; materialData._materialKey = materialKey; materialData._material = HEU_MaterialFactory.CreateNewHoudiniStandardMaterial(assetCacheFolderPath, materialName, bWriteToFile); materialData._material.name = materialName; materialCache.Add(materialData); return materialData; }
public static Texture2D RenderAndExtractImageToTexture(HEU_SessionBase session, HAPI_MaterialInfo materialInfo, HAPI_ParmId textureParmID, string textureName, string assetCacheFolderPath) { //Debug.LogFormat("Rendering texture {0} with name {1} for material {2} at path {3}", textureParmID, textureName, materialInfo.nodeId, assetCacheFolderPath); Texture2D texture = null; // First we get Houdini to render the texture to an image buffer, then query the buffer over HAPI // Next we convert to PNG, and write out to file in our Assets directory // The reason for querying as a buffer is to workaround a bug with ExtractHoudiniImageToTextureFile // Note: intentionly ignoring any errors as sometimes there aren't any textures if (session.RenderTextureToImage(materialInfo.nodeId, textureParmID, false)) { texture = HEU_MaterialFactory.ExtractHoudiniImageToTextureRaw(session, materialInfo, "C A"); if(texture != null) { texture.name = textureName; // Get the Textures folder in the assetCacheFolderPath. Make sure it exists. assetCacheFolderPath = HEU_AssetDatabase.AppendTexturesPathToAssetFolder(assetCacheFolderPath); HEU_AssetDatabase.CreatePathWithFolders(assetCacheFolderPath); // We are defaulting to PNG here if no extension already set. This forces it to use PNG format below. if (!textureName.EndsWith(".png") && !textureName.EndsWith(".jpg")) { textureName = textureName + ".png"; } string textureFileName = HEU_Platform.BuildPath(assetCacheFolderPath, string.Format("{0}", textureName)); byte[] encodedBytes; if(textureName.EndsWith(".jpg")) { encodedBytes = texture.EncodeToJPG(); } else // Use PNG otherwise { encodedBytes = texture.EncodeToPNG(); } HEU_Platform.WriteBytes(textureFileName, encodedBytes); // Re-import for project to recognize the new texture file HEU_AssetDatabase.ImportAsset(textureFileName, HEU_AssetDatabase.HEU_ImportAssetOptions.Default); // Load the new texture file texture = HEU_AssetDatabase.LoadAssetAtPath(textureFileName, typeof(Texture2D)) as Texture2D; } //texture = HEU_MaterialFactory.ExtractHoudiniImageToTextureFile(session, materialInfo, "C A", assetCacheFolderPath); } return texture; }
public static int GetUnitySubstanceMaterialKey(string unityMaterialPath, string substanceName, int substanceIndex) { System.Text.StringBuilder strBuilder = new System.Text.StringBuilder(); strBuilder.Append(unityMaterialPath); if (!string.IsNullOrEmpty(substanceName)) { strBuilder.AppendFormat("-{0}", substanceName); } if (substanceIndex >= 0) { strBuilder.AppendFormat("-{0}", substanceIndex); } return HEU_MaterialFactory.MaterialNameToKey(strBuilder.ToString()); }
private void GenerateEditPointBoxNewMesh() { if (_selectedAttributesStore == null) { return; } Vector3[] positionArray = new Vector3[0]; _selectedAttributesStore.GetPositionAttributeValues(out positionArray); int numPoints = positionArray.Length; if (numPoints != _previousEditMeshPointCount) { _editPointsSelectedIndices.Clear(); _previousEditMeshPointCount = numPoints; } if (numPoints > 0) { float boxSize = HEU_EditorUtility.GetSerializedProperty(_toolsInfoSerializedObject, "_editPointBoxSize").floatValue; Color unselectedColor = HEU_EditorUtility.GetSerializedProperty(_toolsInfoSerializedObject, "_editPointBoxUnselectedColor").colorValue; Color selectedColor = HEU_EditorUtility.GetSerializedProperty(_toolsInfoSerializedObject, "_editPointBoxSelectedColor").colorValue; if (_editPointBoxMaterial == null) { _editPointBoxMaterial = HEU_MaterialFactory.CreateNewHoudiniStandardMaterial("", "EditPointMaterial", false); } Color[] pointColors = new Color[numPoints]; for (int i = 0; i < numPoints; ++i) { pointColors[i] = unselectedColor; } int numSelected = _editPointsSelectedIndices.Count; for (int i = 0; i < numSelected; ++i) { pointColors[_editPointsSelectedIndices[i]] = selectedColor; } _editPointBoxMesh = HEU_GeometryUtility.GenerateCubeMeshFromPoints(positionArray, pointColors, boxSize); _GUIChanged = true; } }
/// <summary> /// Sets a material on the given Terrain object. /// Currently sets the default Terrain material from the plugin settings, if its valid. /// </summary> /// <param name="terrain">The terrain to set material for</param> public static void SetTerrainMaterial(Terrain terrain) { // Use material specified in Plugin settings. string terrainMaterialPath = HEU_PluginSettings.DefaultTerrainMaterial; if (!string.IsNullOrEmpty(terrainMaterialPath)) { Material material = HEU_MaterialFactory.LoadUnityMaterial(terrainMaterialPath); if (material != null) { #if UNITY_2019_2_OR_NEWER terrain.materialTemplate = material; #else terrain.materialType = Terrain.MaterialType.Custom; terrain.materialTemplate = material; #endif } } // TODO: If none specified, guess based on Render settings? }
// LOGIC ----------------------------------------------------------------------------------------------------- public void DestroyAllData(HEU_HoudiniAsset asset) { _attributeDatas.Clear(); _positionAttributeValues = null; _vertexIndices = null; if (_localMaterial != null) { HEU_MaterialFactory.DestroyNonAssetMaterial(_localMaterial, false); _localMaterial = null; } _outputGameObject = null; _outputMaterials = null; _localMeshCollider = null; _outputMeshCollider = null; _outputColliderMesh = null; _outputMesh = null; }
public static HEU_MaterialData CreateHoudiniMaterialData(HEU_SessionBase session, HAPI_NodeId assetID, HAPI_NodeId materialID, HAPI_NodeId geoID, HAPI_PartId partID, List<HEU_MaterialData> materialCache, string assetCacheFolderPath) { string materialName = ""; if (materialID == HEU_Defines.HEU_INVALID_NODE_ID) { return GetOrCreateDefaultMaterialInCache(session, geoID, partID, false, materialCache, assetCacheFolderPath); } else { materialName = HEU_SessionManager.GetUniqueMaterialShopName(assetID, materialID); } HEU_MaterialData materialData = ScriptableObject.CreateInstance<HEU_MaterialData>(); materialData._materialSource = HEU_MaterialData.Source.HOUDINI; materialData._materialKey = materialID; materialData._material = HEU_MaterialFactory.CreateNewHoudiniStandardMaterial(assetCacheFolderPath, materialName, true); materialData._material.name = materialName; //Debug.LogFormat("New Material ID: {0} - {1}", materialID, materialName); if (materialID != HEU_Defines.HEU_INVALID_NODE_ID) { // Get material info from Houdini to populate the Unity material values HAPI_MaterialInfo materialInfo = new HAPI_MaterialInfo(); if (session.GetMaterialInfo(materialID, ref materialInfo)) { if (materialInfo.exists) { materialData.UpdateMaterialFromHoudini(materialInfo, assetCacheFolderPath); } } } //Debug.LogFormat("Created new material with id={0} and name={1}", materialID, materialName); materialCache.Add(materialData); return materialData; }
public static int GetMaterialKeyFromAttributeIndex(HEU_GenerateGeoCache geoCache, int attributeIndex, out string unityMaterialName, out string substanceName, out int substanceIndex) { unityMaterialName = null; substanceName = null; substanceIndex = -1; if (attributeIndex < geoCache._unityMaterialAttrName.Length && geoCache._unityMaterialAttrStringsMap.TryGetValue(geoCache._unityMaterialAttrName[attributeIndex], out unityMaterialName)) { if (geoCache._substanceMaterialAttrNameInfo.exists && geoCache._substanceMaterialAttrName.Length > 0) { geoCache._substanceMaterialAttrStringsMap.TryGetValue(geoCache._substanceMaterialAttrName[attributeIndex], out substanceName); } if (geoCache._substanceMaterialAttrIndexInfo.exists && string.IsNullOrEmpty(substanceName) && geoCache._substanceMaterialAttrIndex[attributeIndex] >= 0) { substanceIndex = geoCache._substanceMaterialAttrIndex[attributeIndex]; } return HEU_MaterialFactory.GetUnitySubstanceMaterialKey(unityMaterialName, substanceName, substanceIndex); } return HEU_Defines.HEU_INVALID_MATERIAL; }
private bool UseLegacyShaders(HAPI_MaterialInfo materialInfo, string assetCacheFolderPath, HEU_SessionBase session, HAPI_NodeInfo nodeInfo, HAPI_ParmInfo[] parmInfos) { // Diffuse texture - render & extract int diffuseMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_OGL_TEX1_ATTR, HEU_Defines.MAT_OGL_TEX1_ATTR_ENABLED); if (diffuseMapParmIndex < 0) { diffuseMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_BASECOLOR_ATTR, HEU_Defines.MAT_BASECOLOR_ATTR_ENABLED); if (diffuseMapParmIndex < 0) { diffuseMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_MAP_ATTR, ""); } } if (diffuseMapParmIndex >= 0 && diffuseMapParmIndex < parmInfos.Length) { string diffuseTextureFileName = GetTextureFileNameFromMaterialParam(session, materialInfo.nodeId, parmInfos[diffuseMapParmIndex]); _material.mainTexture = HEU_MaterialFactory.RenderAndExtractImageToTexture(session, materialInfo, parmInfos[diffuseMapParmIndex].id, diffuseTextureFileName, assetCacheFolderPath, false); } // Normal map - render & extract texture int normalMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_NORMAL_ATTR, HEU_Defines.MAT_NORMAL_ATTR_ENABLED); if (normalMapParmIndex < 0) { normalMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_OGL_NORMAL_ATTR, ""); } if (normalMapParmIndex >= 0 && normalMapParmIndex < parmInfos.Length) { string normalTextureFileName = GetTextureFileNameFromMaterialParam(session, materialInfo.nodeId, parmInfos[normalMapParmIndex]); Texture2D normalMap = HEU_MaterialFactory.RenderAndExtractImageToTexture(session, materialInfo, parmInfos[normalMapParmIndex].id, normalTextureFileName, assetCacheFolderPath, true); if (normalMap != null) { _material.SetTexture(HEU_Defines.UNITY_SHADER_BUMP_MAP, normalMap); } } // Assign shader properties // Clamp shininess to non-zero as results in very hard shadows. Unity's UI does not allow zero either. float shininess; if (!HEU_ParameterUtility.GetParameterFloatValue(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_OGL_ROUGH_ATTR, 0f, out shininess)) { HEU_ParameterUtility.GetParameterFloatValue(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_ROUGH_ATTR, 0f, out shininess); } _material.SetFloat(HEU_Defines.UNITY_SHADER_SHININESS, Mathf.Max(0.03f, 1.0f - shininess)); Color diffuseColor; if (!HEU_ParameterUtility.GetParameterColor3Value(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_OGL_DIFF_ATTR, Color.white, out diffuseColor)) { HEU_ParameterUtility.GetParameterColor3Value(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_DIFF_ATTR, Color.white, out diffuseColor); } float alpha; if (!HEU_ParameterUtility.GetParameterFloatValue(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_OGL_ALPHA_ATTR, 1f, out alpha)) { HEU_ParameterUtility.GetParameterFloatValue(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_ALPHA_ATTR, 1f, out alpha); } diffuseColor.a = alpha; _material.SetColor(HEU_Defines.UNITY_SHADER_COLOR, diffuseColor); Color specular; if (!HEU_ParameterUtility.GetParameterColor3Value(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_OGL_SPEC_ATTR, Color.black, out specular)) { HEU_ParameterUtility.GetParameterColor3Value(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_SPEC_ATTR, Color.black, out specular); } _material.SetColor(HEU_Defines.UNITY_SHADER_SPEC_COLOR, specular); return true; }
/// <summary> /// For this object's _material, we update the shader attributes and /// fetch the textures from Houdini. /// </summary> /// <param name="materialInfo">This material's info from Houdini</param> /// <param name="assetCacheFolderPath">Path to asset's cache folder</param> public bool UpdateMaterialFromHoudini(HAPI_MaterialInfo materialInfo, string assetCacheFolderPath) { if (_material == null) { return false; } HEU_SessionBase session = HEU_SessionManager.GetOrCreateDefaultSession(); HAPI_NodeInfo nodeInfo = new HAPI_NodeInfo(); if (!session.GetNodeInfo(materialInfo.nodeId, ref nodeInfo)) { return false; } // Get all parameters of this material HAPI_ParmInfo[] parmInfos = new HAPI_ParmInfo[nodeInfo.parmCount]; if (!HEU_GeneralUtility.GetArray1Arg(materialInfo.nodeId, session.GetParams, parmInfos, 0, nodeInfo.parmCount)) { return false; } // Assign transparency shader or non-transparent. bool isTransparent = IsTransparentMaterial(session, materialInfo.nodeId, parmInfos); if (isTransparent) { _material.shader = HEU_MaterialFactory.FindPluginShader(HEU_PluginSettings.DefaultTransparentShader); } else { _material.shader = HEU_MaterialFactory.FindPluginShader(HEU_PluginSettings.DefaultStandardShader); } if (HEU_PluginSettings.UseLegacyShaders) { return UseLegacyShaders(materialInfo, assetCacheFolderPath, session, nodeInfo, parmInfos); } // Diffuse texture - render & extract int diffuseMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_OGL_TEX1_ATTR, HEU_Defines.MAT_OGL_TEX1_ATTR_ENABLED); if (diffuseMapParmIndex < 0) { diffuseMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_BASECOLOR_ATTR, HEU_Defines.MAT_BASECOLOR_ATTR_ENABLED); } if (diffuseMapParmIndex >= 0 && diffuseMapParmIndex < parmInfos.Length) { string diffuseTextureFileName = GetTextureFileNameFromMaterialParam(session, materialInfo.nodeId, parmInfos[diffuseMapParmIndex]); _material.mainTexture = HEU_MaterialFactory.RenderAndExtractImageToTexture(session, materialInfo, parmInfos[diffuseMapParmIndex].id, diffuseTextureFileName, assetCacheFolderPath, false); } Color diffuseColor; if (!HEU_ParameterUtility.GetParameterColor3Value(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_OGL_DIFF_ATTR, Color.white, out diffuseColor)) { HEU_ParameterUtility.GetParameterColor3Value(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_DIFF_ATTR, Color.white, out diffuseColor); } float alpha; GetMaterialAlpha(session, materialInfo.nodeId, parmInfos, 1f, out alpha); if (isTransparent) { int opacityMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_OGL_OPACITY_MAP_ATTR, HEU_Defines.MAT_OGL_OPACITY_MAP_ATTR_ENABLED); if (opacityMapParmIndex < 0) { opacityMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_OPACITY_MAP_ATTR, HEU_Defines.MAT_OPACITY_MAP_ATTR_ENABLED); } if (opacityMapParmIndex >= 0 && opacityMapParmIndex < parmInfos.Length) { string opacityTextureFileName = GetTextureFileNameFromMaterialParam(session, materialInfo.nodeId, parmInfos[opacityMapParmIndex]); _material.SetTexture(HEU_Defines.UNITY_SHADER_OPACITY_MAP, HEU_MaterialFactory.RenderAndExtractImageToTexture(session, materialInfo, parmInfos[opacityMapParmIndex].id, opacityTextureFileName, assetCacheFolderPath, false)); } } diffuseColor.a = alpha; _material.SetColor(HEU_Defines.UNITY_SHADER_COLOR, diffuseColor); if (HEU_PluginSettings.UseSpecularShader) { Color specular; Color defaultSpecular = new Color(0.2f, 0.2f, 0.2f, 1); if (!HEU_ParameterUtility.GetParameterColor3Value(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_OGL_SPEC_ATTR, defaultSpecular, out specular)) { HEU_ParameterUtility.GetParameterColor3Value(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_SPEC_ATTR, defaultSpecular, out specular); } _material.SetColor(HEU_Defines.UNITY_SHADER_SPEC_COLOR, specular); int specMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_OGL_SPEC_MAP_ATTR, HEU_Defines.MAT_OGL_SPEC_MAP_ATTR_ENABLED); if (specMapParmIndex < 0) { specMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_SPEC_MAP_ATTR, HEU_Defines.MAT_SPEC_MAP_ATTR_ENABLED); } if (specMapParmIndex >= 0 && specMapParmIndex < parmInfos.Length) { string specTextureFileName = GetTextureFileNameFromMaterialParam(session, materialInfo.nodeId, parmInfos[specMapParmIndex]); _material.SetTexture(HEU_Defines.UNITY_SHADER_SPEC_MAP, HEU_MaterialFactory.RenderAndExtractImageToTexture(session, materialInfo, parmInfos[specMapParmIndex].id, specTextureFileName, assetCacheFolderPath, false)); } } else { float metallic = 0; if (!HEU_ParameterUtility.GetParameterFloatValue(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_OGL_METALLIC_ATTR, 0f, out metallic)) { HEU_ParameterUtility.GetParameterFloatValue(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_METALLIC_ATTR, 0f, out metallic); } _material.SetFloat(HEU_Defines.UNITY_SHADER_METALLIC, metallic); int metallicMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_OGL_METALLIC_MAP_ATTR, HEU_Defines.MAT_OGL_METALLIC_MAP_ATTR_ENABLED); if (metallicMapParmIndex < 0) { metallicMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_METALLIC_MAP_ATTR, HEU_Defines.MAT_METALLIC_MAP_ATTR_ENABLED); } if (metallicMapParmIndex >= 0 && metallicMapParmIndex < parmInfos.Length) { string metallicTextureFileName = GetTextureFileNameFromMaterialParam(session, materialInfo.nodeId, parmInfos[metallicMapParmIndex]); _material.SetTexture(HEU_Defines.UNITY_SHADER_METALLIC_MAP, HEU_MaterialFactory.RenderAndExtractImageToTexture(session, materialInfo, parmInfos[metallicMapParmIndex].id, metallicTextureFileName, assetCacheFolderPath, false)); } } // Normal map - render & extract texture int normalMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_NORMAL_ATTR, HEU_Defines.MAT_NORMAL_ATTR_ENABLED); if (normalMapParmIndex < 0) { normalMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_OGL_NORMAL_ATTR, ""); } if (normalMapParmIndex >= 0 && normalMapParmIndex < parmInfos.Length) { string normalTextureFileName = GetTextureFileNameFromMaterialParam(session, materialInfo.nodeId, parmInfos[normalMapParmIndex]); Texture2D normalMap = HEU_MaterialFactory.RenderAndExtractImageToTexture(session, materialInfo, parmInfos[normalMapParmIndex].id, normalTextureFileName, assetCacheFolderPath, true); if (normalMap != null) { _material.SetTexture(HEU_Defines.UNITY_SHADER_BUMP_MAP, normalMap); } } // Emission Color emission; Color defaultEmission = new Color(0, 0, 0, 0); if (!HEU_ParameterUtility.GetParameterColor3Value(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_OGL_EMISSIVE_ATTR, defaultEmission, out emission)) { HEU_ParameterUtility.GetParameterColor3Value(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_EMISSIVE_ATTR, defaultEmission, out emission); } _material.SetColor(HEU_Defines.UNITY_SHADER_EMISSION_COLOR, emission); int emissionMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_OGL_EMISSIVE_MAP_ATTR, HEU_Defines.MAT_OGL_EMISSIVE_MAP_ATTR_ENABLED); if (emissionMapParmIndex < 0) { emissionMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_EMISSIVE_MAP_ATTR, HEU_Defines.MAT_EMISSIVE_MAP_ATTR_ENABLED); } if (emissionMapParmIndex >= 0 && emissionMapParmIndex < parmInfos.Length) { string emissionTextureFileName = GetTextureFileNameFromMaterialParam(session, materialInfo.nodeId, parmInfos[emissionMapParmIndex]); _material.SetTexture(HEU_Defines.UNITY_SHADER_EMISSION_MAP, HEU_MaterialFactory.RenderAndExtractImageToTexture(session, materialInfo, parmInfos[emissionMapParmIndex].id, emissionTextureFileName, assetCacheFolderPath, false)); } // Smoothness (need to invert roughness!) float roughness; float defaultRoughness = 0.5f; if (!HEU_ParameterUtility.GetParameterFloatValue(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_OGL_ROUGH_ATTR, defaultRoughness, out roughness)) { HEU_ParameterUtility.GetParameterFloatValue(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_ROUGH_ATTR, defaultRoughness, out roughness); } // Clamp shininess to non-zero as results in very hard shadows. Unity's UI does not allow zero either. _material.SetFloat(HEU_Defines.UNITY_SHADER_SMOOTHNESS, Mathf.Max(0.03f, 1.0f - roughness)); int roughMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_OGL_ROUGH_MAP_ATTR, HEU_Defines.MAT_OGL_ROUGH_MAP_ATTR_ENABLED); if (roughMapParmIndex < 0) { roughMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_ROUGH_MAP_ATTR, HEU_Defines.MAT_ROUGH_MAP_ATTR_ENABLED); } if (roughMapParmIndex >= 0 && roughMapParmIndex < parmInfos.Length) { string roughTextureFileName = GetTextureFileNameFromMaterialParam(session, materialInfo.nodeId, parmInfos[roughMapParmIndex]); _material.SetTexture(HEU_Defines.UNITY_SHADER_SMOOTHNESS_MAP, HEU_MaterialFactory.RenderAndExtractImageToTexture(session, materialInfo, parmInfos[roughMapParmIndex].id, roughTextureFileName, assetCacheFolderPath, false, invertTexture: true)); } // Occlusion (only has ogl map) int occlusionMapParmIndex = HEU_ParameterUtility.FindTextureParamByNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_OGL_OCCLUSION_MAP_ATTR, HEU_Defines.MAT_OGL_ROUGH_MAP_ATTR_ENABLED); if (occlusionMapParmIndex >= 0 && occlusionMapParmIndex < parmInfos.Length) { string occlusionTextureFileName = GetTextureFileNameFromMaterialParam(session, materialInfo.nodeId, parmInfos[occlusionMapParmIndex]); _material.SetTexture(HEU_Defines.UNITY_SHADER_OCCLUSION_MAP, HEU_MaterialFactory.RenderAndExtractImageToTexture(session, materialInfo, parmInfos[occlusionMapParmIndex].id, occlusionTextureFileName, assetCacheFolderPath, false)); } return true; }
private void GenerateTerrain(List<HEU_LoadBufferVolume> terrainBuffers) { Transform parent = this.gameObject.transform; int numVolues = terrainBuffers.Count; for(int t = 0; t < numVolues; ++t) { if (terrainBuffers[t]._heightMap != null) { GameObject newGameObject = new GameObject("heightfield_" + terrainBuffers[t]._tileIndex); Transform newTransform = newGameObject.transform; newTransform.parent = parent; HEU_GeneratedOutput generatedOutput = new HEU_GeneratedOutput(); generatedOutput._outputData._gameObject = newGameObject; Terrain terrain = HEU_GeneralUtility.GetOrCreateComponent<Terrain>(newGameObject); TerrainCollider collider = HEU_GeneralUtility.GetOrCreateComponent<TerrainCollider>(newGameObject); terrain.terrainData = new TerrainData(); TerrainData terrainData = terrain.terrainData; collider.terrainData = terrainData; int heightMapSize = terrainBuffers[t]._heightMapSize; terrainData.heightmapResolution = heightMapSize; if (terrainData.heightmapResolution != heightMapSize) { Debug.LogErrorFormat("Unsupported terrain size: {0}", heightMapSize); continue; } terrainData.baseMapResolution = heightMapSize; terrainData.alphamapResolution = heightMapSize; const int resolutionPerPatch = 128; terrainData.SetDetailResolution(resolutionPerPatch, resolutionPerPatch); terrainData.SetHeights(0, 0, terrainBuffers[t]._heightMap); terrainData.size = new Vector3(terrainBuffers[t]._terrainSizeX, terrainBuffers[t]._heightRange, terrainBuffers[t]._terrainSizeY); terrain.Flush(); // Set position HAPI_Transform hapiTransformVolume = new HAPI_Transform(true); hapiTransformVolume.position[0] += terrainBuffers[t]._position[0]; hapiTransformVolume.position[1] += terrainBuffers[t]._position[1]; hapiTransformVolume.position[2] += terrainBuffers[t]._position[2]; HEU_HAPIUtility.ApplyLocalTransfromFromHoudiniToUnity(ref hapiTransformVolume, newTransform); // Set layers Texture2D defaultTexture = HEU_VolumeCache.LoadDefaultSplatTexture(); int numLayers = terrainBuffers[t]._layers.Count; #if UNITY_2018_3_OR_NEWER // Create TerrainLayer for each heightfield layer // Note that 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[numLayers]; for (int m = 0; m < numLayers; ++m) { terrainLayers[m] = new TerrainLayer(); HEU_LoadBufferVolumeLayer layer = terrainBuffers[t]._layers[m]; if (!string.IsNullOrEmpty(layer._diffuseTexturePath)) { // Using Resources.Load is much faster than AssetDatabase.Load //terrainLayers[m].diffuseTexture = HEU_MaterialFactory.LoadTexture(layer._diffuseTexturePath); terrainLayers[m].diffuseTexture = Resources.Load<Texture2D>(layer._diffuseTexturePath); } if (terrainLayers[m].diffuseTexture == null) { terrainLayers[m].diffuseTexture = defaultTexture; } terrainLayers[m].diffuseRemapMin = Vector4.zero; terrainLayers[m].diffuseRemapMax = Vector4.one; if (!string.IsNullOrEmpty(layer._maskTexturePath)) { // Using Resources.Load is much faster than AssetDatabase.Load //terrainLayers[m].maskMapTexture = HEU_MaterialFactory.LoadTexture(layer._maskTexturePath); terrainLayers[m].maskMapTexture = Resources.Load<Texture2D>(layer._maskTexturePath); } terrainLayers[m].maskMapRemapMin = Vector4.zero; terrainLayers[m].maskMapRemapMax = Vector4.one; terrainLayers[m].metallic = layer._metallic; if (!string.IsNullOrEmpty(layer._normalTexturePath)) { terrainLayers[m].normalMapTexture = HEU_MaterialFactory.LoadTexture(layer._normalTexturePath); } 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 && terrainLayers[m].diffuseTexture != null) { // Use texture size if tile size is 0 layer._tileSize = new Vector2(terrainLayers[m].diffuseTexture.width, terrainLayers[m].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[numLayers]; for (int m = 0; m < numLayers; ++m) { splatPrototypes[m] = new SplatPrototype(); HEU_LoadBufferVolumeLayer layer = terrainBuffers[t]._layers[m]; Texture2D diffuseTexture = null; if (!string.IsNullOrEmpty(layer._diffuseTexturePath)) { diffuseTexture = HEU_MaterialFactory.LoadTexture(layer._diffuseTexturePath); } if (diffuseTexture == null) { diffuseTexture = defaultTexture; } splatPrototypes[m].texture = diffuseTexture; splatPrototypes[m].tileOffset = layer._tileOffset; if (layer._tileSize.magnitude == 0f && diffuseTexture != null) { // Use texture size if tile size is 0 layer._tileSize = new Vector2(diffuseTexture.width, diffuseTexture.height); } splatPrototypes[m].tileSize = layer._tileSize; splatPrototypes[m].metallic = layer._metallic; splatPrototypes[m].smoothness = layer._smoothness; if (!string.IsNullOrEmpty(layer._normalTexturePath)) { splatPrototypes[m].normalMap = HEU_MaterialFactory.LoadTexture(layer._normalTexturePath); } } terrainData.splatPrototypes = splatPrototypes; #endif terrainData.SetAlphamaps(0, 0, terrainBuffers[t]._splatMaps); //string assetPath = HEU_AssetDatabase.CreateAssetCacheFolder("terrainData"); //AssetDatabase.CreateAsset(terrainData, assetPath); //Debug.Log("Created asset data at " + assetPath); terrainBuffers[t]._generatedOutput = generatedOutput; _generatedOutputs.Add(generatedOutput); SetOutputVisiblity(terrainBuffers[t]); } } }
/// <summary> /// For this object's _material, we update the shader attributes and /// fetch the textures from Houdini. /// </summary> /// <param name="materialInfo">This material's info from Houdini</param> /// <param name="assetCacheFolderPath">Path to asset's cache folder</param> public void UpdateMaterialFromHoudini(HAPI_MaterialInfo materialInfo, string assetCacheFolderPath) { HEU_SessionBase session = HEU_SessionManager.GetOrCreateDefaultSession(); HAPI_NodeInfo nodeInfo = new HAPI_NodeInfo(); if (!session.GetNodeInfo(materialInfo.nodeId, ref nodeInfo)) { return; } // Get all parameters of this material HAPI_ParmInfo[] parmInfos = new HAPI_ParmInfo[nodeInfo.parmCount]; if (!HEU_GeneralUtility.GetArray1Arg(materialInfo.nodeId, session.GetParams, parmInfos, 0, nodeInfo.parmCount)) { return; } // Assign transparency shader or non-transparent. if (IsTransparentMaterial(session, materialInfo.nodeId, parmInfos)) { _material.shader = HEU_MaterialFactory.FindPluginShader(HEU_PluginSettings.DefaultTransparentShader); } else { _material.shader = HEU_MaterialFactory.FindPluginShader(HEU_PluginSettings.DefaultStandardShader); } // Diffuse texture - render & extract int diffuseMapParmIndex = HEU_ParameterUtility.GetParameterIndexFromNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_OGL_TEX1_ATTR); if (diffuseMapParmIndex < 0) { diffuseMapParmIndex = HEU_ParameterUtility.GetParameterIndexFromNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_BASECOLOR_ATTR); if (diffuseMapParmIndex < 0) { diffuseMapParmIndex = HEU_ParameterUtility.GetParameterIndexFromNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_MAP_ATTR); } } if (diffuseMapParmIndex >= 0 && diffuseMapParmIndex < parmInfos.Length) { string diffuseTextureFileName = GetTextureFileNameFromMaterialParam(session, materialInfo.nodeId, parmInfos[diffuseMapParmIndex]); _material.mainTexture = HEU_MaterialFactory.RenderAndExtractImageToTexture(session, materialInfo, parmInfos[diffuseMapParmIndex].id, diffuseTextureFileName, assetCacheFolderPath); } // Normal map - render & extract texture int normalMapParmIndex = HEU_ParameterUtility.GetParameterIndexFromNameOrTag(session, nodeInfo.id, parmInfos, HEU_Defines.MAT_OGL_NORMAL_ATTR); if (normalMapParmIndex >= 0 && normalMapParmIndex < parmInfos.Length) { string normalTextureFileName = GetTextureFileNameFromMaterialParam(session, materialInfo.nodeId, parmInfos[normalMapParmIndex]); Texture2D normalMap = HEU_MaterialFactory.RenderAndExtractImageToTexture(session, materialInfo, parmInfos[normalMapParmIndex].id, normalTextureFileName, assetCacheFolderPath); if (normalMap != null) { _material.SetTexture(HEU_Defines.UNITY_SHADER_BUMP_MAP, normalMap); } } // Assign shader properties // Clamp shininess to non-zero as results in very hard shadows. Unity's UI does not allow zero either. float shininess = HEU_ParameterUtility.GetParameterFloatValue(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_OGL_ROUGH_ATTR, 0f); _material.SetFloat(HEU_Defines.UNITY_SHADER_SHININESS, Mathf.Max(0.03f, 1.0f - shininess)); Color diffuseColor = HEU_ParameterUtility.GetParameterColor3Value(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_OGL_DIFF_ATTR, Color.white); diffuseColor.a = HEU_ParameterUtility.GetParameterFloatValue(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_OGL_ALPHA_ATTR, 1f); _material.SetColor(HEU_Defines.UNITY_SHADER_COLOR, diffuseColor); Color specular = HEU_ParameterUtility.GetParameterColor3Value(session, materialInfo.nodeId, parmInfos, HEU_Defines.MAT_OGL_SPEC_ATTR, Color.black); _material.SetColor(HEU_Defines.UNITY_SHADER_SPECCOLOR, specular); }
private bool DrawDetailsGeometry() { bool bChanged = false; EditorGUIUtility.labelWidth = 250; { bool oldValue = HEU_PluginSettings.Curves_ShowInSceneView; bool newValue = HEU_EditorUI.DrawToggleLeft(oldValue, "Show Curves in Scene View"); if (newValue != oldValue) { HEU_PluginSettings.Curves_ShowInSceneView = newValue; HEU_HoudiniAsset.SetCurvesVisibilityInScene(newValue); bChanged = true; } } HEU_EditorUI.DrawSeparator(); { float oldValue = HEU_PluginSettings.NormalGenerationThresholdAngle; float newValue = EditorGUILayout.DelayedFloatField("Normal Generation Threshold Angle", oldValue); if (newValue != oldValue) { HEU_PluginSettings.NormalGenerationThresholdAngle = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.DefaultTerrainMaterial; if (_terrainMaterial == null && !string.IsNullOrEmpty(oldValue)) { //Debug.Log("Loading terrain material at: " + oldValue); _terrainMaterial = HEU_MaterialFactory.LoadUnityMaterial(oldValue); } Material newMaterial = EditorGUILayout.ObjectField("Default Terrain Material", _terrainMaterial, typeof(Material), false) as Material; if (newMaterial != _terrainMaterial) { HEU_PluginSettings.DefaultTerrainMaterial = (newMaterial != null) ? HEU_AssetDatabase.GetAssetPathWithSubAssetSupport(newMaterial) : ""; _terrainMaterial = newMaterial; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.TerrainSplatTextureDefault; string newValue = EditorGUILayout.DelayedTextField("Default Terrain Splat Texture", oldValue); if (!newValue.Equals(oldValue)) { HEU_PluginSettings.TerrainSplatTextureDefault = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.DefaultStandardShader; string newValue = EditorGUILayout.DelayedTextField("Default Standard Shader", oldValue); if (!newValue.Equals(oldValue)) { HEU_PluginSettings.DefaultStandardShader = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.DefaultTransparentShader; string newValue = EditorGUILayout.DelayedTextField("Default Transparent Shader", oldValue); if (!newValue.Equals(oldValue)) { HEU_PluginSettings.DefaultTransparentShader = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.DefaultVertexColorShader; string newValue = EditorGUILayout.DelayedTextField("Default Vertex Color Shader", oldValue); if (!newValue.Equals(oldValue)) { HEU_PluginSettings.DefaultVertexColorShader = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.DefaultCurveShader; string newValue = EditorGUILayout.DelayedTextField("Default Curve Shader", oldValue); if (!newValue.Equals(oldValue)) { HEU_PluginSettings.DefaultCurveShader = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); EditorGUIUtility.labelWidth = 0; return bChanged; }
private bool DrawDetailsGeometry() { bool bChanged = false; EditorGUIUtility.labelWidth = 250; { bool oldValue = HEU_PluginSettings.Curves_ShowInSceneView; bool newValue = HEU_EditorUI.DrawToggleLeft(oldValue, "Show Curves in Scene View"); if (newValue != oldValue) { HEU_PluginSettings.Curves_ShowInSceneView = newValue; HEU_HoudiniAsset.SetCurvesVisibilityInScene(newValue); bChanged = true; } } HEU_EditorUI.DrawSeparator(); { int oldValue = HEU_PluginSettings.MaxVerticesPerPrimitive; int newValue = EditorGUILayout.DelayedIntField("Max Vertices Per Primitive", oldValue); if (newValue != oldValue) { if (newValue == 3 || newValue == 4) { HEU_PluginSettings.MaxVerticesPerPrimitive = newValue; bChanged = true; } else { Debug.LogWarningFormat("Plugin only supports 3 (triangles) or 4 (quads) max vertices values."); } } } HEU_EditorUI.DrawSeparator(); { float oldValue = HEU_PluginSettings.NormalGenerationThresholdAngle; float newValue = EditorGUILayout.DelayedFloatField("Normal Generation Threshold Angle", oldValue); if (newValue != oldValue) { HEU_PluginSettings.NormalGenerationThresholdAngle = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.DefaultTerrainMaterial; if (_terrainMaterial == null && !string.IsNullOrEmpty(oldValue)) { //Debug.Log("Loading terrain material at: " + oldValue); _terrainMaterial = HEU_MaterialFactory.LoadUnityMaterial(oldValue); } Material newMaterial = EditorGUILayout.ObjectField("Default Terrain Material", _terrainMaterial, typeof(Material), false) as Material; if (newMaterial != _terrainMaterial) { string materialPath = ""; if (newMaterial != null) { materialPath = HEU_AssetDatabase.GetAssetPathWithSubAssetSupport(newMaterial); if (!string.IsNullOrEmpty(materialPath) && (materialPath.StartsWith(HEU_Defines.DEFAULT_UNITY_BUILTIN_RESOURCES))) { // Default materials need to be specially handled materialPath = HEU_AssetDatabase.GetUniqueAssetPathForUnityAsset(newMaterial); newMaterial = HEU_AssetDatabase.LoadUnityAssetFromUniqueAssetPath<Material>(materialPath); } } HEU_PluginSettings.DefaultTerrainMaterial = materialPath; _terrainMaterial = newMaterial; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.TerrainSplatTextureDefault; string newValue = EditorGUILayout.DelayedTextField("Default Terrain Splat Texture", oldValue); if (!newValue.Equals(oldValue)) { HEU_PluginSettings.TerrainSplatTextureDefault = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.DefaultStandardShader; string newValue = EditorGUILayout.DelayedTextField("Default Standard Shader", oldValue); if (!newValue.Equals(oldValue)) { HEU_PluginSettings.DefaultStandardShader = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.DefaultTransparentShader; string newValue = EditorGUILayout.DelayedTextField("Default Transparent Shader", oldValue); if (!newValue.Equals(oldValue)) { HEU_PluginSettings.DefaultTransparentShader = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.DefaultVertexColorShader; string newValue = EditorGUILayout.DelayedTextField("Default Vertex Color Shader", oldValue); if (!newValue.Equals(oldValue)) { HEU_PluginSettings.DefaultVertexColorShader = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); { string oldValue = HEU_PluginSettings.DefaultCurveShader; string newValue = EditorGUILayout.DelayedTextField("Default Curve Shader", oldValue); if (!newValue.Equals(oldValue)) { HEU_PluginSettings.DefaultCurveShader = newValue; bChanged = true; } } HEU_EditorUI.DrawSeparator(); EditorGUIUtility.labelWidth = 0; return bChanged; }
public void SetupMeshAndMaterials(HEU_HoudiniAsset asset, HAPI_PartType partType, GameObject outputGameObject) { Color[] oldColors = _outputMesh != null && _outputMesh.isReadable ? _outputMesh.colors : null; _outputMesh = null; _outputGameObject = null; if (HEU_HAPIUtility.IsSupportedPolygonType(partType)) { // Get the generated mesh. If mesh is missing, nothing we can do. MeshFilter meshFilter = outputGameObject.GetComponent<MeshFilter>(); if (meshFilter != null && meshFilter.sharedMesh != null) { _outputMesh = meshFilter.sharedMesh; if (_outputMesh.isReadable) { Color[] newColors = _outputMesh.colors; if (oldColors != null) { // Restore old colors back to newly generated mesh so // as to keep color "state" for visualization int oldLen = oldColors.Length; if (newColors == null || newColors.Length == 0) { newColors = new Color[_outputMesh.vertices.Length]; } int newLen = newColors.Length; for (int i = 0; i < newLen && i < oldLen; ++i) { newColors[i] = oldColors[i]; } _outputMesh.colors = newColors; _outputMesh.UploadMeshData(false); } else if (newColors == null || newColors.Length == 0) { // Assign new default colors int count = _outputMesh.vertices.Length; newColors = new Color[count]; for (int i = 0; i < count; ++i) { newColors[i] = new Color(0.3f, 0.06f, 0.62f); } _outputMesh.colors = newColors; _outputMesh.UploadMeshData(false); } } } else { // Without a valid mesh, we won't be able to paint so nothing else to do return; } _outputGameObject = outputGameObject; MeshRenderer meshRenderer = _outputGameObject.GetComponent<MeshRenderer>(); if (meshRenderer != null) { _outputMeshRendererInitiallyEnabled = meshRenderer.enabled; if (_localMaterial == null) { _localMaterial = HEU_MaterialFactory.GetNewMaterialWithShader(null, HEU_PluginSettings.DefaultVertexColorShader, HEU_Defines.EDITABLE_MATERIAL, false); } } } }
private void GenerateTerrain(List<HEU_LoadBufferVolume> terrainBuffers) { Transform parent = this.gameObject.transform; // Directory to store generated terrain files. string outputTerrainpath = GetOutputCacheDirectory(); outputTerrainpath = HEU_Platform.BuildPath(outputTerrainpath, "Terrain"); int numVolumes = terrainBuffers.Count; for(int t = 0; t < numVolumes; ++t) { if (terrainBuffers[t]._heightMap != null) { GameObject newGameObject = new GameObject("heightfield_" + terrainBuffers[t]._tileIndex); Transform newTransform = newGameObject.transform; newTransform.parent = parent; HEU_GeneratedOutput generatedOutput = new HEU_GeneratedOutput(); generatedOutput._outputData._gameObject = newGameObject; Terrain terrain = HEU_GeneralUtility.GetOrCreateComponent<Terrain>(newGameObject); TerrainCollider collider = HEU_GeneralUtility.GetOrCreateComponent<TerrainCollider>(newGameObject); if (!string.IsNullOrEmpty(terrainBuffers[t]._terrainDataPath)) { // Load the source TerrainData, then make a unique copy of it in the cache folder TerrainData sourceTerrainData = HEU_AssetDatabase.LoadAssetAtPath(terrainBuffers[t]._terrainDataPath, typeof(TerrainData)) as TerrainData; if (sourceTerrainData == null) { Debug.LogWarningFormat("TerrainData, set via attribute, not found at: {0}", terrainBuffers[t]._terrainDataPath); } terrain.terrainData = HEU_AssetDatabase.CopyUniqueAndLoadAssetAtAnyPath(sourceTerrainData, outputTerrainpath, typeof(TerrainData)) as TerrainData; if (terrain.terrainData != null) { // Store path so that it can be deleted on clean up AddGeneratedOutputFilePath(HEU_AssetDatabase.GetAssetPath(terrain.terrainData)); } } if (terrain.terrainData == null) { terrain.terrainData = new TerrainData(); } TerrainData terrainData = terrain.terrainData; collider.terrainData = terrainData; HEU_TerrainUtility.SetTerrainMaterial(terrain, terrainBuffers[t]._specifiedTerrainMaterialName); #if UNITY_2018_3_OR_NEWER terrain.allowAutoConnect = true; // This has to be set after setting material terrain.drawInstanced = true; #endif int heightMapSize = terrainBuffers[t]._heightMapWidth; terrainData.heightmapResolution = heightMapSize; if (terrainData.heightmapResolution != heightMapSize) { Debug.LogErrorFormat("Unsupported terrain size: {0}", heightMapSize); continue; } // The terrainData.baseMapResolution is not set here, but rather left to whatever default Unity uses // The terrainData.alphamapResolution is set later when setting the alphamaps. // 32 is the default for resolutionPerPatch const int detailResolution = 1024; const int resolutionPerPatch = 32; terrainData.SetDetailResolution(detailResolution, resolutionPerPatch); terrainData.SetHeights(0, 0, terrainBuffers[t]._heightMap); // Note that Unity uses a default height range of 600 when a flat terrain is created. // Without a non-zero value for the height range, user isn't able to draw heights. // Therefore, set 600 as the value if height range is currently 0 (due to flat heightfield). float heightRange = terrainBuffers[t]._heightRange; if (heightRange == 0) { heightRange = 600; } terrainData.size = new Vector3(terrainBuffers[t]._terrainSizeX, heightRange, terrainBuffers[t]._terrainSizeY); terrain.Flush(); // Set position HAPI_Transform hapiTransformVolume = new HAPI_Transform(true); hapiTransformVolume.position[0] += terrainBuffers[t]._position[0]; hapiTransformVolume.position[1] += terrainBuffers[t]._position[1]; hapiTransformVolume.position[2] += terrainBuffers[t]._position[2]; HEU_HAPIUtility.ApplyLocalTransfromFromHoudiniToUnity(ref hapiTransformVolume, newTransform); // Set layers Texture2D defaultTexture = HEU_VolumeCache.LoadDefaultSplatTexture(); int numLayers = terrainBuffers[t]._splatLayers.Count; #if UNITY_2018_3_OR_NEWER // Create TerrainLayer for each heightfield layer. // Note that height and mask layers are ignored (i.e. not created as TerrainLayers). // Since height layer is first, only process layers from 2nd index onwards. if (numLayers > 1) { // Keep existing TerrainLayers, and either update or append to them TerrainLayer[] existingTerrainLayers = terrainData.terrainLayers; // Total layers are existing layers + new alpha maps List<TerrainLayer> finalTerrainLayers = new List<TerrainLayer>(existingTerrainLayers); for (int m = 1; m < numLayers; ++m) { TerrainLayer terrainlayer = null; int terrainLayerIndex = -1; bool bSetTerrainLayerProperties = true; HEU_LoadBufferVolumeLayer layer = terrainBuffers[t]._splatLayers[m]; // Look up TerrainLayer file via attribute if user has set it if (!string.IsNullOrEmpty(layer._layerPath)) { terrainlayer = HEU_AssetDatabase.LoadAssetAtPath(layer._layerPath, typeof(TerrainLayer)) as TerrainLayer; if (terrainlayer == null) { Debug.LogWarningFormat("TerrainLayer, set via attribute, not found at: {0}", layer._layerPath); continue; } else { // Always check if its part of existing list so as not to add it again terrainLayerIndex = HEU_TerrainUtility.GetTerrainLayerIndex(terrainlayer, existingTerrainLayers); } } if (terrainlayer == null) { terrainlayer = new TerrainLayer(); terrainLayerIndex = finalTerrainLayers.Count; finalTerrainLayers.Add(terrainlayer); } else { // For existing TerrainLayer, make a copy of it if it has custom layer attributes // because we don't want to change the original TerrainLayer. if (layer._hasLayerAttributes && terrainLayerIndex >= 0) { // Copy the TerrainLayer file TerrainLayer prevTerrainLayer = terrainlayer; terrainlayer = HEU_AssetDatabase.CopyAndLoadAssetAtAnyPath(terrainlayer, outputTerrainpath, typeof(TerrainLayer), true) as TerrainLayer; if (terrainlayer != null) { // Update the TerrainLayer reference in the list with this copy finalTerrainLayers[terrainLayerIndex] = terrainlayer; // Store path for clean up later AddGeneratedOutputFilePath(HEU_AssetDatabase.GetAssetPath(terrainlayer)); } else { Debug.LogErrorFormat("Unable to copy TerrainLayer '{0}' for generating Terrain. " + "Using original TerrainLayer. Will not be able to set any TerrainLayer properties.", layer._layerName); terrainlayer = prevTerrainLayer; bSetTerrainLayerProperties = false; // Again, continuing on to keep proper indexing. } } } if (bSetTerrainLayerProperties) { if (!string.IsNullOrEmpty(layer._diffuseTexturePath)) { terrainlayer.diffuseTexture = HEU_MaterialFactory.LoadTexture(layer._diffuseTexturePath); } if (terrainlayer.diffuseTexture == null) { terrainlayer.diffuseTexture = defaultTexture; } terrainlayer.diffuseRemapMin = Vector4.zero; terrainlayer.diffuseRemapMax = Vector4.one; if (!string.IsNullOrEmpty(layer._maskTexturePath)) { terrainlayer.maskMapTexture = HEU_MaterialFactory.LoadTexture(layer._maskTexturePath); } terrainlayer.maskMapRemapMin = Vector4.zero; terrainlayer.maskMapRemapMax = Vector4.one; terrainlayer.metallic = layer._metallic; if (!string.IsNullOrEmpty(layer._normalTexturePath)) { terrainlayer.normalMapTexture = HEU_MaterialFactory.LoadTexture(layer._normalTexturePath); } terrainlayer.normalScale = layer._normalScale; terrainlayer.smoothness = layer._smoothness; terrainlayer.specular = layer._specularColor; terrainlayer.tileOffset = layer._tileOffset; if (layer._tileSize.magnitude == 0f && terrainlayer.diffuseTexture != null) { // Use texture size if tile size is 0 layer._tileSize = new Vector2(terrainlayer.diffuseTexture.width, terrainlayer.diffuseTexture.height); } terrainlayer.tileSize = layer._tileSize; } } terrainData.terrainLayers = finalTerrainLayers.ToArray(); } #else // Need to create SplatPrototype for each layer in heightfield, representing the textures. SplatPrototype[] splatPrototypes = new SplatPrototype[numLayers]; for (int m = 0; m < numLayers; ++m) { splatPrototypes[m] = new SplatPrototype(); HEU_LoadBufferVolumeLayer layer = terrainBuffers[t]._splatLayers[m]; Texture2D diffuseTexture = null; if (!string.IsNullOrEmpty(layer._diffuseTexturePath)) { diffuseTexture = HEU_MaterialFactory.LoadTexture(layer._diffuseTexturePath); } if (diffuseTexture == null) { diffuseTexture = defaultTexture; } splatPrototypes[m].texture = diffuseTexture; splatPrototypes[m].tileOffset = layer._tileOffset; if (layer._tileSize.magnitude == 0f && diffuseTexture != null) { // Use texture size if tile size is 0 layer._tileSize = new Vector2(diffuseTexture.width, diffuseTexture.height); } splatPrototypes[m].tileSize = layer._tileSize; splatPrototypes[m].metallic = layer._metallic; splatPrototypes[m].smoothness = layer._smoothness; if (!string.IsNullOrEmpty(layer._normalTexturePath)) { splatPrototypes[m].normalMap = HEU_MaterialFactory.LoadTexture(layer._normalTexturePath); } } terrainData.splatPrototypes = splatPrototypes; #endif // Set the splatmaps if (terrainBuffers[t]._splatMaps != null) { // Set the alphamap size before setting the alphamaps to get correct scaling // The alphamap size comes from the first alphamap layer int alphamapResolution = terrainBuffers[t]._heightMapWidth; if (numLayers > 1) { alphamapResolution = terrainBuffers[t]._splatLayers[1]._heightMapWidth; } terrainData.alphamapResolution = alphamapResolution; terrainData.SetAlphamaps(0, 0, terrainBuffers[t]._splatMaps); } // Set the tree scattering if (terrainBuffers[t]._scatterTrees != null) { HEU_TerrainUtility.ApplyScatterTrees(terrainData, terrainBuffers[t]._scatterTrees); } // Set the detail layers if (terrainBuffers[t]._detailPrototypes != null) { HEU_TerrainUtility.ApplyDetailLayers(terrain, terrainData, terrainBuffers[t]._detailProperties, terrainBuffers[t]._detailPrototypes, terrainBuffers[t]._detailMaps); } terrainBuffers[t]._generatedOutput = generatedOutput; _generatedOutputs.Add(generatedOutput); SetOutputVisiblity(terrainBuffers[t]); } } }
private void GenerateTerrain(List<HEU_LoadBufferVolume> terrainBuffers) { Transform parent = this.gameObject.transform; int numVolumes = terrainBuffers.Count; for(int t = 0; t < numVolumes; ++t) { if (terrainBuffers[t]._heightMap != null) { GameObject newGameObject = new GameObject("heightfield_" + terrainBuffers[t]._tileIndex); Transform newTransform = newGameObject.transform; newTransform.parent = parent; HEU_GeneratedOutput generatedOutput = new HEU_GeneratedOutput(); generatedOutput._outputData._gameObject = newGameObject; Terrain terrain = HEU_GeneralUtility.GetOrCreateComponent<Terrain>(newGameObject); TerrainCollider collider = HEU_GeneralUtility.GetOrCreateComponent<TerrainCollider>(newGameObject); if (!string.IsNullOrEmpty(terrainBuffers[t]._terrainDataPath)) { terrain.terrainData = HEU_AssetDatabase.LoadAssetAtPath(terrainBuffers[t]._terrainDataPath, typeof(TerrainData)) as TerrainData; if (terrain.terrainData == null) { Debug.LogWarningFormat("TerrainData, set via attribute, not found at: {0}", terrainBuffers[t]._terrainDataPath); } } if (terrain.terrainData == null) { terrain.terrainData = new TerrainData(); } TerrainData terrainData = terrain.terrainData; collider.terrainData = terrainData; HEU_TerrainUtility.SetTerrainMaterial(terrain); #if UNITY_2018_3_OR_NEWER terrain.allowAutoConnect = true; // This has to be set after setting material terrain.drawInstanced = true; #endif int heightMapSize = terrainBuffers[t]._heightMapWidth; terrainData.heightmapResolution = heightMapSize; if (terrainData.heightmapResolution != heightMapSize) { Debug.LogErrorFormat("Unsupported terrain size: {0}", heightMapSize); continue; } // The terrainData.baseMapResolution is not set here, but rather left to whatever default Unity uses // The terrainData.alphamapResolution is set later when setting the alphamaps. // 32 is the default for resolutionPerPatch const int detailResolution = 1024; const int resolutionPerPatch = 32; terrainData.SetDetailResolution(detailResolution, resolutionPerPatch); terrainData.SetHeights(0, 0, terrainBuffers[t]._heightMap); // Note that Unity uses a default height range of 600 when a flat terrain is created. // Without a non-zero value for the height range, user isn't able to draw heights. // Therefore, set 600 as the value if height range is currently 0 (due to flat heightfield). float heightRange = terrainBuffers[t]._heightRange; if (heightRange == 0) { heightRange = 600; } terrainData.size = new Vector3(terrainBuffers[t]._terrainSizeX, heightRange, terrainBuffers[t]._terrainSizeY); terrain.Flush(); // Set position HAPI_Transform hapiTransformVolume = new HAPI_Transform(true); hapiTransformVolume.position[0] += terrainBuffers[t]._position[0]; hapiTransformVolume.position[1] += terrainBuffers[t]._position[1]; hapiTransformVolume.position[2] += terrainBuffers[t]._position[2]; HEU_HAPIUtility.ApplyLocalTransfromFromHoudiniToUnity(ref hapiTransformVolume, newTransform); // Set layers Texture2D defaultTexture = HEU_VolumeCache.LoadDefaultSplatTexture(); int numLayers = terrainBuffers[t]._layers.Count; #if UNITY_2018_3_OR_NEWER // Create TerrainLayer for each heightfield layer. // Note that height and mask layers are ignored (i.e. not created as TerrainLayers). // Since height layer is first, only process layers from 2nd index onwards. if (numLayers > 1) { TerrainLayer[] terrainLayers = new TerrainLayer[numLayers - 1]; for (int m = 1; m < numLayers; ++m) { TerrainLayer terrainlayer = null; HEU_LoadBufferVolumeLayer layer = terrainBuffers[t]._layers[m]; // Look up TerrainLayer file via attribute if user has set it if (!string.IsNullOrEmpty(layer._layerPath)) { terrainlayer = HEU_AssetDatabase.LoadAssetAtPath(layer._layerPath, typeof(TerrainLayer)) as TerrainLayer; if (terrainlayer == null) { Debug.LogWarningFormat("TerrainLayer, set via attribute, not found at: {0}", layer._layerPath); continue; } } if (terrainlayer == null) { terrainlayer = new TerrainLayer(); } if (!string.IsNullOrEmpty(layer._diffuseTexturePath)) { terrainlayer.diffuseTexture = HEU_MaterialFactory.LoadTexture(layer._diffuseTexturePath); } if (terrainlayer.diffuseTexture == null) { terrainlayer.diffuseTexture = defaultTexture; } terrainlayer.diffuseRemapMin = Vector4.zero; terrainlayer.diffuseRemapMax = Vector4.one; if (!string.IsNullOrEmpty(layer._maskTexturePath)) { terrainlayer.maskMapTexture = HEU_MaterialFactory.LoadTexture(layer._maskTexturePath); } terrainlayer.maskMapRemapMin = Vector4.zero; terrainlayer.maskMapRemapMax = Vector4.one; terrainlayer.metallic = layer._metallic; if (!string.IsNullOrEmpty(layer._normalTexturePath)) { terrainlayer.normalMapTexture = HEU_MaterialFactory.LoadTexture(layer._normalTexturePath); } terrainlayer.normalScale = layer._normalScale; terrainlayer.smoothness = layer._smoothness; terrainlayer.specular = layer._specularColor; terrainlayer.tileOffset = layer._tileOffset; if (layer._tileSize.magnitude == 0f && terrainlayer.diffuseTexture != null) { // Use texture size if tile size is 0 layer._tileSize = new Vector2(terrainlayer.diffuseTexture.width, terrainlayer.diffuseTexture.height); } terrainlayer.tileSize = layer._tileSize; // Note index is m - 1 due to skipping height layer terrainLayers[m - 1] = terrainlayer; } terrainData.terrainLayers = terrainLayers; } #else // Need to create SplatPrototype for each layer in heightfield, representing the textures. SplatPrototype[] splatPrototypes = new SplatPrototype[numLayers]; for (int m = 0; m < numLayers; ++m) { splatPrototypes[m] = new SplatPrototype(); HEU_LoadBufferVolumeLayer layer = terrainBuffers[t]._layers[m]; Texture2D diffuseTexture = null; if (!string.IsNullOrEmpty(layer._diffuseTexturePath)) { diffuseTexture = HEU_MaterialFactory.LoadTexture(layer._diffuseTexturePath); } if (diffuseTexture == null) { diffuseTexture = defaultTexture; } splatPrototypes[m].texture = diffuseTexture; splatPrototypes[m].tileOffset = layer._tileOffset; if (layer._tileSize.magnitude == 0f && diffuseTexture != null) { // Use texture size if tile size is 0 layer._tileSize = new Vector2(diffuseTexture.width, diffuseTexture.height); } splatPrototypes[m].tileSize = layer._tileSize; splatPrototypes[m].metallic = layer._metallic; splatPrototypes[m].smoothness = layer._smoothness; if (!string.IsNullOrEmpty(layer._normalTexturePath)) { splatPrototypes[m].normalMap = HEU_MaterialFactory.LoadTexture(layer._normalTexturePath); } } terrainData.splatPrototypes = splatPrototypes; #endif // Set the splatmaps if (terrainBuffers[t]._splatMaps != null) { // Set the alphamap size before setting the alphamaps to get correct scaling // The alphamap size comes from the first alphamap layer int alphamapResolution = terrainBuffers[t]._heightMapWidth; if (numLayers > 1) { alphamapResolution = terrainBuffers[t]._layers[1]._heightMapWidth; } terrainData.alphamapResolution = alphamapResolution; terrainData.SetAlphamaps(0, 0, terrainBuffers[t]._splatMaps); } // Set the tree scattering if (terrainBuffers[t]._scatterTrees != null) { HEU_TerrainUtility.ApplyScatter(terrainData, terrainBuffers[t]._scatterTrees); } terrainBuffers[t]._generatedOutput = generatedOutput; _generatedOutputs.Add(generatedOutput); SetOutputVisiblity(terrainBuffers[t]); } } }
/// <summary> /// Apply the given detail layers and properties to the given terrain. /// The detail distance and resolution will be set, along with detail prototypes, and layers. /// </summary> /// <param name="terrain">The Terrain to set the detail properies on</param> /// <param name="terrainData">The TerrainData to apply the layers to</param> /// <param name="detailProperties">Container for detail distance and resolution</param> /// <param name="heuDetailPrototypes">Data for creating DetailPrototypes</param> /// <param name="convertedDetailMaps">The detail maps to set for the detail layers</param> public static void ApplyDetailLayers(Terrain terrain, TerrainData terrainData, HEU_DetailProperties detailProperties, List<HEU_DetailPrototype> heuDetailPrototypes, List<int[,]> convertedDetailMaps) { #if UNITY_2018_3_OR_NEWER if (detailProperties != null) { if (detailProperties._detailDistance >= 0) { terrain.detailObjectDistance = detailProperties._detailDistance; } if (detailProperties._detailDensity >= 0) { terrain.detailObjectDensity = detailProperties._detailDensity; } int resPerPath = detailProperties._detailResolutionPerPatch > 0 ? detailProperties._detailResolutionPerPatch : terrainData.detailResolutionPerPatch; int detailRes = detailProperties._detailResolution > 0 ? detailProperties._detailResolution : terrainData.detailResolutionPerPatch; if (resPerPath > 0 && detailRes > 0) { // This should match with half the terrain size terrainData.SetDetailResolution(detailRes, resPerPath); } } if (heuDetailPrototypes.Count != convertedDetailMaps.Count) { Debug.LogError("Number of volume detail layers differs from converted detail maps. Unable to apply detail layers."); return; } // For now, just override existing detail prototypes and layers // If user asks for appending/overwriting them, then can use a new index attribute to map them List<DetailPrototype> detailPrototypes = new List<DetailPrototype>(); int numDetailLayers = heuDetailPrototypes.Count; for(int i = 0; i < numDetailLayers; ++i) { DetailPrototype detailPrototype = new DetailPrototype(); HEU_DetailPrototype heuDetail = heuDetailPrototypes[i]; if (!string.IsNullOrEmpty(heuDetail._prototypePrefab)) { detailPrototype.prototype = HEU_AssetDatabase.LoadAssetAtPath(heuDetail._prototypePrefab, typeof(GameObject)) as GameObject; detailPrototype.usePrototypeMesh = true; } else if (!string.IsNullOrEmpty(heuDetail._prototypeTexture)) { detailPrototype.prototypeTexture = HEU_MaterialFactory.LoadTexture(heuDetail._prototypeTexture); detailPrototype.usePrototypeMesh = false; } detailPrototype.bendFactor = heuDetail._bendFactor; detailPrototype.dryColor = heuDetail._dryColor; detailPrototype.healthyColor = heuDetail._healthyColor; detailPrototype.maxHeight = heuDetail._maxHeight; detailPrototype.maxWidth = heuDetail._maxWidth; detailPrototype.minHeight = heuDetail._minHeight; detailPrototype.minWidth = heuDetail._minWidth; detailPrototype.noiseSpread = heuDetail._noiseSpread; detailPrototype.renderMode = (DetailRenderMode)heuDetail._renderMode; detailPrototypes.Add(detailPrototype); } // Set the DetailPrototypes if (detailPrototypes.Count > 0) { terrainData.detailPrototypes = detailPrototypes.ToArray(); } // Set the DetailLayers for(int i = 0; i < numDetailLayers; ++i) { terrainData.SetDetailLayer(0, 0, i, convertedDetailMaps[i]); } #endif }
public static bool GenerateMeshUsingGeoCache(HEU_SessionBase session, HEU_HoudiniAsset asset, GameObject gameObject, HEU_GenerateGeoCache geoCache, bool bGenerateUVs, bool bGenerateTangents, bool bPartInstanced) { #if HEU_PROFILER_ON float generateMeshTime = Time.realtimeSinceStartup; #endif string collisionGroupName = HEU_PluginSettings.CollisionGroupName; string renderCollisionGroupName = HEU_PluginSettings.RenderedCollisionGroupName; // Stores submesh data based on material key (ie. a submesh for each unique material) // Unity requires that if using multiple materials in the same GameObject, then we // need to create corresponding number of submeshes as materials. // So we'll create a submesh for each material in use. // Each submesh will have a list of vertices and their attributes which // we'll collect in a helper class (HEU_MeshData). // Once we collected all the submesh data, we create a CombineInstance for each // submesh, then combine it while perserving the submeshes. Dictionary<int, HEU_MeshData> subMeshesMap = new Dictionary<int, HEU_MeshData>(); string defaultMaterialName = HEU_HoudiniAsset.GenerateDefaultMaterialName(geoCache.GeoID, geoCache.PartID); int defaultMaterialKey = HEU_MaterialFactory.MaterialNameToKey(defaultMaterialName); int singleFaceUnityMaterialKey = HEU_Defines.HEU_INVALID_MATERIAL; int singleFaceHoudiniMaterialKey = HEU_Defines.HEU_INVALID_MATERIAL; // Now go through each group data and acquire the vertex data. // We'll create the collider mesh rightaway and assign to the gameobject. int numCollisionMeshes = 0; foreach (KeyValuePair<string, int[]> groupSplitFacesPair in geoCache._groupSplitVertexIndices) { string groupName = groupSplitFacesPair.Key; int[] groupVertexList = groupSplitFacesPair.Value; bool bIsCollidable = groupName.Contains(collisionGroupName); bool bIsRenderCollidable = groupName.Contains(renderCollisionGroupName); if (bIsCollidable || bIsRenderCollidable) { if (numCollisionMeshes > 0) { Debug.LogWarningFormat("More than 1 collision mesh detected for part {0}.\nOnly a single collision mesh is supported per part.", geoCache._partName); } if (geoCache._partInfo.type == HAPI_PartType.HAPI_PARTTYPE_BOX) { // Box collider HAPI_BoxInfo boxInfo = new HAPI_BoxInfo(); if (session.GetBoxInfo(geoCache.GeoID, geoCache.PartID, ref boxInfo)) { BoxCollider boxCollider = HEU_GeneralUtility.GetOrCreateComponent<BoxCollider>(gameObject); boxCollider.center = new Vector3(-boxInfo.center[0], boxInfo.center[1], boxInfo.center[2]); boxCollider.size = new Vector3(boxInfo.size[0] * 2f, boxInfo.size[1] * 2f, boxInfo.size[2] * 2f); // TODO: Should we apply the box info rotation here to the box collider? // If so, it should be in its own gameobject? } } else if (geoCache._partInfo.type == HAPI_PartType.HAPI_PARTTYPE_SPHERE) { // Sphere collider HAPI_SphereInfo sphereInfo = new HAPI_SphereInfo(); if (session.GetSphereInfo(geoCache.GeoID, geoCache.PartID, ref sphereInfo)) { SphereCollider sphereCollider = HEU_GeneralUtility.GetOrCreateComponent<SphereCollider>(gameObject); sphereCollider.center = new Vector3(-sphereInfo.center[0], sphereInfo.center[1], sphereInfo.center[2]); sphereCollider.radius = sphereInfo.radius; } } else { // Mesh collider List<Vector3> collisionVertices = new List<Vector3>(); for (int v = 0; v < groupVertexList.Length; ++v) { int index = groupVertexList[v]; if (index >= 0 && index < geoCache._posAttr.Length) { collisionVertices.Add(new Vector3(-geoCache._posAttr[index * 3], geoCache._posAttr[index * 3 + 1], geoCache._posAttr[index * 3 + 2])); } } int[] collisionIndices = new int[collisionVertices.Count]; for (int i = 0; i < collisionIndices.Length; ++i) { collisionIndices[i] = i; } Mesh collisionMesh = new Mesh(); #if UNITY_2017_3_OR_NEWER collisionMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; #endif collisionMesh.name = groupName; collisionMesh.vertices = collisionVertices.ToArray(); collisionMesh.triangles = collisionIndices; collisionMesh.RecalculateBounds(); MeshCollider meshCollider = HEU_GeneralUtility.GetOrCreateComponent<MeshCollider>(gameObject); meshCollider.sharedMesh = collisionMesh; } numCollisionMeshes++; } if (bIsCollidable && !bIsRenderCollidable) { continue; } // After this point, we'll be only processing renderable geometry // Transfer indices for each attribute from the single large list into group lists float[] groupColorAttr = new float[0]; HEU_GenerateGeoCache.TransferRegularPointAttributesToVertices(groupVertexList, ref geoCache._colorAttrInfo, geoCache._colorAttr, ref groupColorAttr); float[] groupAlphaAttr = new float[0]; HEU_GenerateGeoCache.TransferRegularPointAttributesToVertices(groupVertexList, ref geoCache._alphaAttrInfo, geoCache._alphaAttr, ref groupAlphaAttr); float[] groupNormalAttr = new float[0]; HEU_GenerateGeoCache.TransferRegularPointAttributesToVertices(groupVertexList, ref geoCache._normalAttrInfo, geoCache._normalAttr, ref groupNormalAttr); float[] groupTangentsAttr = new float[0]; HEU_GenerateGeoCache.TransferRegularPointAttributesToVertices(groupVertexList, ref geoCache._tangentAttrInfo, geoCache._tangentAttr, ref groupTangentsAttr); float[] groupUVAttr = new float[0]; HEU_GenerateGeoCache.TransferRegularPointAttributesToVertices(groupVertexList, ref geoCache._uvAttrInfo, geoCache._uvAttr, ref groupUVAttr); float[] groupUV2Attr = new float[0]; HEU_GenerateGeoCache.TransferRegularPointAttributesToVertices(groupVertexList, ref geoCache._uv2AttrInfo, geoCache._uv2Attr, ref groupUV2Attr); float[] groupUV3Attr = new float[0]; HEU_GenerateGeoCache.TransferRegularPointAttributesToVertices(groupVertexList, ref geoCache._uv3AttrInfo, geoCache._uv3Attr, ref groupUV3Attr); // Unity mesh creation requires # of vertices must equal # of attributes (color, normal, uvs). // HAPI gives us point indices. Since our attributes are via vertex, we need to therefore // create new indices of vertices that correspond to our attributes. // To reindex, we go through each index, add each attribute corresponding to that index to respective lists. // Then we set the index of where we added those attributes as the new index. int numIndices = groupVertexList.Length; for (int vertexIndex = 0; vertexIndex < numIndices; vertexIndex += 3) { // groupVertexList contains -1 for unused indices, and > 0 for used if (groupVertexList[vertexIndex] == -1) { continue; } int faceIndex = vertexIndex / 3; int faceMaterialID = geoCache._houdiniMaterialIDs[faceIndex]; // Get the submesh ID for this face. Depends on whether it is a Houdini or Unity material. // Using default material as failsafe int submeshID = HEU_Defines.HEU_INVALID_MATERIAL; if (geoCache._unityMaterialAttrInfo.exists) { // This face might have a Unity or Substance material attribute. // Formulate the submesh ID by combining the material attributes. if (geoCache._singleFaceUnityMaterial) { if (singleFaceUnityMaterialKey == HEU_Defines.HEU_INVALID_MATERIAL && geoCache._unityMaterialInfos.Count > 0) { // Use first material var unityMaterialMapEnumerator = geoCache._unityMaterialInfos.GetEnumerator(); if (unityMaterialMapEnumerator.MoveNext()) { singleFaceUnityMaterialKey = unityMaterialMapEnumerator.Current.Key; } } submeshID = singleFaceUnityMaterialKey; } else { int attrIndex = faceIndex; if (geoCache._unityMaterialAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_PRIM || geoCache._unityMaterialAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_POINT) { if (geoCache._unityMaterialAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_POINT) { attrIndex = groupVertexList[vertexIndex]; } string unityMaterialName = ""; string substanceName = ""; int substanceIndex = -1; submeshID = HEU_GenerateGeoCache.GetMaterialKeyFromAttributeIndex(geoCache, attrIndex, out unityMaterialName, out substanceName, out substanceIndex); } else { // (geoCache._unityMaterialAttrInfo.owner == HAPI_AttributeOwner.HAPI_ATTROWNER_DETAIL) should have been handled as geoCache._singleFaceMaterial above Debug.LogErrorFormat("Unity material attribute not supported for attribute type {0}!", geoCache._unityMaterialAttrInfo.owner); } } } if (submeshID == HEU_Defines.HEU_INVALID_MATERIAL) { // Check if has Houdini material assignment if (geoCache._houdiniMaterialIDs.Length > 0) { if (geoCache._singleFaceHoudiniMaterial) { if (singleFaceHoudiniMaterialKey == HEU_Defines.HEU_INVALID_MATERIAL) { singleFaceHoudiniMaterialKey = geoCache._houdiniMaterialIDs[0]; } submeshID = singleFaceHoudiniMaterialKey; } else if (faceMaterialID > 0) { submeshID = faceMaterialID; } } if (submeshID == HEU_Defines.HEU_INVALID_MATERIAL) { // Use default material submeshID = defaultMaterialKey; } } if (!subMeshesMap.ContainsKey(submeshID)) { // New submesh subMeshesMap.Add(submeshID, new HEU_MeshData()); } HEU_MeshData subMeshData = subMeshesMap[submeshID]; for (int triIndex = 0; triIndex < 3; ++triIndex) { int vertexTriIndex = vertexIndex + triIndex; int positionIndex = groupVertexList[vertexTriIndex]; // Position Vector3 position = new Vector3(-geoCache._posAttr[positionIndex * 3 + 0], geoCache._posAttr[positionIndex * 3 + 1], geoCache._posAttr[positionIndex * 3 + 2]); subMeshData._vertices.Add(position); // Color if (geoCache._colorAttrInfo.exists) { Color tempColor = new Color(); tempColor.r = Mathf.Clamp01(groupColorAttr[vertexTriIndex * geoCache._colorAttrInfo.tupleSize + 0]); tempColor.g = Mathf.Clamp01(groupColorAttr[vertexTriIndex * geoCache._colorAttrInfo.tupleSize + 1]); tempColor.b = Mathf.Clamp01(groupColorAttr[vertexTriIndex * geoCache._colorAttrInfo.tupleSize + 2]); if (geoCache._alphaAttrInfo.exists) { tempColor.a = Mathf.Clamp01(groupAlphaAttr[vertexTriIndex]); } else if (geoCache._colorAttrInfo.tupleSize == 4) { tempColor.a = Mathf.Clamp01(groupColorAttr[vertexTriIndex * geoCache._colorAttrInfo.tupleSize + 3]); } else { tempColor.a = 1f; } subMeshData._colors.Add(tempColor); } else { subMeshData._colors.Add(Color.white); } // Normal if (vertexTriIndex < groupNormalAttr.Length) { // Flip the x Vector3 normal = new Vector3(-groupNormalAttr[vertexTriIndex * 3 + 0], groupNormalAttr[vertexTriIndex * 3 + 1], groupNormalAttr[vertexTriIndex * 3 + 2]); subMeshData._normals.Add(normal); } else { // We'll be calculating normals later subMeshData._normals.Add(Vector3.zero); } // UV1 if (vertexTriIndex < groupUVAttr.Length) { Vector2 uv = new Vector2(groupUVAttr[vertexTriIndex * 2 + 0], groupUVAttr[vertexTriIndex * 2 + 1]); subMeshData._UVs.Add(uv); } // UV2 if (vertexTriIndex < groupUV2Attr.Length) { Vector2 uv = new Vector2(groupUV2Attr[vertexTriIndex * 2 + 0], groupUV2Attr[vertexTriIndex * 2 + 1]); subMeshData._UV2s.Add(uv); } // UV3 if (vertexTriIndex < groupUV3Attr.Length) { Vector2 uv = new Vector2(groupUV3Attr[vertexTriIndex * 2 + 0], groupUV3Attr[vertexTriIndex * 2 + 1]); subMeshData._UV3s.Add(uv); } // Tangents if (bGenerateTangents && vertexTriIndex < groupTangentsAttr.Length) { Vector4 tangent = Vector4.zero; if (geoCache._tangentAttrInfo.tupleSize == 4) { tangent = new Vector4(-groupTangentsAttr[vertexTriIndex * 4 + 0], groupTangentsAttr[vertexTriIndex * 4 + 1], groupTangentsAttr[vertexTriIndex * 4 + 2], groupTangentsAttr[vertexTriIndex * 4 + 3]); } else if (geoCache._tangentAttrInfo.tupleSize == 3) { tangent = new Vector4(-groupTangentsAttr[vertexTriIndex * 3 + 0], groupTangentsAttr[vertexTriIndex * 3 + 1], groupTangentsAttr[vertexTriIndex * 3 + 2], 1); } subMeshData._tangents.Add(tangent); } subMeshData._indices.Add(subMeshData._vertices.Count - 1); //Debug.LogFormat("Submesh index mat {0} count {1}", faceMaterialID, subMeshData._indices.Count); } if (!geoCache._normalAttrInfo.exists) { // To generate normals after all the submeshes have been defined, we // calculate and store each triangle normal, along with the list // of connected vertices for each vertex int triIndex = subMeshData._indices.Count - 3; int i1 = subMeshData._indices[triIndex + 0]; int i2 = subMeshData._indices[triIndex + 1]; int i3 = subMeshData._indices[triIndex + 2]; // Triangle normal Vector3 p1 = subMeshData._vertices[i2] - subMeshData._vertices[i1]; Vector3 p2 = subMeshData._vertices[i3] - subMeshData._vertices[i1]; Vector3 normal = Vector3.Cross(p1, p2).normalized; subMeshData._triangleNormals.Add(normal); int normalIndex = subMeshData._triangleNormals.Count - 1; // Connected vertices geoCache._sharedNormalIndices[groupVertexList[vertexIndex + 0]].Add(new VertexEntry(submeshID, i1, normalIndex)); geoCache._sharedNormalIndices[groupVertexList[vertexIndex + 1]].Add(new VertexEntry(submeshID, i2, normalIndex)); geoCache._sharedNormalIndices[groupVertexList[vertexIndex + 2]].Add(new VertexEntry(submeshID, i3, normalIndex)); } } } int numSubmeshes = subMeshesMap.Keys.Count; bool bGenerated = false; if (numSubmeshes > 0) { if (!geoCache._normalAttrInfo.exists) { // Normal calculation // Go throuch each vertex for the entire geometry and calculate the normal vector based on connected // vertices. This includes vertex connections between submeshes so we should get smooth transitions across submeshes. int numSharedNormals = geoCache._sharedNormalIndices.Length; for (int a = 0; a < numSharedNormals; ++a) { for (int b = 0; b < geoCache._sharedNormalIndices[a].Count; ++b) { Vector3 sumNormal = new Vector3(); VertexEntry leftEntry = geoCache._sharedNormalIndices[a][b]; HEU_MeshData leftSubMesh = subMeshesMap[leftEntry._meshKey]; List<VertexEntry> rightList = geoCache._sharedNormalIndices[a]; for (int c = 0; c < rightList.Count; ++c) { VertexEntry rightEntry = rightList[c]; HEU_MeshData rightSubMesh = subMeshesMap[rightEntry._meshKey]; if (leftEntry._vertexIndex == rightEntry._vertexIndex) { sumNormal += rightSubMesh._triangleNormals[rightEntry._normalIndex]; } else { float dot = Vector3.Dot(leftSubMesh._triangleNormals[leftEntry._normalIndex], rightSubMesh._triangleNormals[rightEntry._normalIndex]); if (dot >= geoCache._cosineThreshold) { sumNormal += rightSubMesh._triangleNormals[rightEntry._normalIndex]; } } } leftSubMesh._normals[leftEntry._vertexIndex] = sumNormal.normalized; } } } // Go through each valid submesh data and upload into a CombineInstance for combining. // Each CombineInstance represents a submesh in the final mesh. // And each submesh in that final mesh corresponds to a material. // Filter out only the submeshes with valid geometry List<Material> validMaterials = new List<Material>(); List<int> validSubmeshes = new List<int>(); Dictionary<int, HEU_MaterialData> assetMaterialMap = asset.GetMaterialDataMap(); foreach (KeyValuePair<int, HEU_MeshData> meshPair in subMeshesMap) { HEU_MeshData meshData = meshPair.Value; if (meshData._indices.Count > 0) { int materialKey = meshPair.Key; // Find the material or create it HEU_MaterialData materialData = null; HEU_UnityMaterialInfo unityMaterialInfo = null; if (geoCache._unityMaterialInfos.TryGetValue(materialKey, out unityMaterialInfo)) { if (!assetMaterialMap.TryGetValue(materialKey, out materialData)) { // Create the material materialData = asset.CreateUnitySubstanceMaterialData(materialKey, unityMaterialInfo._unityMaterialPath, unityMaterialInfo._substancePath, unityMaterialInfo._substanceIndex); assetMaterialMap.Add(materialData._materialKey, materialData); } } else if (!assetMaterialMap.TryGetValue(materialKey, out materialData)) { if (materialKey == defaultMaterialKey) { materialData = asset.GetOrCreateDefaultMaterialInCache(session, geoCache.GeoID, geoCache.PartID, false); } else { materialData = asset.CreateHoudiniMaterialData(session, materialKey, geoCache.GeoID, geoCache.PartID); } } if (materialData != null) { validSubmeshes.Add(meshPair.Key); validMaterials.Add(materialData._material); if (materialData != null && bPartInstanced) { // Handle GPU instancing on material for instanced meshes if (materialData._materialSource != HEU_MaterialData.Source.UNITY && materialData._materialSource != HEU_MaterialData.Source.SUBSTANCE) { // Always enable GPU instancing for material generated from Houdini HEU_MaterialFactory.EnableGPUInstancing(materialData._material); } } } } } int validNumSubmeshes = validSubmeshes.Count; CombineInstance[] meshCombiner = new CombineInstance[validNumSubmeshes]; for (int submeshIndex = 0; submeshIndex < validNumSubmeshes; ++submeshIndex) { HEU_MeshData submesh = subMeshesMap[validSubmeshes[submeshIndex]]; CombineInstance combine = new CombineInstance(); combine.mesh = new Mesh(); #if UNITY_2017_3_OR_NEWER combine.mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; #endif combine.mesh.SetVertices(submesh._vertices); combine.mesh.SetIndices(submesh._indices.ToArray(), MeshTopology.Triangles, 0); if (submesh._colors.Count > 0) { combine.mesh.SetColors(submesh._colors); } if (submesh._normals.Count > 0) { combine.mesh.SetNormals(submesh._normals); } if (submesh._tangents.Count > 0) { combine.mesh.SetTangents(submesh._tangents); } if (bGenerateUVs) { // TODO: revisit to test this out Vector2[] generatedUVs = HEU_GeometryUtility.GeneratePerTriangle(combine.mesh); if (generatedUVs != null) { combine.mesh.uv = generatedUVs; } } else if (submesh._UVs.Count > 0) { combine.mesh.SetUVs(0, submesh._UVs); } if (submesh._UV2s.Count > 0) { combine.mesh.SetUVs(1, submesh._UV2s); } if (submesh._UV3s.Count > 0) { combine.mesh.SetUVs(2, submesh._UV3s); } combine.transform = Matrix4x4.identity; combine.mesh.RecalculateBounds(); //Debug.LogFormat("Number of submeshes {0}", combine.mesh.subMeshCount); meshCombiner[submeshIndex] = combine; } // Geometry data MeshFilter meshFilter = HEU_GeneralUtility.GetOrCreateComponent<MeshFilter>(gameObject); meshFilter.sharedMesh = new Mesh(); #if UNITY_2017_3_OR_NEWER meshFilter.sharedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; #endif meshFilter.sharedMesh.name = geoCache._partName + "_mesh"; meshFilter.sharedMesh.CombineMeshes(meshCombiner, false, false); meshFilter.sharedMesh.RecalculateBounds(); if (!geoCache._tangentAttrInfo.exists && bGenerateTangents) { HEU_GeometryUtility.CalculateMeshTangents(meshFilter.sharedMesh); } meshFilter.sharedMesh.UploadMeshData(true); // Render data MeshRenderer meshRenderer = HEU_GeneralUtility.GetOrCreateComponent<MeshRenderer>(gameObject); meshRenderer.sharedMaterials = validMaterials.ToArray(); bGenerated = true; } #if HEU_PROFILER_ON Debug.LogFormat("GENERATE MESH TIME:: {0}", (Time.realtimeSinceStartup - generateMeshTime)); #endif return bGenerated; }