Automatic texture RGB and A merge on change

Hi all :slight_smile:

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:

Here’s the script of tex_postpro.cs :

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEngine.UI;

[Serializable]
public class def_texture_pair
{
   public Texture2D RGB,ALPHA;
}



public class tex_postpro : MonoBehaviour, ISerializationCallbackReceiver
{
   public static def_texture_pair[] textures_pairs;
   public static bool enable_treatment=true;
 

   public def_texture_pair[] tex;                  //don't use for anything else
   public bool TexMergeEnable=true;

   public void OnAfterDeserialize()
   {
      textures_pairs = new def_texture_pair[tex.Length];
 
      int i=0;
      foreach(def_texture_pair dp in tex)
      {
         textures_pairs[i]=dp;
         i++;
      }
 
      enable_treatment = TexMergeEnable;
   }

   public void OnBeforeSerialize()
   {
      //intSerializationHelper = myStaticInt;  // uncomment this if you want the field to reflect the value of the static field on playmode
   }


}

// Postprocesses textures stored in the inspector....
public class Texture_RGBA_Merge : AssetPostprocessor
{
//bool memo_is_readable=false;
 
   void print(string s)
   {
      Debug.Log(s);
   }
 
 
 

 
   void OnPostprocessTexture(Texture2D texture)
   {
   bool process_textures = false;
  
   string texture_asset_path = assetPath;
   string path_RGB="",path_ALPHA="";
 
   def_texture_pair found=null;
 
      if(!tex_postpro.enable_treatment) return;
 
 
      foreach(def_texture_pair dp in tex_postpro.textures_pairs)
      {
         path_RGB = AssetDatabase.GetAssetPath(dp.RGB);
         path_ALPHA = AssetDatabase.GetAssetPath(dp.ALPHA);
    
//         Debug.Log("tex = "+assetPath);
//         Debug.Log("RGB = "+path_RGB);
//         Debug.Log("ALPHA = "+path_ALPHA);
    
//         Debug.Log(""+texture_asset_path.IndexOf(path_RGB));
//         Debug.Log(""+texture_asset_path.IndexOf(path_ALPHA));
    
    
         if(texture_asset_path.IndexOf(path_RGB) != -1 ||
            texture_asset_path.IndexOf(path_ALPHA) != -1)
         {
            print("entering in import of :"+assetPath);
 
            process_textures = true;
       
            found = dp;
         }
      }
   
   
   
      if (process_textures == false)
      {
         print("Nothing to do");
         return;
      }

      path_RGB = AssetDatabase.GetAssetPath(found.RGB);
      path_ALPHA = AssetDatabase.GetAssetPath(found.ALPHA);
 
      // set the output filename and path
      string filename_out = Path.GetFileNameWithoutExtension(path_RGB)+Path.GetFileNameWithoutExtension(path_ALPHA)+".png";
      string path_out = Path.GetDirectoryName(path_RGB)+"\\"+filename_out;
 
      Debug.Log("Processing & exporting : "+path_out);

      // get the RGB and ALPHA textures
      Texture2D rgb = found.RGB;
      Texture2D a = found.ALPHA;

      // control both input textures have the same size....
      if(rgb.width != a.width || rgb.height != a.height)
      {
         print("Textures must have the same size !!!! ABORTING.");
         return;
      }
 
 
      // allocate the target texture or load the existing one
      Texture2D dest;

      dest = new Texture2D( rgb.width,rgb.height,TextureFormat.ARGB32,false);

      Color[] rgb_buffer = rgb.GetPixels(0);
      Color[] a_buffer = a.GetPixels(0);
 
      // create the output texture ( we'll use the rgb color buffer for sparing mem... )
      for (int i = 0; i < rgb_buffer.Length; i++)
      {
         rgb_buffer[i].a = a_buffer[i].r;
      }
 
      dest.SetPixels(rgb_buffer,0);
      dest.Apply();
 
      // should we write a new file ?
      // Encode texture into PNG
      byte[] bytes = dest.EncodeToPNG();

      // write the output file
      File.WriteAllBytes(path_out, bytes);

      TextureImporter tImporter = AssetImporter.GetAtPath( path_out ) as TextureImporter;
//      AssetDatabase.SaveAssets();
        tImporter.SaveAndReimport();
        AssetDatabase.Refresh();
   }
}

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 :slight_smile:

Just wanted to share :wink:

Happy unitying !

I got hard time with this script…
though it works fine, it doesn’t free memory wich gets quickly saturated with 8K textures :confused:

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

anyone could gimme some advices please ?

thanks in advance and happy unitying !

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 :wink: )

Tell me how to free memory !

Thanks in advance :slight_smile:

and happy unitying !

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 :slight_smile: and happy unitying !

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?

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 ?

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)