Skip to content

Instantly share code, notes, and snippets.

@FleshMobProductions
Last active July 29, 2023 21:19
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FleshMobProductions/83e8346b5e37c1487034926fbeec3039 to your computer and use it in GitHub Desktop.
Save FleshMobProductions/83e8346b5e37c1487034926fbeec3039 to your computer and use it in GitHub Desktop.
Unity Editor script to combine a specular and a gloss map from Mixamo for use in a Standard Shader with Specular Setup
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Linq;
namespace FMPUtils.Editor
{
public class EditorHelpUtilities
{
public static bool DoesAssetAtPathExist(string assetDatabaseFilePath)
{
return string.IsNullOrEmpty(AssetDatabase.AssetPathToGUID(assetDatabaseFilePath)) == false;
}
public static bool DisplayConfirmDialog(string title, string text, string okText = "Proceed", string cancelText = "Cancel")
{
return EditorUtility.DisplayDialog(title, text, okText, cancelText);
}
/// <summary>
/// Returns the path to the folder, not including the trailing "/" of the folder.
/// If the asset is at top level, will return an empty string
/// </summary>
public static string GetAssetFolderPathFromAssetFilePath(string assetDatabaseFilePath)
{
if (string.IsNullOrEmpty(assetDatabaseFilePath))
return string.Empty;
int lastSlashIndex = assetDatabaseFilePath.LastIndexOf('/');
if (lastSlashIndex < 0)
return string.Empty;
return assetDatabaseFilePath.Substring(0, lastSlashIndex);
}
}
}
using System.Text;
using UnityEngine;
using UnityEditor;
namespace FMPUtils.Editor
{
/// <summary>
/// An editor script to combine a specular and a gloss map (from Mixamo) such that the gloss map
/// grey scale value is inserted as alpha channel to the specular map values. This will generate
/// the texture for the Unity 5 Standard Specular setup at least.
/// Information taken from https://forum.unity.com/threads/how-do-i-use-a-gloss-map.297752/
/// </summary>
public class SpecularGlossMapCombinerWindow : EditorWindow
{
private Texture2D specularMap;
private Texture2D glossMap;
private bool useAutoNaming;
private string outputTextureName;
private StringBuilder requirementsMessageSB = new StringBuilder();
private GUIStyle requirementsErrorStyle;
[MenuItem("FMPUtils/Specular-Gloss Map Combiner")]
public static void ShowWindow()
{
EditorWindow.GetWindow<SpecularGlossMapCombinerWindow>(false, "Specular-Gloss Map Combiner", true);
}
private void OnGUI()
{
EditorGUILayout.LabelField("Specular-Gloss Map Combiner", EditorStyles.boldLabel);
EditorGUILayout.Space();
specularMap = (Texture2D)EditorGUILayout.ObjectField("Specular Map:", specularMap, typeof(Texture2D), false);
glossMap = (Texture2D)EditorGUILayout.ObjectField("Gloss Map:", glossMap, typeof(Texture2D), false);
bool useAutoNamingPrev = useAutoNaming;
useAutoNaming = EditorGUILayout.Toggle("Use Auto Naming", useAutoNaming);
if (!useAutoNamingPrev && useAutoNaming)
outputTextureName = specularMap != null ? specularMap.name + "CombinedMap" : "SpecGlossCombinedMap";
using (new EditorGUI.DisabledScope(useAutoNaming))
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Output Texture Name", GUILayout.MaxWidth(150));
outputTextureName = EditorGUILayout.TextField(outputTextureName);
EditorGUILayout.EndHorizontal();
}
requirementsMessageSB.Length = 0;
bool allowCreation = true;
if (specularMap == null || glossMap == null)
{
allowCreation = false;
requirementsMessageSB.AppendLine("Valid specular and gloss map need to be assigned");
}
else
{
if (specularMap.width != glossMap.width || specularMap.height != glossMap.height)
{
allowCreation = false;
requirementsMessageSB.AppendLine("Assigned Specular and Gloss map do not have matching resolutions");
}
}
if (string.IsNullOrWhiteSpace(outputTextureName))
{
allowCreation = false;
requirementsMessageSB.AppendLine("Output Texture Name cannot be empty");
}
EditorGUILayout.Space();
using (new EditorGUI.DisabledScope(!allowCreation))
{
if (GUILayout.Button("Combine Textures and Save"))
{
CombineAndStoreTexture();
}
}
if (!allowCreation)
{
if (requirementsErrorStyle == null)
{
requirementsErrorStyle = new GUIStyle(EditorStyles.wordWrappedLabel);
requirementsErrorStyle.normal.textColor = Color.red;
}
EditorGUILayout.Space();
EditorGUILayout.LabelField(requirementsMessageSB.ToString(), requirementsErrorStyle);
}
}
/// <summary>
/// Assumes that both textures have RGBA color channels in that order. Result texture will be of type png
/// </summary>
private void CombineAndStoreTexture()
{
string specularMapPath = AssetDatabase.GetAssetPath(specularMap);
string folderMapPath = EditorHelpUtilities.GetAssetFolderPathFromAssetFilePath(specularMapPath);
string targetMapPath = $"{folderMapPath}/{outputTextureName}.png";
if (!EditorHelpUtilities.DisplayConfirmDialog("Generate and save combined texture?",
$"Do you want to combine the specular map {specularMap.name} and gloss map {glossMap.name} and store it at the path {targetMapPath}"))
return;
if (EditorHelpUtilities.DoesAssetAtPathExist(targetMapPath))
{
if (!EditorHelpUtilities.DisplayConfirmDialog("Overwrite existing texture?",
$"A texture at {targetMapPath} already exists, do you want to overwrite it?"))
return;
}
// With a simple AssetDatabase.CreateAsset call I get an unreadable texture asset, so I save it as png instead
Texture2D resultTexture = new Texture2D(specularMap.width, specularMap.height, TextureFormat.RGBA32, specularMap.mipmapCount > 1);
bool specularMapReadEnabledOriginal = specularMap.isReadable;
bool glossMapReadEnabledOriginal = glossMap.isReadable;
SetReadWriteEnabledFlag(specularMap, true);
SetReadWriteEnabledFlag(glossMap, true);
AssetDatabase.Refresh();
// Check if isReadable flags have changed and if so, reload the textures:
if (!specularMapReadEnabledOriginal)
specularMap = (Texture2D)AssetDatabase.LoadAssetAtPath(AssetDatabase.GetAssetPath(specularMap), typeof(Texture2D));
if (!glossMapReadEnabledOriginal)
glossMap = (Texture2D)AssetDatabase.LoadAssetAtPath(AssetDatabase.GetAssetPath(glossMap), typeof(Texture2D));
Color[] targetMapColors = specularMap.GetPixels();
Color[] glossMapColors = glossMap.GetPixels();
for (int i = 0; i < targetMapColors.Length; i++)
{
Color specularColor = targetMapColors[i];
float glossGreyscale = glossMapColors[i].r;
specularColor.a = glossGreyscale;
targetMapColors[i] = specularColor;
}
resultTexture.SetPixels(targetMapColors);
byte[] textureData = resultTexture.EncodeToPNG();
System.IO.File.WriteAllBytes(targetMapPath, textureData);
AssetDatabase.SaveAssets();
SetReadWriteEnabledFlag(specularMap, specularMapReadEnabledOriginal);
SetReadWriteEnabledFlag(glossMap, glossMapReadEnabledOriginal);
AssetDatabase.Refresh();
}
private bool SetReadWriteEnabledFlag(Texture targetTexture, bool isReadable)
{
string assetPath = AssetDatabase.GetAssetPath(targetTexture);
if (string.IsNullOrEmpty(assetPath))
return false;
TextureImporter importer = AssetImporter.GetAtPath(assetPath) as TextureImporter;
if (importer == null)
return false;
if (importer.isReadable != isReadable)
{
importer.isReadable = isReadable;
importer.SaveAndReimport();
AssetDatabase.SaveAssets();
}
return true;
}
}
}
@ba9lo
Copy link

ba9lo commented Apr 6, 2021

I get 5 errors all about EditorHelpUtilities.

@FleshMobProductions
Copy link
Author

Thanks for the comment! I noticed that I forgot to add my utility library for this gist - added it now with the subset of methods that are required. I hope it works for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment