Search Unity

Editor Script Processing textures on Import

Discussion in 'Scripting' started by MagnusChristensson, Nov 19, 2017.

  1. MagnusChristensson

    MagnusChristensson

    Joined:
    Apr 12, 2015
    Posts:
    2
    Hello community, first time posting!
    I am trying to make an Editor script that on import of textures into a certain folder it creates a material with the imported texture name and then depending on what type of texture it is, put it into the correct channel of the PBR Material. So far I have gotten everything to work as intended except for actually placing the texture inside the correct channel of the material. The script is later on going to do alot more checks and fix settings of imported textures since it will be handeling .dds textures. But the script has been tested with all types of textures and still the same result. However if I just delete the material and press re-import on the texture when it is already inside unity it works and the texture is placed in the albedo channel as intended.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5. using System.IO;
    6.  
    7. public class MaterialProcessor : AssetPostprocessor {
    8.  
    9.     public void OnPostprocessTexture(Texture2D texture)
    10.     {
    11.         bool isInMaterialsDirectory = assetPath.IndexOf("/Materials/") != -1;
    12.    
    13.         if (isInMaterialsDirectory)
    14.         {
    15.             string currentTexture = Path.GetFileName(assetPath);
    16.             string[] materialsPath = new string[1];
    17.             materialsPath[0] = "Assets/Materials";
    18.             currentTexture.Contains("Albedo");
    19.  
    20.             if (true)
    21.             {
    22.                 string textureName = currentTexture.Replace("_Albedo.tga", "");
    23.                 string [] isInMaterials = AssetDatabase.FindAssets(textureName, materialsPath);
    24.                 if (isInMaterials.Length == 0)
    25.                 {
    26.                     CreateNewMaterial(textureName, texture, assetPath);
    27.                 }
    28.             }
    29.         }
    30.     }
    31.     void CreateNewMaterial(string materialName, Texture2D texture, string anAssetPath)
    32.     {
    33.  
    34.         Material newMat = new Material(Shader.Find("Standard (Roughness setup)"));
    35.          
    36.         AssetDatabase.CreateAsset(newMat, "Assets/Materials/" + materialName + ".mat");
    37.  
    38.         Texture2D tex = AssetDatabase.LoadAssetAtPath(anAssetPath, typeof(Texture2D)) as Texture2D;
    39.    
    40.         Debug.Log(tex);
    41.  
    42.         newMat.SetTexture("_MainTex", tex);
    43.  
    44.         AssetDatabase.Refresh();
    45.  
    46.     }
    47. }
    I have also tried using the "Texture2D texture" that is the imported texture that triggers the Script, but this was not working at all, so I chnged the code to get the texture at it's new location instead which seems to be working for most part.

    On first Import the Debug.Log(tex) returns Null in the console and this is produced by unity on the AssetDatabase.Refresh();

    Code (CSharp):
    1. A default asset was created for 'Assets/Materials/Test_Albedo.tga' because the asset importer crashed on it last time.
    2. You can select the asset and use the 'Assets -> Reimport' menu command to try importing it again, or you can replace the asset and it will auto import again.
    3. UnityEditor.AssetDatabase:Refresh()
    4. MaterialProcessor:CreateNewMaterial(String, Texture2D, String) (at Assets/Editor/MaterialProcessor.cs:46)
    5. MaterialProcessor:OnPostprocessTexture(Texture2D) (at Assets/Editor/MaterialProcessor.cs:28)
    6. UnityEditor.AssetPostprocessingInternal:PostprocessTexture(Texture2D, String)
    7. UnityEditorInternal.InternalEditorUtility:ProjectWindowDrag(HierarchyProperty, Boolean)
    8. UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)
    I did at one point in time have it all working and putting the albedo inside the correct channel on first import. But I then had so much code that was commented out and also had some lines that seemed to be doing the same thing in a diffrent way. During the cleanup I removed some of this code and stupidly enough did not save a working copy before I did, and it seems one of the code snippets seemingly doing the same thing as another one was crucial to make this work. I do believe it had to do with the assignment of the texture down at the bottom of the script. What the fixed my problem with not having to re-import when I had it all working was the adding of the AssetDatabase.Refresh(); in the end of the script. I do realise alot of this cod may be improved upon so please, I am open for suggestions.
     
  2. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    I don't think the asset exists yet while it's getting imported, so the AssetDatabase.LoadAssetAtPath plan is probably a bad idea. Essentially what you get to know in the importer during OnPostProcessTexture is where the texture will be created.

    I got it working by creating the material during OnPostProcessTexture, and then using OnPostProcessAllAssets to load the texture and material, and then combine them. It seems like OnPostProcessAllAssets happens after the texture is actually loadable.

    Code (csharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.IO;
    4.  
    5. public class MaterialProcessorWorking : AssetPostprocessor
    6. {
    7.     private static string createdMaterialName;
    8.     private static string createdTextureName;
    9.  
    10.     public void OnPostprocessTexture(Texture2D texture)
    11.     {
    12.         bool isInMaterialsDirectory = assetPath.Contains("/Materials/");
    13.  
    14.         if (isInMaterialsDirectory)
    15.         {
    16.             string currentTexture = Path.GetFileName(assetPath).Split('.')[0];
    17.             Material newMat = new Material(Shader.Find("Standard (Roughness setup)"));
    18.            
    19.             createdTextureName = assetPath;
    20.             createdMaterialName = "Assets/Materials/" + currentTexture + ".mat";
    21.  
    22.             if (AssetDatabase.LoadAssetAtPath<Material>(createdMaterialName))
    23.             {
    24.                 //material already exists, don't overwrite
    25.                 createdTextureName = null;
    26.                 createdMaterialName = null;
    27.             }
    28.             else
    29.             {
    30.                 AssetDatabase.CreateAsset(newMat, createdMaterialName);
    31.             }
    32.         }
    33.     }
    34.  
    35.     private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    36.     {
    37.         if (string.IsNullOrEmpty(createdMaterialName))
    38.             return;
    39.  
    40.         var createdTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(createdTextureName);
    41.         var createdMaterial = AssetDatabase.LoadAssetAtPath<Material>(createdMaterialName);
    42.         createdMaterial.mainTexture = createdTexture;
    43.  
    44.         createdMaterialName = null;
    45.         createdTextureName = null;
    46.     }
    47. }
     
    MagnusChristensson likes this.
  3. MagnusChristensson

    MagnusChristensson

    Joined:
    Apr 12, 2015
    Posts:
    2
    Thank you Baste! I will try this out tomorrow when I get back to work on it.