fix: fixup expressions menus to avoid validation errors (#434)

This change scales oversized expression menu items, and redacts undefined
parameters to avoid hard-to-diagnose build errors.
This commit is contained in:
bd_ 2023-09-14 21:24:20 +09:00 committed by GitHub
parent 6af61302f0
commit ebda9cf7d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 853 additions and 14 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8caa5ed1b0324604cb456b0d5d7a92fb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -0,0 +1,116 @@
fileFormatVersion: 2
guid: adf82baccd885794cb63be6da2bf289e
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,116 @@
fileFormatVersion: 2
guid: 9bb00a795610e1f4998410320612d6bd
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@
using nadena.dev.modular_avatar.core.editor;
using NUnit.Framework;
using VRC.SDK3.Avatars.Components;
namespace modular_avatar_tests
{
public class ExpressionMenuFixupTests : TestBase
{
[Test]
public void testExpMenuFixup()
{
var avatar = CreatePrefab("ExpressionsMenuFixupTestAvatar.prefab");
AvatarProcessor.ProcessAvatar(avatar);
var descriptor = avatar.GetComponent<VRCAvatarDescriptor>();
var expMenu = descriptor.expressionsMenu;
Assert.AreEqual(3, expMenu.controls.Count);
Assert.AreEqual("testparam", expMenu.controls[0].parameter.name);
Assert.AreEqual(256, expMenu.controls[0].icon.height);
Assert.AreEqual(192, expMenu.controls[0].icon.width);
Assert.AreEqual("", expMenu.controls[1].parameter.name);
Assert.AreEqual(192, expMenu.controls[1].icon.height);
Assert.AreEqual(256, expMenu.controls[1].icon.width);
Assert.AreEqual("testparam", expMenu.controls[2].subParameters[0].name);
Assert.AreEqual("", expMenu.controls[2].subParameters[1].name);
foreach (var label in expMenu.controls[2].labels)
{
Assert.LessOrEqual(label.icon.width, 256);
Assert.LessOrEqual(label.icon.height, 256);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c1bfdcd79a862e40a2c896f18ae0ca0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,322 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &6204877465042736622
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6776946804248067904}
- component: {fileID: 5187824885137632139}
- component: {fileID: 1283586302596586804}
- component: {fileID: 3838643251041431458}
m_Layer: 0
m_Name: ExpressionsMenuFixupTestAvatar
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &6776946804248067904
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6204877465042736622}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 20.384737, y: 1.6344047, z: 3.1785903}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &5187824885137632139
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6204877465042736622}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 542108242, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3}
m_Name:
m_EditorClassIdentifier:
Name:
ViewPosition: {x: 0, y: 1.6, z: 0.2}
Animations: 0
ScaleIPD: 1
lipSync: 0
lipSyncJawBone: {fileID: 0}
lipSyncJawClosed: {x: 0, y: 0, z: 0, w: 1}
lipSyncJawOpen: {x: 0, y: 0, z: 0, w: 1}
VisemeSkinnedMesh: {fileID: 0}
MouthOpenBlendShapeName: Facial_Blends.Jaw_Down
VisemeBlendShapes: []
unityVersion:
portraitCameraPositionOffset: {x: 0, y: 0, z: 0}
portraitCameraRotationOffset: {x: 0, y: 1, z: 0, w: -0.00000004371139}
networkIDs: []
customExpressions: 1
expressionsMenu: {fileID: 11400000, guid: f2f0d7df290a6bc48bcb1efe964c6281, type: 2}
expressionParameters: {fileID: 11400000, guid: 16746f1009cf9ae4fb8391d44dc8257a,
type: 2}
enableEyeLook: 0
customEyeLookSettings:
eyeMovement:
confidence: 0.5
excitement: 0.5
leftEye: {fileID: 0}
rightEye: {fileID: 0}
eyesLookingStraight:
linked: 1
left: {x: 0, y: 0, z: 0, w: 0}
right: {x: 0, y: 0, z: 0, w: 0}
eyesLookingUp:
linked: 1
left: {x: 0, y: 0, z: 0, w: 0}
right: {x: 0, y: 0, z: 0, w: 0}
eyesLookingDown:
linked: 1
left: {x: 0, y: 0, z: 0, w: 0}
right: {x: 0, y: 0, z: 0, w: 0}
eyesLookingLeft:
linked: 1
left: {x: 0, y: 0, z: 0, w: 0}
right: {x: 0, y: 0, z: 0, w: 0}
eyesLookingRight:
linked: 1
left: {x: 0, y: 0, z: 0, w: 0}
right: {x: 0, y: 0, z: 0, w: 0}
eyelidType: 0
upperLeftEyelid: {fileID: 0}
upperRightEyelid: {fileID: 0}
lowerLeftEyelid: {fileID: 0}
lowerRightEyelid: {fileID: 0}
eyelidsDefault:
upper:
linked: 1
left: {x: 0, y: 0, z: 0, w: 0}
right: {x: 0, y: 0, z: 0, w: 0}
lower:
linked: 1
left: {x: 0, y: 0, z: 0, w: 0}
right: {x: 0, y: 0, z: 0, w: 0}
eyelidsClosed:
upper:
linked: 1
left: {x: 0, y: 0, z: 0, w: 0}
right: {x: 0, y: 0, z: 0, w: 0}
lower:
linked: 1
left: {x: 0, y: 0, z: 0, w: 0}
right: {x: 0, y: 0, z: 0, w: 0}
eyelidsLookingUp:
upper:
linked: 1
left: {x: 0, y: 0, z: 0, w: 0}
right: {x: 0, y: 0, z: 0, w: 0}
lower:
linked: 1
left: {x: 0, y: 0, z: 0, w: 0}
right: {x: 0, y: 0, z: 0, w: 0}
eyelidsLookingDown:
upper:
linked: 1
left: {x: 0, y: 0, z: 0, w: 0}
right: {x: 0, y: 0, z: 0, w: 0}
lower:
linked: 1
left: {x: 0, y: 0, z: 0, w: 0}
right: {x: 0, y: 0, z: 0, w: 0}
eyelidsSkinnedMesh: {fileID: 0}
eyelidsBlendshapes:
customizeAnimationLayers: 0
baseAnimationLayers:
- isEnabled: 0
type: 0
animatorController: {fileID: 0}
mask: {fileID: 0}
isDefault: 1
- isEnabled: 0
type: 4
animatorController: {fileID: 0}
mask: {fileID: 0}
isDefault: 1
- isEnabled: 0
type: 5
animatorController: {fileID: 0}
mask: {fileID: 0}
isDefault: 1
specialAnimationLayers:
- isEnabled: 0
type: 6
animatorController: {fileID: 0}
mask: {fileID: 0}
isDefault: 1
- isEnabled: 0
type: 7
animatorController: {fileID: 0}
mask: {fileID: 0}
isDefault: 1
- isEnabled: 0
type: 8
animatorController: {fileID: 0}
mask: {fileID: 0}
isDefault: 1
AnimationPreset: {fileID: 0}
animationHashSet: []
autoFootsteps: 1
autoLocomotion: 1
collider_head:
isMirrored: 1
state: 0
transform: {fileID: 0}
radius: 0
height: 0
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0, w: 1}
collider_torso:
isMirrored: 1
state: 0
transform: {fileID: 0}
radius: 0
height: 0
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0, w: 1}
collider_footR:
isMirrored: 1
state: 0
transform: {fileID: 0}
radius: 0
height: 0
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0, w: 1}
collider_footL:
isMirrored: 1
state: 0
transform: {fileID: 0}
radius: 0
height: 0
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0, w: 1}
collider_handR:
isMirrored: 1
state: 0
transform: {fileID: 0}
radius: 0
height: 0
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0, w: 1}
collider_handL:
isMirrored: 1
state: 0
transform: {fileID: 0}
radius: 0
height: 0
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0, w: 1}
collider_fingerIndexL:
isMirrored: 1
state: 0
transform: {fileID: 0}
radius: 0
height: 0
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0, w: 1}
collider_fingerMiddleL:
isMirrored: 1
state: 0
transform: {fileID: 0}
radius: 0
height: 0
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0, w: 1}
collider_fingerRingL:
isMirrored: 1
state: 0
transform: {fileID: 0}
radius: 0
height: 0
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0, w: 1}
collider_fingerLittleL:
isMirrored: 1
state: 0
transform: {fileID: 0}
radius: 0
height: 0
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0, w: 1}
collider_fingerIndexR:
isMirrored: 1
state: 0
transform: {fileID: 0}
radius: 0
height: 0
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0, w: 1}
collider_fingerMiddleR:
isMirrored: 1
state: 0
transform: {fileID: 0}
radius: 0
height: 0
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0, w: 1}
collider_fingerRingR:
isMirrored: 1
state: 0
transform: {fileID: 0}
radius: 0
height: 0
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0, w: 1}
collider_fingerLittleR:
isMirrored: 1
state: 0
transform: {fileID: 0}
radius: 0
height: 0
position: {x: 0, y: 0, z: 0}
rotation: {x: 0, y: 0, z: 0, w: 1}
--- !u!114 &1283586302596586804
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6204877465042736622}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -1427037861, guid: 4ecd63eff847044b68db9453ce219299, type: 3}
m_Name:
m_EditorClassIdentifier:
launchedFromSDKPipeline: 0
completedSDKPipeline: 0
blueprintId:
contentType: 0
assetBundleUnityVersion:
fallbackStatus: 0
--- !u!95 &3838643251041431458
Animator:
serializedVersion: 3
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6204877465042736622}
m_Enabled: 1
m_Avatar: {fileID: 0}
m_Controller: {fileID: 0}
m_CullingMode: 0
m_UpdateMode: 0
m_ApplyRootMotion: 0
m_LinearVelocityBlending: 0
m_WarningMessage:
m_HasTransformHierarchy: 1
m_AllowConstantClipSamplingOptimization: 1
m_KeepAnimatorControllerStateOnDisable: 0

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 188009d5762353447bb0a11e779c1abb
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,20 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -1506855854, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3}
m_Name: parameters
m_EditorClassIdentifier:
parameters:
- name: testparam
valueType: 0
saved: 1
defaultValue: 0
networkSynced: 1

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 16746f1009cf9ae4fb8391d44dc8257a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,55 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -340790334, guid: 67cc4cb7839cd3741b63733d5adf0442, type: 3}
m_Name: test menu
m_EditorClassIdentifier:
controls:
- name: retain
icon: {fileID: 2800000, guid: adf82baccd885794cb63be6da2bf289e, type: 3}
type: 101
parameter:
name: testparam
value: 1
style: 0
subMenu: {fileID: 0}
subParameters: []
labels: []
- name: redact
icon: {fileID: 2800000, guid: 9bb00a795610e1f4998410320612d6bd, type: 3}
type: 101
parameter:
name: redact
value: 1
style: 0
subMenu: {fileID: 0}
subParameters: []
labels: []
- name: New Control
icon: {fileID: 0}
type: 201
parameter:
name:
value: 1
style: 0
subMenu: {fileID: 0}
subParameters:
- name: testparam
- name: redact
labels:
- name:
icon: {fileID: 2800000, guid: 9bb00a795610e1f4998410320612d6bd, type: 3}
- name:
icon: {fileID: 2800000, guid: adf82baccd885794cb63be6da2bf289e, type: 3}
- name:
icon: {fileID: 2800000, guid: 9bb00a795610e1f4998410320612d6bd, type: 3}
- name:
icon: {fileID: 2800000, guid: adf82baccd885794cb63be6da2bf289e, type: 3}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f2f0d7df290a6bc48bcb1efe964c6281
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -221,6 +221,8 @@ namespace nadena.dev.modular_avatar.core.editor
context.AnimationDatabase.Commit();
new GCGameObjectsPass(context, avatarGameObject).OnPreprocessAvatar();
FixupExpressionsMenuPass.FixupExpressionsMenu(context);
context.CommitReferencedAssets();

View File

@ -102,20 +102,6 @@ namespace nadena.dev.modular_avatar.core.editor
foreach (var control in newMenu.controls)
{
if (Util.ValidateExpressionMenuIcon(control.icon) != Util.ValidateExpressionMenuIconResult.Success)
control.icon = null;
for (int i = 0; i < control.labels.Length; i++)
{
var label = control.labels[i];
var labelResult = Util.ValidateExpressionMenuIcon(label.icon);
if (labelResult != Util.ValidateExpressionMenuIconResult.Success)
{
label.icon = null;
control.labels[i] = label;
}
}
if (control.type == VRCExpressionsMenu.Control.ControlType.SubMenu)
{
control.subMenu = CloneMenu(control.subMenu);

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 597d4035b7eb43e180a013ceac128a84
timeCreated: 1694687629

View File

@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using VRC.SDK3.Avatars.ScriptableObjects;
namespace nadena.dev.modular_avatar.core.editor
{
internal class FixupExpressionsMenuPass
{
internal static void FixupExpressionsMenu(BuildContext context)
{
var expressionsMenu = context.AvatarDescriptor.expressionsMenu;
if (expressionsMenu == null) return;
var parameters = context.AvatarDescriptor.expressionParameters?.parameters
?? new VRCExpressionParameters.Parameter[0];
var parameterNames = parameters.Select(p=> p.name).ToImmutableHashSet();
if (!Util.IsTemporaryAsset(expressionsMenu))
{
expressionsMenu = context.CloneMenu(expressionsMenu);
context.AvatarDescriptor.expressionsMenu = expressionsMenu;
}
// 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 = "";
}
foreach (var subParam in control.subParameters ?? Array.Empty<VRCExpressionsMenu.Control.Parameter>())
{
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];
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;
}
}
}
}
}
}
#if UNITY_ANDROID
private const TextureFormat TargetFormat = TextureFormat.ASTC_4x4;
#else
private const TextureFormat TargetFormat = TextureFormat.DXT5;
#endif
private static Texture2D MaybeScaleIcon(BuildContext context, Texture2D original)
{
if (original.width <= 256 && original.height <= 256 && IsCompressedFormat(original.format))
{
return original;
}
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);
var tmpRenderTex = RenderTexture.GetTemporary(newWidth, newHeight, 0, RenderTextureFormat.ARGB32);
var originalActiveRenderTex = RenderTexture.active;
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");
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1020bef86d91452ba6b138c249d25bb5
timeCreated: 1694688050