Changing texture import settings during runtime

I’m working on a painting game that has to copy texture files during runtime, but I’m running into problems getting everything to work as desired.

After a lot of goofing around with all kinds of different copy, delete, and load functions I figured out that I need to be able to change import settings on individual textures during runtime. (OR at the very least I need to be able to enable read/write to a texture file)

Also: I want to avoid a situation that enables read/write for every texture in the game, ideally I would like to only flag the files I want for read/write.

Any insight?

I ran into a similar problem in my own little paint program. I have figured out how to change their settings by using the AssetPostprocessor. As an example :

using UnityEngine;
using UnityEditor;

public class TexturePostProcessor : AssetPostprocessor
{
    
	void OnPreprocessTexture()
    {
		
		if(assetPath.Contains("DirectoryOfInterest"))
		{
			TextureImporter importer = assetImporter as TextureImporter;
			importer.textureType  = TextureImporterType.Advanced;
			importer.textureFormat = TextureImporterFormat.AutomaticTruecolor;
			importer.isReadable = true;
			importer.filterMode = FilterMode.Point;
			importer.npotScale = TextureImporterNPOTScale.None;
	 
			Object asset = AssetDatabase.LoadAssetAtPath(importer.assetPath, typeof(Texture2D));
			if (asset)
			{
				EditorUtility.SetDirty(asset);
			}
			else
			{
			   importer.textureType  = TextureImporterType.Advanced ;   
			}
		}
		
    }
}

You see here I’m able to change tons of different settings for the texture, including but not limited to read/write capability. What we are doing here is highjacking the importer before it actually applies the default settings, and applying our OWN default settings. This script must be placed inside a folder called “Editor” inside your asset hierarchy. By using if(assetPath.Contains(“YourDirectoryHere”)) you can change the import settings of files in a particular folder only. You can also have this check the name. For example if the name of your file was always going to be TempImageFIle, you could use assetPath.Contains(“TempImageFile”) to find it.

Finally, if create an image during runtime and want to be able to load the file without actually restarting the program, you need to refresh the unity assets. To do this you use AssetDataBase.Refresh(); Make sure whatever script calls AssetDataBase.Refresh() is using UnityEditor!

The imported texture in the editor is not the texture that will be available in the runtime. (You can’t change it during a real game build’s runtime as there’s no longer a untouched source texture to work from.)

You can manipulate it anyway you wish using Texture2D.SetPixels, etc. Keep in mind it creates additional ram/cpu usage.

If you can achieve the desired effects using shaders instead that’s recommended.

I’ve made painting using GPU, that’s what it’s for, right?
Obviously, you can’t change import settings during runtime (it’s IMPORT settings, just a way how to translate a png or other format to a texture. Once it’s done it’s done).
You don’t have to enable read/write, you can just blit / render your texture to a renderTexture (and then if you really want, you can read pixels from that). But for painting, i’d suggest entirely gpu-based solution, by sequentially rendering into a renderTexture and not clearing it.

Anyway here’s my texture painting tool, as is

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TexturePaint : System.IDisposable
{
    private Texture2D m_targetTexture;
    private RenderTexture m_rt;
    private bool m_painting;

    public Material brush;

    public event System.Action<Texture> onDisplayTextureChanged;

    public bool painting
    {
        get { return m_painting; }
        set
        {
            if (m_painting == value) return;
            m_painting = value;
            if (m_painting)
                OnBeginPainting();
            else
                OnEndPainting();
        }
    }

    private void OnBeginPainting()
    {
        if (!m_rt)
            m_rt = new RenderTexture(2,2,24, RenderTextureFormat.ARGBFloat);
        m_rt.width = m_targetTexture.width;
        m_rt.height = m_targetTexture.height;
        m_rt.Create();
        
        Graphics.Blit(m_targetTexture, m_rt);
        if (onDisplayTextureChanged != null)
            onDisplayTextureChanged(m_rt);
    }

    private void OnEndPainting()
    {
        var tempLdr = RenderTexture.GetTemporary(m_rt.width, m_rt.height, 0, RenderTextureFormat.ARGB32);
        try
        {
            Graphics.Blit(m_rt, tempLdr);
            RenderTexture.active = tempLdr;
            m_targetTexture.ReadPixels(new Rect(0, 0, tempLdr.width, tempLdr.height), 0, 0, false);
            m_targetTexture.Apply(true);
            RenderTexture.active = null;
            m_rt.Release();
        }
        finally
        {
            RenderTexture.ReleaseTemporary(tempLdr);
            if (onDisplayTextureChanged != null)
                onDisplayTextureChanged(m_targetTexture);
        }
    }

    public void Dispose()
    {
        painting = false;
    }

    ~TexturePaint()
    {
        Dispose();
    }

    public Texture2D targetTexture
    {
        get { return m_targetTexture;  }
        set
        {
            if (m_targetTexture == value)
                return;
            m_targetTexture = value;
        }
    }

    public void Paint(Vector2 uv, float radius, float amount)
    {
        if (!painting) throw new System.InvalidOperationException();
        Paint(uv, radius, amount, brush, m_rt);
    }

    public static void Paint(Vector2 uv, float radius, float amount, Material brush, RenderTexture target )
    {
        var origColor = brush.color;
        try
        {
            var color = origColor;
            color.a *= amount;
            brush.color = color;

            Vector2 radius2 = new Vector2(radius, radius);

            Graphics.SetRenderTarget(target);
            GL.LoadOrtho();
            brush.SetPass(0);
            GL.Begin(GL.TRIANGLE_STRIP);
            GL.TexCoord2(1, 0);
            GL.Vertex(new Vector2(uv.x + radius2.x, uv.y - radius2.y));
            GL.TexCoord2(0, 0);
            GL.Vertex(new Vector2(uv.x - radius2.x, uv.y - radius2.y));
            GL.TexCoord2(1, 1);
            GL.Vertex(new Vector2(uv.x + radius2.x, uv.y + radius2.y));
            GL.TexCoord2(0, 1);
            GL.Vertex(new Vector2(uv.x - radius2.x, uv.y + radius2.y));
            GL.End();
        }

        finally
        {
            brush.color = origColor;
        }
    }


}