2023-11-10 14:37:56 +08:00
|
|
|
|
#if MA_VRCSDK3_AVATARS
|
|
|
|
|
|
|
|
|
|
using System;
|
2023-09-14 20:24:20 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Collections.Immutable;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using UnityEditor;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.Experimental.Rendering;
|
|
|
|
|
using VRC.SDK3.Avatars.ScriptableObjects;
|
2023-09-20 17:49:20 +08:00
|
|
|
|
using Object = UnityEngine.Object;
|
2023-09-14 20:24:20 +08:00
|
|
|
|
|
|
|
|
|
namespace nadena.dev.modular_avatar.core.editor
|
|
|
|
|
{
|
|
|
|
|
internal class FixupExpressionsMenuPass
|
|
|
|
|
{
|
2023-09-20 17:49:20 +08:00
|
|
|
|
private const string DEFAULT_EXP_MENU_GUID = "024fb8ef5b3988c46b446863c92f4522";
|
|
|
|
|
private const string DEFAULT_EXP_PARAM_GUID = "03a6d797deb62f0429471c4e17ea99a7";
|
2023-09-30 23:09:43 +08:00
|
|
|
|
|
2023-09-14 20:24:20 +08:00
|
|
|
|
internal static void FixupExpressionsMenu(BuildContext context)
|
|
|
|
|
{
|
2024-10-28 01:06:35 +08:00
|
|
|
|
if (!context.AvatarDescriptor) return;
|
|
|
|
|
|
2023-09-20 17:49:20 +08:00
|
|
|
|
context.AvatarDescriptor.customExpressions = true;
|
2023-09-30 23:09:43 +08:00
|
|
|
|
|
2023-09-14 20:24:20 +08:00
|
|
|
|
var expressionsMenu = context.AvatarDescriptor.expressionsMenu;
|
2023-09-20 17:49:20 +08:00
|
|
|
|
if (expressionsMenu == null)
|
|
|
|
|
{
|
|
|
|
|
var defaultExpMenu = AssetDatabase.LoadAssetAtPath<VRCExpressionsMenu>(
|
|
|
|
|
AssetDatabase.GUIDToAssetPath(DEFAULT_EXP_MENU_GUID)
|
|
|
|
|
);
|
2023-09-30 23:09:43 +08:00
|
|
|
|
|
2023-09-20 17:49:20 +08:00
|
|
|
|
expressionsMenu = Object.Instantiate(defaultExpMenu);
|
|
|
|
|
context.AvatarDescriptor.expressionsMenu = expressionsMenu;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (context.AvatarDescriptor.expressionParameters == null)
|
|
|
|
|
{
|
|
|
|
|
var defaultExpParam = AssetDatabase.LoadAssetAtPath<VRCExpressionParameters>(
|
|
|
|
|
AssetDatabase.GUIDToAssetPath(DEFAULT_EXP_PARAM_GUID)
|
|
|
|
|
);
|
2023-09-30 23:09:43 +08:00
|
|
|
|
|
2023-09-20 17:49:20 +08:00
|
|
|
|
context.AvatarDescriptor.expressionParameters = Object.Instantiate(defaultExpParam);
|
|
|
|
|
}
|
2023-09-30 23:09:43 +08:00
|
|
|
|
|
2023-09-20 17:49:20 +08:00
|
|
|
|
var parameters = context.AvatarDescriptor.expressionParameters.parameters
|
2023-09-14 20:24:20 +08:00
|
|
|
|
?? new VRCExpressionParameters.Parameter[0];
|
2023-09-30 23:09:43 +08:00
|
|
|
|
var parameterNames = parameters.Select(p => p.name).ToImmutableHashSet();
|
2023-09-14 20:24:20 +08:00
|
|
|
|
|
2023-09-30 23:09:43 +08:00
|
|
|
|
if (!context.PluginBuildContext.IsTemporaryAsset(expressionsMenu))
|
2023-09-14 20:24:20 +08:00
|
|
|
|
{
|
|
|
|
|
expressionsMenu = context.CloneMenu(expressionsMenu);
|
|
|
|
|
context.AvatarDescriptor.expressionsMenu = expressionsMenu;
|
|
|
|
|
}
|
2023-09-30 23:09:43 +08:00
|
|
|
|
|
2023-09-14 20:24:20 +08:00
|
|
|
|
// Walk menu recursively
|
|
|
|
|
var visitedMenus = new HashSet<VRCExpressionsMenu>();
|
|
|
|
|
var iconMapping = new Dictionary<Texture2D, Texture2D>();
|
|
|
|
|
|
|
|
|
|
VisitMenu(expressionsMenu);
|
|
|
|
|
|
|
|
|
|
void VisitMenu(VRCExpressionsMenu menu)
|
|
|
|
|
{
|
|
|
|
|
if (!visitedMenus.Add(menu)) return;
|
|
|
|
|
|
|
|
|
|
foreach (var control in menu.controls)
|
|
|
|
|
{
|
|
|
|
|
if (control.parameter != null &&
|
|
|
|
|
!string.IsNullOrEmpty(control.parameter.name) &&
|
|
|
|
|
!parameterNames.Contains(control.parameter.name))
|
|
|
|
|
{
|
|
|
|
|
control.parameter.name = "";
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-30 23:09:43 +08:00
|
|
|
|
foreach (var subParam in control.subParameters ??
|
|
|
|
|
Array.Empty<VRCExpressionsMenu.Control.Parameter>())
|
2023-09-14 20:24:20 +08:00
|
|
|
|
{
|
|
|
|
|
if (subParam != null &&
|
|
|
|
|
!string.IsNullOrEmpty(subParam.name) &&
|
|
|
|
|
!parameterNames.Contains(subParam.name))
|
|
|
|
|
{
|
|
|
|
|
subParam.name = "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (control.icon != null)
|
|
|
|
|
{
|
|
|
|
|
if (!iconMapping.TryGetValue(control.icon, out var newIcon))
|
|
|
|
|
{
|
|
|
|
|
iconMapping[control.icon] = newIcon = MaybeScaleIcon(context, control.icon);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
control.icon = newIcon;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (control.labels != null)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < control.labels.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
var label = control.labels[i];
|
2023-09-30 23:09:43 +08:00
|
|
|
|
|
2023-09-14 20:24:20 +08:00
|
|
|
|
if (label.icon != null)
|
|
|
|
|
{
|
|
|
|
|
if (!iconMapping.TryGetValue(label.icon, out var newIcon))
|
|
|
|
|
{
|
|
|
|
|
iconMapping[label.icon] = newIcon = MaybeScaleIcon(context, label.icon);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
label.icon = newIcon;
|
|
|
|
|
control.labels[i] = label;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-30 23:09:43 +08:00
|
|
|
|
#if UNITY_ANDROID
|
2023-09-14 20:24:20 +08:00
|
|
|
|
private const TextureFormat TargetFormat = TextureFormat.ASTC_4x4;
|
2023-09-30 23:09:43 +08:00
|
|
|
|
#else
|
2023-09-14 20:24:20 +08:00
|
|
|
|
private const TextureFormat TargetFormat = TextureFormat.DXT5;
|
2023-09-30 23:09:43 +08:00
|
|
|
|
#endif
|
|
|
|
|
|
2023-09-14 20:24:20 +08:00
|
|
|
|
private static Texture2D MaybeScaleIcon(BuildContext context, Texture2D original)
|
|
|
|
|
{
|
|
|
|
|
if (original.width <= 256 && original.height <= 256 && IsCompressedFormat(original.format))
|
|
|
|
|
{
|
|
|
|
|
return original;
|
|
|
|
|
}
|
2023-09-30 23:09:43 +08:00
|
|
|
|
|
2023-09-14 20:24:20 +08:00
|
|
|
|
var newRatio = Math.Min(256f / original.width, 256f / original.height);
|
|
|
|
|
var newWidth = Math.Min(256, Mathf.RoundToInt(original.width * newRatio));
|
|
|
|
|
var newHeight = Math.Min(256, Mathf.RoundToInt(original.height * newRatio));
|
|
|
|
|
|
|
|
|
|
var newTex = new Texture2D(newWidth, newHeight, TextureFormat.RGBA32, true);
|
|
|
|
|
context.SaveAsset(newTex);
|
2023-09-30 23:09:43 +08:00
|
|
|
|
|
2023-09-14 20:24:20 +08:00
|
|
|
|
var tmpRenderTex = RenderTexture.GetTemporary(newWidth, newHeight, 0, RenderTextureFormat.ARGB32);
|
|
|
|
|
var originalActiveRenderTex = RenderTexture.active;
|
2023-09-30 23:09:43 +08:00
|
|
|
|
|
2023-09-14 20:24:20 +08:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Graphics.Blit(original, tmpRenderTex);
|
|
|
|
|
RenderTexture.active = tmpRenderTex;
|
|
|
|
|
newTex.ReadPixels(new Rect(0, 0, newWidth, newHeight), 0, 0);
|
|
|
|
|
newTex.Apply();
|
|
|
|
|
EditorUtility.CompressTexture(newTex, TargetFormat, TextureCompressionQuality.Normal);
|
|
|
|
|
|
|
|
|
|
return newTex;
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
RenderTexture.active = originalActiveRenderTex;
|
|
|
|
|
RenderTexture.ReleaseTemporary(tmpRenderTex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool IsCompressedFormat(TextureFormat format)
|
|
|
|
|
{
|
|
|
|
|
var name = format.ToString();
|
|
|
|
|
return name.StartsWith("DXT") || name.StartsWith("ASTC");
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-10 14:37:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|