Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Question Automatic texture RGB and A merge on change

Discussion in 'Scripting' started by grobonom, Jul 15, 2020.

  1. grobonom

    grobonom

    Joined:
    Jun 23, 2018
    Posts:
    335
    Hi all :)

    I got 2 textures:
    1 with RGB
    1 with ALPHA

    I need to edit them ( eg in GIMP ) separately and when they're changed i need to merge them in an RGBA unique texture.
    Of course i can do this by hand with some tools but as those textures are often edited, i want to automate the process.

    Therefore i used the OnPostprocessTexture from the asset postprocessor.
    I also wanted to keep a list of all RGB+A textures pairs in the editor.
    For this i made a small monobehaviour script that i put on an 'EditorOnly' gameobject:
    tex_postpro_setup.jpg

    Here's the script of tex_postpro.cs :
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System.IO;
    5. using UnityEngine;
    6. using UnityEditor;
    7. using UnityEngine.UI;
    8.  
    9. [Serializable]
    10. public class def_texture_pair
    11. {
    12.    public Texture2D RGB,ALPHA;
    13. }
    14.  
    15.  
    16.  
    17. public class tex_postpro : MonoBehaviour, ISerializationCallbackReceiver
    18. {
    19.    public static def_texture_pair[] textures_pairs;
    20.    public static bool enable_treatment=true;
    21.  
    22.  
    23.    public def_texture_pair[] tex;                  //don't use for anything else
    24.    public bool TexMergeEnable=true;
    25.  
    26.    public void OnAfterDeserialize()
    27.    {
    28.       textures_pairs = new def_texture_pair[tex.Length];
    29.  
    30.       int i=0;
    31.       foreach(def_texture_pair dp in tex)
    32.       {
    33.          textures_pairs[i]=dp;
    34.          i++;
    35.       }
    36.  
    37.       enable_treatment = TexMergeEnable;
    38.    }
    39.  
    40.    public void OnBeforeSerialize()
    41.    {
    42.       //intSerializationHelper = myStaticInt;  // uncomment this if you want the field to reflect the value of the static field on playmode
    43.    }
    44.  
    45.  
    46. }
    47.  
    48. // Postprocesses textures stored in the inspector....
    49. public class Texture_RGBA_Merge : AssetPostprocessor
    50. {
    51. //bool memo_is_readable=false;
    52.  
    53.    void print(string s)
    54.    {
    55.       Debug.Log(s);
    56.    }
    57.  
    58.  
    59.  
    60.  
    61.  
    62.    void OnPostprocessTexture(Texture2D texture)
    63.    {
    64.    bool process_textures = false;
    65.  
    66.    string texture_asset_path = assetPath;
    67.    string path_RGB="",path_ALPHA="";
    68.  
    69.    def_texture_pair found=null;
    70.  
    71.       if(!tex_postpro.enable_treatment) return;
    72.  
    73.  
    74.       foreach(def_texture_pair dp in tex_postpro.textures_pairs)
    75.       {
    76.          path_RGB = AssetDatabase.GetAssetPath(dp.RGB);
    77.          path_ALPHA = AssetDatabase.GetAssetPath(dp.ALPHA);
    78.    
    79. //         Debug.Log("tex = "+assetPath);
    80. //         Debug.Log("RGB = "+path_RGB);
    81. //         Debug.Log("ALPHA = "+path_ALPHA);
    82.    
    83. //         Debug.Log(""+texture_asset_path.IndexOf(path_RGB));
    84. //         Debug.Log(""+texture_asset_path.IndexOf(path_ALPHA));
    85.    
    86.    
    87.          if(texture_asset_path.IndexOf(path_RGB) != -1 ||
    88.             texture_asset_path.IndexOf(path_ALPHA) != -1)
    89.          {
    90.             print("entering in import of :"+assetPath);
    91.  
    92.             process_textures = true;
    93.        
    94.             found = dp;
    95.          }
    96.       }
    97.    
    98.    
    99.    
    100.       if (process_textures == false)
    101.       {
    102.          print("Nothing to do");
    103.          return;
    104.       }
    105.  
    106.       path_RGB = AssetDatabase.GetAssetPath(found.RGB);
    107.       path_ALPHA = AssetDatabase.GetAssetPath(found.ALPHA);
    108.  
    109.       // set the output filename and path
    110.       string filename_out = Path.GetFileNameWithoutExtension(path_RGB)+Path.GetFileNameWithoutExtension(path_ALPHA)+".png";
    111.       string path_out = Path.GetDirectoryName(path_RGB)+"\\"+filename_out;
    112.  
    113.       Debug.Log("Processing & exporting : "+path_out);
    114.  
    115.       // get the RGB and ALPHA textures
    116.       Texture2D rgb = found.RGB;
    117.       Texture2D a = found.ALPHA;
    118.  
    119.       // control both input textures have the same size....
    120.       if(rgb.width != a.width || rgb.height != a.height)
    121.       {
    122.          print("Textures must have the same size !!!! ABORTING.");
    123.          return;
    124.       }
    125.  
    126.  
    127.       // allocate the target texture or load the existing one
    128.       Texture2D dest;
    129.  
    130.       dest = new Texture2D( rgb.width,rgb.height,TextureFormat.ARGB32,false);
    131.  
    132.       Color[] rgb_buffer = rgb.GetPixels(0);
    133.       Color[] a_buffer = a.GetPixels(0);
    134.  
    135.       // create the output texture ( we'll use the rgb color buffer for sparing mem... )
    136.       for (int i = 0; i < rgb_buffer.Length; i++)
    137.       {
    138.          rgb_buffer[i].a = a_buffer[i].r;
    139.       }
    140.  
    141.       dest.SetPixels(rgb_buffer,0);
    142.       dest.Apply();
    143.  
    144.       // should we write a new file ?
    145.       // Encode texture into PNG
    146.       byte[] bytes = dest.EncodeToPNG();
    147.  
    148.       // write the output file
    149.       File.WriteAllBytes(path_out, bytes);
    150.  
    151.       TextureImporter tImporter = AssetImporter.GetAtPath( path_out ) as TextureImporter;
    152. //      AssetDatabase.SaveAssets();
    153.         tImporter.SaveAndReimport();
    154.         AssetDatabase.Refresh();
    155.    }
    156. }

    The strange part is that i get the message:
    A default asset was created for 'Assets/scripts/test_postpro_textures/B.png' because the asset importer crashed on it last time.
    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.


    However, things seem to work fine and could automate many textures pairs :)

    Just wanted to share ;)

    Happy unitying !
     
    Last edited: Jul 15, 2020
  2. grobonom

    grobonom

    Joined:
    Jun 23, 2018
    Posts:
    335
    I got hard time with this script...
    though it works fine, it doesn't free memory wich gets quickly saturated with 8K textures :/

    The Destroy or DestroyImmediate seems not usable in the postprocessor domain :(

    anyone could gimme some advices please ?

    thanks in advance and happy unitying !
     
  3. grobonom

    grobonom

    Joined:
    Jun 23, 2018
    Posts:
    335
    This really works like a charm !!!!
    An extremely convenient texture import-merger !!!!

    I guess this could heavily enhance users workflow.

    But....

    Memory allocation is a pain and not freed when script finishes...

    PLEAAAAASE !
    Any U3D dev light my candle ?
    Or maybe a good C# dev ( that my script will make laugh ;) )

    Tell me how to free memory !

    Thanks in advance :)

    and happy unitying !
     
  4. grobonom

    grobonom

    Joined:
    Jun 23, 2018
    Posts:
    335
    Bump on the later post for the memory problem ?

    Please..... because i often have to restart unity to recover memory.

    Destroy DO NOT WORK !

    Help !

    Thanks :) and happy unitying !
     
  5. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    1,094
    What is the reason you need them apart so badly?
    Can't you just have 2 layers in gimp and export them as a single png?
    Or are you sharing the alpha texture with many other textures?
     
  6. grobonom

    grobonom

    Joined:
    Jun 23, 2018
    Posts:
    335
    because i edit alpha and RGB separately very often and i was bored with merging in gimp or with u3D addon texture layer merge. I simply wanted to automate this simple task.
    Anyway the question is not why i do what i do but why it eats memory that is not freed untill i quit and restart unity ?
     
  7. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    1,094
    Just tested it, seems that it automatically cleans up.
    But if you need to free up memory during the process you can still use
    UnityEngine.Object.Destroy(TheTexture)
     
    Last edited: Aug 16, 2020