Access a material's saved properties via script?

Okay so here’s my problem:

  1. I have a shader with a _MainTex2 texture.
  2. I want to rename _MainTex2 to _MainTex, in the shader.
  3. As soon as my customers install this update, all of their saved asset files will lose their references to _MainTex2, resulting in pink shaders.

Here’s the debug infomation saved on the material (selecting a copy of the material that’s saved on the hard drive as an asset):

As you can see, the material has saved the reference to _MainTex2. I would like to write a script that would read this “saved properties”, and get that reference, so I can set it to _MainTex.

Anyone know how to access a materials “saved properties”?

Can’t you override the whole asset?

Yes, but I need a way to access the saved properties to get the reference saved in _MainTex2.

Wow, literally nobody has ever tried to do this before. I’m the first person. Any google search for this topic now takes you straight to this thread lol.

have a look at Unity - Scripting API: Material.CopyPropertiesFromMaterial
maybe this could work?

I’m stuck on same problem, have you found a solution?

I want to create a shader updater that will handle renaming to keep the values instead of losing them…

For now, the only thing that I’m thinking about is creating a small program that will lookup the info in file directly, and do the appropriate copy/paste… but that’s a real hassle imo, would be nice if there is a simpler way to do this

what I found : You can create an editor script that will fetch all your assets with given name (we use naming convention)

string[ ] allMats = AssetDatabase.FindAssets(“MAT_”);

and work with these to get the materials, and ultimately, you set the new property with the value of the old property, given that it’s still in its interface (i.e. in the properties section of the shader).

I ran into the exact same problem today. I had to change some material property names (Custom LWRP Graph) in a shader and broke pretty much every material in our project by doing so.

For anyone running into the same problem, here is the script I wrote to fix all currently selected materials again in my project:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
public class FixMaterials
{
    [MenuItem("MSQ/FIX_MATERIALS")]
    public static void UpdateOldProperties()
    {
        foreach (var mat in Selection.GetFiltered<Material>(SelectionMode.Assets))
        {
            Debug.Log(mat.name);
            FixColor(mat, "_tint", "_Color");
            FixFloat(mat, "_metallicMultiplier", "_Metallic");
            FixFloat(mat, "_smoothnessMultiplier", "_Glossiness");
        }
    }

    private static void FixFloat(Material mat, string oldName, string newName)
    {
        if (mat.HasProperty(newName))
        {
            var so = new SerializedObject(mat);
            var itr = so.GetIterator();
            while (itr.Next(true))
            {
                if (itr.displayName == oldName)
                {
                    if(itr.hasChildren)
                    {
                        var itrC = itr.Copy();
                        itrC.Next(true); //Walk into child ("First")
                        itrC.Next(false); //Walk into sibling ("Second")
                        mat.SetFloat(newName, itrC.floatValue);
                    }
                }
            }
        }
    }

    private static void FixColor(Material mat, string oldName, string newName)
    {
        if (mat.HasProperty(newName))
        {
            var so = new SerializedObject(mat);
            var itr = so.GetIterator();
            while (itr.Next(true))
            {
                if (itr.displayName == oldName)
                {
                    if (itr.hasChildren)
                    {
                        var itrC = itr.Copy();
                        itrC.Next(true); //Walk into child ("First")
                        itrC.Next(false); //Walk into sibling ("Second")
                        mat.SetColor(newName, itrC.colorValue);
                    }
                }
            }
        }
    }
}
#endif

The script simply copy/pastes the values of the old properties to the new ones.
It should be pretty easy to modify and extend to you own needs :wink:
To run it, simply select all material you wish to update and select MSQ->FIX_MATERIALS form the top navigation bar.

Greetings
MSQ Tobi

3 Likes

Unity provides its own solution to this problem. Here is how I managed to convert 3d max auto-generated materials into urp materials.

using UnityEditor.Rendering;

// ....

private static void Convert3dMaxMaterials(List<Material> materials)
{
    var upgrader = Create3dMaxToUrpUpgrader();
    foreach (var material in materials)
    {
        MaterialUpgrader.Upgrade(material, upgrader,
            MaterialUpgrader.UpgradeFlags.CleanupNonUpgradedProperties | MaterialUpgrader.UpgradeFlags.LogErrorOnNonExistingProperty);
    }
}
private static MaterialUpgrader Create3dMaxToUrpUpgrader()
{
    var upgrader = new MaterialUpgrader();
    upgrader.RenameShader("Shader Graphs/PhysicalMaterial3DsMax", "Universal Render Pipeline/Simple Lit");
   
    upgrader.RenameTexture("_BASE_COLOR_MAP", "_BaseMap");
    upgrader.RenameTexture("_BUMP_MAP", "_BumpMap");
    upgrader.RenameTexture("_EMISSION_COLOR_MAP", "_EmissionMap");
   
    upgrader.RenameColor("_BASE_COLOR", "_BaseColor");
    upgrader.RenameColor("_EMISSION_COLOR", "_EmissionColor");
    return upgrader;
}