internal static void Apply(GameObject avatar) { #if VRC_SDK_VRCSDK2 || VRC_SDK_VRCSDK3 foreach (Component component in AvatarValidation.FindIllegalComponents(avatar)) { Object.DestroyImmediate(component); } #endif }
// Token: 0x0600577D RID: 22397 RVA: 0x001E2CD8 File Offset: 0x001E10D8 private GameObject CreateAvatarClone(string name, AvatarCloneType type) { GameObject gameObject = UnityEngine.Object.Instantiate <GameObject>(this.currentAvatar); AvatarValidation.RemoveIllegalComponents(name, gameObject, true); gameObject.name = name; gameObject.transform.parent = this.currentAvatar.transform.parent; gameObject.transform.position = this.currentAvatar.transform.position; gameObject.transform.rotation = this.currentAvatar.transform.rotation; AvatarClone avatarClone = gameObject.AddComponent <AvatarClone>(); avatarClone.OriginalAvatar = this.currentAvatar; avatarClone.CloneType = type; avatarClone.StripComponents(); Tools.SetLayerRecursively(gameObject, (type != AvatarCloneType.Mirror) ? LayerMask.NameToLayer("PlayerLocal") : LayerMask.NameToLayer("MirrorReflection"), -1); return(gameObject); }
// Token: 0x0600577B RID: 22395 RVA: 0x001E2320 File Offset: 0x001E0720 private void AttachAvatarInternal(UnityEngine.Object Prefab, string Name, float Scale = 1f) { this.WasMeasured = false; if (this == null || base.gameObject == null || base.GetComponent <Transform>() == null) { Debug.LogError("AttachAvatarInternal: VRCAvatarManager was destroyed, could not attach avatar: " + Name); return; } GameObject gameObject = AssetManagement.Instantiate <GameObject>(Prefab); if (gameObject == null || gameObject.GetComponent <Transform>() == null) { Debug.LogError("Could not instantiate avatar " + Name); return; } AvatarValidation.RemoveIllegalComponents(Name, gameObject, true); AvatarValidation.EnforceAudioSourceLimits(gameObject); if (!this.localPlayer) { foreach (Camera camera in gameObject.GetComponentsInChildren <Camera>()) { Debug.LogWarning("Removing camera from " + camera.gameObject.name); UnityEngine.Object.Destroy(camera); } } int num = 2; VRC.Network.AssignNetworkIDsToObject(gameObject, false, VRC.Network.GetOwnerId(base.gameObject).Value, ref num); VRC.Network.IsObjectReady(gameObject); base.transform.localPosition = Vector3.zero; base.transform.localRotation = Quaternion.identity; gameObject.transform.parent = base.transform; gameObject.transform.localPosition = new Vector3(0f, 0f, 0f); gameObject.transform.localRotation = new Quaternion(0f, 0f, 0f, 1f); gameObject.transform.localScale *= Scale; VRC_AvatarDescriptor component = gameObject.GetComponent <VRC_AvatarDescriptor>(); if (component == null) { Debug.LogError("Avatar Missing Descriptor"); Analytics.Send(ApiAnalyticEvent.EventType.error, "Avatar Missing Descriptor: " + Name, null, null); UnityEngine.Object.Destroy(gameObject); return; } if (gameObject.GetComponent <Animator>() == null) { Debug.Log("Avatar Missing Animator"); if (base.transform.parent != null) { Debug.Log("Avatar player object name=" + base.transform.parent.name); } Analytics.Send(ApiAnalyticEvent.EventType.error, "Avatar Missing Animator: " + Name, null, null); } if (this._avatarMirrorClone != null) { this.DestroyAvatarRenderClone(); } if (this.currentAvatar != null) { VRC_EventHandler[] componentsInChildren2 = this.currentAvatar.GetComponentsInChildren <VRC_EventHandler>(); foreach (VRC_EventHandler vrc_EventHandler in componentsInChildren2) { vrc_EventHandler.Unregister(); } if (this.currentAvatar.GetComponent <Transform>() != null) { this.currentAvatar.GetComponent <Transform>().parent = null; } UnityEngine.Object.Destroy(this.currentAvatar); } this.currentAvatar = gameObject; this.currentAvatarDescriptor = component; AudioListener[] componentsInChildren3 = gameObject.GetComponentsInChildren <AudioListener>(); foreach (AudioListener audioListener in componentsInChildren3) { audioListener.enabled = false; } if (!this.localPlayer) { Camera[] componentsInChildren4 = gameObject.GetComponentsInChildren <Camera>(); foreach (Camera camera2 in componentsInChildren4) { camera2.enabled = false; } } AudioListener[] componentsInChildren5 = gameObject.GetComponentsInChildren <AudioListener>(); foreach (AudioListener audioListener2 in componentsInChildren5) { audioListener2.enabled = false; } SkinnedMeshRenderer[] componentsInChildren6 = this.currentAvatar.GetComponentsInChildren <SkinnedMeshRenderer>(); foreach (SkinnedMeshRenderer skinnedMeshRenderer in componentsInChildren6) { skinnedMeshRenderer.updateWhenOffscreen = true; } this.visibleElements = gameObject.GetComponentsInChildren <Renderer>(true); this.visibleElementEnable = new bool[this.visibleElements.Length]; for (int num2 = 0; num2 < this.visibleElements.Length; num2++) { this.visibleElementEnable[num2] = this.visibleElements[num2].enabled; } List <IVRCCullable> list = new List <IVRCCullable>(); foreach (DynamicBone item in gameObject.GetComponentsInChildren <DynamicBone>(true)) { list.Add(item); } this.cullableElements = list.ToArray(); this._laserSelectRegion = null; if (!this.localPlayer && this._vrcPlayer != null) { Transform transform = this._vrcPlayer.transform.Find("SelectRegion"); if (transform != null) { this._laserSelectRegion = transform.gameObject.GetComponent <PlayerSelector>(); } } Animator component2 = this.currentAvatar.GetComponent <Animator>(); if (component2 != null && component2.avatar != null) { if (component2.isHuman) { this._nonHuman = false; this.currentAvatar.GetOrAddComponent <VRIK>(); this.currentAvatar.GetOrAddComponent <FullBodyBipedIK>(); this.currentAvatar.GetOrAddComponent <global::LimbIK>(); component2.runtimeAnimatorController = null; if (component.Animations == VRC_AvatarDescriptor.AnimationSet.Female) { this._useFemaleAnims = true; } else { this._useFemaleAnims = false; } AnimatorOverrideController defaults = this.animatorControllerMale; if (this._useFemaleAnims) { defaults = this.animatorControllerFemale; } AnimatorOverrideController defaults2 = this.animatorControllerMaleSitting; if (this._useFemaleAnims) { defaults2 = this.animatorControllerFemaleSitting; } this.currentStandingAnims = this.MergeAnims("CustomStanding", defaults, component.CustomStandingAnims); this.currentSittingAnims = this.MergeAnims("CustomSitting", defaults2, component.CustomSittingAnims); component2.runtimeAnimatorController = this.currentStandingAnims; component2.updateMode = AnimatorUpdateMode.Normal; if (this.currentStandingAnims == null) { Debug.LogError(((!(this._vrcPlayer != null)) ? base.gameObject.name : this._vrcPlayer.name) + " has human avatar with broken standing animations.", this); this.currentStandingAnims = this.animatorControllerMale; } else if (this.currentSittingAnims == null) { Debug.LogError(((!(this._vrcPlayer != null)) ? base.gameObject.name : this._vrcPlayer.name) + " has human avatar with broken sitting animations.", this); this.currentSittingAnims = this.animatorControllerMaleSitting; } } else { this._nonHuman = true; if (this.localPlayer) { VRCTrackingManager.SetAvatarViewPoint(this.currentAvatarDescriptor.ViewPosition, Vector3.zero); } this.currentAvatarArmLength = VRCTracking.DefaultArmLength; this.currentAvatarEyeHeight = this.currentAvatarDescriptor.ViewPosition.y; if (this.localPlayer) { VRCTrackingManager.AdjustViewPositionToAvatar(); } } if (this.localPlayer) { component2.cullingMode = AnimatorCullingMode.AlwaysAnimate; } else { component2.cullingMode = AnimatorCullingMode.CullUpdateTransforms; } component2.applyRootMotion = false; } else { this._nonHuman = true; if (this.localPlayer) { VRCTrackingManager.SetAvatarViewPoint(this.currentAvatarDescriptor.ViewPosition, Vector3.zero); } this.currentAvatarArmLength = VRCTracking.DefaultArmLength; this.currentAvatarEyeHeight = this.currentAvatarDescriptor.ViewPosition.y; if (this.localPlayer) { VRCTrackingManager.AdjustViewPositionToAvatar(); } } Transform transform2 = base.transform.Find("../CameraMount"); if (transform2 != null && base.transform.parent != null) { transform2.position = base.transform.parent.TransformPoint(new Vector3(this.currentAvatarDescriptor.ViewPosition.x / base.transform.parent.localScale.x, this.currentAvatarDescriptor.ViewPosition.y / base.transform.parent.localScale.y, 0f)); } MeshRenderer[] componentsInChildren8 = this.currentAvatar.GetComponentsInChildren <MeshRenderer>(); foreach (MeshRenderer meshRenderer in componentsInChildren8) { meshRenderer.lightProbeUsage = this.useLightProbes; } SkinnedMeshRenderer[] componentsInChildren9 = this.currentAvatar.GetComponentsInChildren <SkinnedMeshRenderer>(); foreach (SkinnedMeshRenderer skinnedMeshRenderer2 in componentsInChildren9) { skinnedMeshRenderer2.lightProbeUsage = this.useLightProbes; skinnedMeshRenderer2.updateWhenOffscreen = this.localPlayer; } Vector3 localPosition = new Vector3(0f, this.currentAvatarDescriptor.ViewPosition.y + 0.5f, 0f); if (this.profile != null) { this.profile.localPosition = localPosition; } if (this.old_profile != null) { this.old_profile.localPosition = localPosition; } if (this.localPlayer) { Tools.SetLayerRecursively(gameObject, LayerMask.NameToLayer("PlayerLocal"), LayerMask.NameToLayer("UiMenu")); } else { Tools.SetLayerRecursively(gameObject, LayerMask.NameToLayer("Player"), LayerMask.NameToLayer("UiMenu")); } if (this.localPlayer) { this.InitAvatarRenderClone(); } this._isAttached = true; this.localAvatarVisibility = VRCAvatarManager.LocalAvatarVisibility.Visible; this.RefreshLocalAvatarVisibility(); if (this.OnAvatarCreated != null) { this.OnAvatarCreated(this.currentAvatar, this.currentAvatarDescriptor, false); } }
public override void ValidateFeatures(VRC_AvatarDescriptor avatar, Animator anim, AvatarPerformanceStats perfStats) { //Create avatar debug hashset VRCAvatarDescriptor avatarSDK3 = avatar as VRCAvatarDescriptor; if (avatarSDK3 != null) { avatarSDK3.animationHashSet.Clear(); foreach (VRCAvatarDescriptor.CustomAnimLayer animLayer in avatarSDK3.baseAnimationLayers) { AnimatorController controller = animLayer.animatorController as AnimatorController; if (controller != null) { foreach (AnimatorControllerLayer layer in controller.layers) { ProcessStateMachine(layer.stateMachine, ""); void ProcessStateMachine(AnimatorStateMachine stateMachine, string prefix) { //Update prefix prefix = prefix + stateMachine.name + "."; //States foreach (var state in stateMachine.states) { VRCAvatarDescriptor.DebugHash hash = new VRCAvatarDescriptor.DebugHash(); string fullName = prefix + state.state.name; hash.hash = Animator.StringToHash(fullName); hash.name = fullName.Remove(0, layer.stateMachine.name.Length + 1); avatarSDK3.animationHashSet.Add(hash); } //Sub State Machines foreach (var subMachine in stateMachine.stateMachines) { ProcessStateMachine(subMachine.stateMachine, prefix); } } } } } } //Validate Playable Layers if (avatarSDK3 != null && avatarSDK3.customizeAnimationLayers) { VRCAvatarDescriptor.CustomAnimLayer gestureLayer = avatarSDK3.baseAnimationLayers[2]; if (anim != null && anim.isHuman && gestureLayer.animatorController != null && gestureLayer.type == VRCAvatarDescriptor.AnimLayerType.Gesture && !gestureLayer.isDefault) { AnimatorController controller = gestureLayer.animatorController as AnimatorController; if (controller != null && controller.layers[0].avatarMask == null) { _builder.OnGUIError(avatar, "Gesture Layer needs valid mask on first animator layer", delegate { OpenAnimatorControllerWindow(controller); }, null); } } } //Expression menu images if (avatarSDK3 != null) { bool ValidateTexture(Texture2D texture) { string path = AssetDatabase.GetAssetPath(texture); TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter; if (importer == null) { return(true); } TextureImporterPlatformSettings settings = importer.GetDefaultPlatformTextureSettings(); //Max texture size if ((texture.width > MAX_ACTION_TEXTURE_SIZE || texture.height > MAX_ACTION_TEXTURE_SIZE) && settings.maxTextureSize > MAX_ACTION_TEXTURE_SIZE) { return(false); } //Compression if (settings.textureCompression == TextureImporterCompression.Uncompressed) { return(false); } //Success return(true); } void FixTexture(Texture2D texture) { string path = AssetDatabase.GetAssetPath(texture); TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter; if (importer == null) { return; } TextureImporterPlatformSettings settings = importer.GetDefaultPlatformTextureSettings(); //Max texture size if (texture.width > MAX_ACTION_TEXTURE_SIZE || texture.height > MAX_ACTION_TEXTURE_SIZE) { settings.maxTextureSize = Math.Min(settings.maxTextureSize, MAX_ACTION_TEXTURE_SIZE); } //Compression if (settings.textureCompression == TextureImporterCompression.Uncompressed) { settings.textureCompression = TextureImporterCompression.Compressed; } //Set & Reimport importer.SetPlatformTextureSettings(settings); AssetDatabase.ImportAsset(path); } //Find all textures List <Texture2D> textures = new List <Texture2D>(); List <VRCExpressionsMenu> menuStack = new List <VRCExpressionsMenu>(); FindTextures(avatarSDK3.expressionsMenu); void FindTextures(VRCExpressionsMenu menu) { if (menu == null || menuStack.Contains(menu)) //Prevent recursive menu searching { return; } menuStack.Add(menu); //Check controls foreach (VRCExpressionsMenu.Control control in menu.controls) { AddTexture(control.icon); if (control.labels != null) { foreach (VRCExpressionsMenu.Control.Label label in control.labels) { AddTexture(label.icon); } } if (control.subMenu != null) { FindTextures(control.subMenu); } } void AddTexture(Texture2D texture) { if (texture != null) { textures.Add(texture); } } } //Validate bool isValid = true; foreach (Texture2D texture in textures) { if (!ValidateTexture(texture)) { isValid = false; } } if (!isValid) { _builder.OnGUIError(avatar, "Images used for Actions & Moods are too large.", delegate { Selection.activeObject = avatar.gameObject; }, FixTextures); } //Fix void FixTextures() { foreach (Texture2D texture in textures) { FixTexture(texture); } } } //Expression menu parameters if (avatarSDK3 != null) { //Check for expression menu/parameters object if (avatarSDK3.expressionsMenu != null || avatarSDK3.expressionParameters != null) { //Menu if (avatarSDK3.expressionsMenu == null) { _builder.OnGUIError(avatar, "VRCExpressionsMenu object reference is missing.", delegate { Selection.activeObject = avatarSDK3; }, null); } //Parameters if (avatarSDK3.expressionParameters == null) { _builder.OnGUIError(avatar, "VRCExpressionParameters object reference is missing.", delegate { Selection.activeObject = avatarSDK3; }, null); } } //Check if parameters is valid if (avatarSDK3.expressionParameters != null && avatarSDK3.expressionParameters.CalcTotalCost() > VRCExpressionParameters.MAX_PARAMETER_COST) { _builder.OnGUIError(avatar, "VRCExpressionParameters has too many parameters defined.", delegate { Selection.activeObject = avatarSDK3.expressionParameters; }, null); } //Find all existing parameters if (avatarSDK3.expressionsMenu != null && avatarSDK3.expressionParameters != null) { List <VRCExpressionsMenu> menuStack = new List <VRCExpressionsMenu>(); List <string> parameters = new List <string>(); List <VRCExpressionsMenu> selects = new List <VRCExpressionsMenu>(); FindParameters(avatarSDK3.expressionsMenu); void FindParameters(VRCExpressionsMenu menu) { if (menu == null || menuStack.Contains(menu)) //Prevent recursive menu searching { return; } menuStack.Add(menu); //Check controls foreach (VRCExpressionsMenu.Control control in menu.controls) { AddParameter(control.parameter); if (control.subParameters != null) { foreach (VRCExpressionsMenu.Control.Parameter subParameter in control.subParameters) { AddParameter(subParameter); } } if (control.subMenu != null) { FindParameters(control.subMenu); } } void AddParameter(VRCExpressionsMenu.Control.Parameter parameter) { if (parameter != null) { parameters.Add(parameter.name); selects.Add(menu); } } } //Validate parameters for (int i = 0; i < parameters.Count; i++) { string parameter = parameters[i]; VRCExpressionsMenu select = selects[i]; //Find bool exists = string.IsNullOrEmpty(parameter) || avatarSDK3.expressionParameters.FindParameter(parameter) != null; if (!exists) { _builder.OnGUIError(avatar, "VRCExpressionsMenu uses a parameter that is not defined.\nParameter: " + parameter, delegate { Selection.activeObject = select; }, null); } } //Validate param choices foreach (var menu in menuStack) { foreach (var control in menu.controls) { bool isValid = true; if (control.type == VRCExpressionsMenu.Control.ControlType.FourAxisPuppet) { isValid &= ValidateNonBoolParam(control.subParameters[0].name); isValid &= ValidateNonBoolParam(control.subParameters[1].name); isValid &= ValidateNonBoolParam(control.subParameters[2].name); isValid &= ValidateNonBoolParam(control.subParameters[3].name); } else if (control.type == VRCExpressionsMenu.Control.ControlType.RadialPuppet) { isValid &= ValidateNonBoolParam(control.subParameters[0].name); } else if (control.type == VRCExpressionsMenu.Control.ControlType.TwoAxisPuppet) { isValid &= ValidateNonBoolParam(control.subParameters[0].name); isValid &= ValidateNonBoolParam(control.subParameters[1].name); } if (!isValid) { _builder.OnGUIError(avatar, "VRCExpressionsMenu uses an invalid parameter for a control.\nControl: " + control.name, delegate { Selection.activeObject = menu; }, null); } } bool ValidateNonBoolParam(string name) { VRCExpressionParameters.Parameter param = string.IsNullOrEmpty(name) ? null : avatarSDK3.expressionParameters.FindParameter(name); if (param != null && param.valueType == VRCExpressionParameters.ValueType.Bool) { return(false); } return(true); } } } } IEnumerable <Component> componentsToRemove = AvatarValidation.FindIllegalComponents(avatar.gameObject); HashSet <string> componentsToRemoveNames = new HashSet <string>(); IEnumerable <Component> toRemove = componentsToRemove as Component[] ?? componentsToRemove.ToArray(); foreach (Component c in toRemove) { if (componentsToRemoveNames.Contains(c.GetType().Name) == false) { componentsToRemoveNames.Add(c.GetType().Name); } } if (componentsToRemoveNames.Count > 0) { _builder.OnGUIError(avatar, "The following component types are found on the Avatar and will be removed by the client: " + string.Join(", ", componentsToRemoveNames.ToArray()), delegate { ShowRestrictedComponents(toRemove); }, delegate { FixRestrictedComponents(toRemove); }); } List <AudioSource> audioSources = avatar.gameObject.GetComponentsInChildren <AudioSource>(true).ToList(); if (audioSources.Count > 0) { _builder.OnGUIWarning(avatar, "Audio sources found on Avatar, they will be adjusted to safe limits, if necessary.", GetAvatarSubSelectAction(avatar, typeof(AudioSource)), null); } List <VRCStation> stations = avatar.gameObject.GetComponentsInChildren <VRCStation>(true).ToList(); if (stations.Count > 0) { _builder.OnGUIWarning(avatar, "Stations found on Avatar, they will be adjusted to safe limits, if necessary.", GetAvatarSubSelectAction(avatar, typeof(VRCStation)), null); } if (VRCSdkControlPanel.HasSubstances(avatar.gameObject)) { _builder.OnGUIWarning(avatar, "This avatar has one or more Substance materials, which is not supported and may break in-game. Please bake your Substances to regular materials.", () => { Selection.objects = VRCSdkControlPanel.GetSubstanceObjects(avatar.gameObject); }, null); } CheckAvatarMeshesForLegacyBlendShapesSetting(avatar); #if UNITY_ANDROID IEnumerable <Shader> illegalShaders = AvatarValidation.FindIllegalShaders(avatar.gameObject); foreach (Shader s in illegalShaders) { _builder.OnGUIError(avatar, "Avatar uses unsupported shader '" + s.name + "'. You can only use the shaders provided in 'VRChat/Mobile' for Quest avatars.", delegate() { Selection.activeObject = avatar.gameObject; }, null); } #endif foreach (AvatarPerformanceCategory perfCategory in Enum.GetValues(typeof(AvatarPerformanceCategory))) { if (perfCategory == AvatarPerformanceCategory.Overall || perfCategory == AvatarPerformanceCategory.PolyCount || perfCategory == AvatarPerformanceCategory.AABB || perfCategory == AvatarPerformanceCategory.AvatarPerformanceCategoryCount) { continue; } Action show = null; switch (perfCategory) { case AvatarPerformanceCategory.AnimatorCount: show = GetAvatarSubSelectAction(avatar, typeof(Animator)); break; case AvatarPerformanceCategory.AudioSourceCount: show = GetAvatarSubSelectAction(avatar, typeof(AudioSource)); break; case AvatarPerformanceCategory.BoneCount: show = GetAvatarSubSelectAction(avatar, typeof(SkinnedMeshRenderer)); break; case AvatarPerformanceCategory.ClothCount: show = GetAvatarSubSelectAction(avatar, typeof(Cloth)); break; case AvatarPerformanceCategory.ClothMaxVertices: show = GetAvatarSubSelectAction(avatar, typeof(Cloth)); break; case AvatarPerformanceCategory.LightCount: show = GetAvatarSubSelectAction(avatar, typeof(Light)); break; case AvatarPerformanceCategory.LineRendererCount: show = GetAvatarSubSelectAction(avatar, typeof(LineRenderer)); break; case AvatarPerformanceCategory.MaterialCount: show = GetAvatarSubSelectAction(avatar, new[] { typeof(MeshRenderer), typeof(SkinnedMeshRenderer) }); break; case AvatarPerformanceCategory.MeshCount: show = GetAvatarSubSelectAction(avatar, new[] { typeof(MeshRenderer), typeof(SkinnedMeshRenderer) }); break; case AvatarPerformanceCategory.ParticleCollisionEnabled: show = GetAvatarSubSelectAction(avatar, typeof(ParticleSystem)); break; case AvatarPerformanceCategory.ParticleMaxMeshPolyCount: show = GetAvatarSubSelectAction(avatar, typeof(ParticleSystem)); break; case AvatarPerformanceCategory.ParticleSystemCount: show = GetAvatarSubSelectAction(avatar, typeof(ParticleSystem)); break; case AvatarPerformanceCategory.ParticleTotalCount: show = GetAvatarSubSelectAction(avatar, typeof(ParticleSystem)); break; case AvatarPerformanceCategory.ParticleTrailsEnabled: show = GetAvatarSubSelectAction(avatar, typeof(ParticleSystem)); break; case AvatarPerformanceCategory.PhysicsColliderCount: show = GetAvatarSubSelectAction(avatar, typeof(Collider)); break; case AvatarPerformanceCategory.PhysicsRigidbodyCount: show = GetAvatarSubSelectAction(avatar, typeof(Rigidbody)); break; case AvatarPerformanceCategory.PolyCount: show = GetAvatarSubSelectAction(avatar, new[] { typeof(MeshRenderer), typeof(SkinnedMeshRenderer) }); break; case AvatarPerformanceCategory.SkinnedMeshCount: show = GetAvatarSubSelectAction(avatar, typeof(SkinnedMeshRenderer)); break; case AvatarPerformanceCategory.TrailRendererCount: show = GetAvatarSubSelectAction(avatar, typeof(TrailRenderer)); break; } // we can only show these buttons if DynamicBone is installed Type dynamicBoneType = typeof(AvatarValidation).Assembly.GetType("DynamicBone"); Type dynamicBoneColliderType = typeof(AvatarValidation).Assembly.GetType("DynamicBoneCollider"); if ((dynamicBoneType != null) && (dynamicBoneColliderType != null)) { switch (perfCategory) { case AvatarPerformanceCategory.DynamicBoneColliderCount: show = GetAvatarSubSelectAction(avatar, dynamicBoneColliderType); break; case AvatarPerformanceCategory.DynamicBoneCollisionCheckCount: show = GetAvatarSubSelectAction(avatar, dynamicBoneColliderType); break; case AvatarPerformanceCategory.DynamicBoneComponentCount: show = GetAvatarSubSelectAction(avatar, dynamicBoneType); break; case AvatarPerformanceCategory.DynamicBoneSimulatedBoneCount: show = GetAvatarSubSelectAction(avatar, dynamicBoneType); break; } } OnGUIPerformanceInfo(avatar, perfStats, perfCategory, show, null); } _builder.OnGUILink(avatar, "Avatar Optimization Tips", VRCSdkControlPanel.AVATAR_OPTIMIZATION_TIPS_URL); }
public override void ValidateFeatures(VRC.SDKBase.VRC_AvatarDescriptor avatar, Animator anim, AvatarPerformanceStats perfStats) { IEnumerable <Component> componentsToRemove = AvatarValidation.FindIllegalComponents(avatar.gameObject); HashSet <string> componentsToRemoveNames = new HashSet <string>(); IEnumerable <Component> toRemove = componentsToRemove as Component[] ?? componentsToRemove.ToArray(); foreach (Component c in toRemove) { if (componentsToRemoveNames.Contains(c.GetType().Name) == false) { componentsToRemoveNames.Add(c.GetType().Name); } } if (componentsToRemoveNames.Count > 0) { _builder.OnGUIError(avatar, "The following component types are found on the Avatar and will be removed by the client: " + string.Join(", ", componentsToRemoveNames.ToArray()), delegate { ShowRestrictedComponents(toRemove); }, delegate { FixRestrictedComponents(toRemove); }); } List <AudioSource> audioSources = avatar.gameObject.GetComponentsInChildren <AudioSource>(true).ToList(); if (audioSources.Count > 0) { _builder.OnGUIWarning(avatar, "Audio sources found on Avatar, they will be adjusted to safe limits, if necessary.", GetAvatarSubSelectAction(avatar, typeof(AudioSource)), null); } List <VRCStation> stations = avatar.gameObject.GetComponentsInChildren <VRCStation>(true).ToList(); if (stations.Count > 0) { _builder.OnGUIWarning(avatar, "Stations found on Avatar, they will be adjusted to safe limits, if necessary.", GetAvatarSubSelectAction(avatar, typeof(VRCStation)), null); } if (VRCSdkControlPanel.HasSubstances(avatar.gameObject)) { _builder.OnGUIWarning(avatar, "This avatar has one or more Substance materials, which is not supported and may break in-game. Please bake your Substances to regular materials.", () => { Selection.objects = VRCSdkControlPanel.GetSubstanceObjects(avatar.gameObject); }, null); } CheckAvatarMeshesForLegacyBlendShapesSetting(avatar); #if UNITY_ANDROID IEnumerable <Shader> illegalShaders = AvatarValidation.FindIllegalShaders(avatar.gameObject); foreach (Shader s in illegalShaders) { _builder.OnGUIError(avatar, "Avatar uses unsupported shader '" + s.name + "'. You can only use the shaders provided in 'VRChat/Mobile' for Quest avatars.", delegate() { Selection.activeObject = avatar.gameObject; }, null); } #endif foreach (AvatarPerformanceCategory perfCategory in Enum.GetValues(typeof(AvatarPerformanceCategory))) { if (perfCategory == AvatarPerformanceCategory.Overall || perfCategory == AvatarPerformanceCategory.PolyCount || perfCategory == AvatarPerformanceCategory.AABB || perfCategory == AvatarPerformanceCategory.AvatarPerformanceCategoryCount) { continue; } Action show = null; switch (perfCategory) { case AvatarPerformanceCategory.AnimatorCount: show = GetAvatarSubSelectAction(avatar, typeof(Animator)); break; case AvatarPerformanceCategory.AudioSourceCount: show = GetAvatarSubSelectAction(avatar, typeof(AudioSource)); break; case AvatarPerformanceCategory.BoneCount: show = GetAvatarSubSelectAction(avatar, typeof(SkinnedMeshRenderer)); break; case AvatarPerformanceCategory.ClothCount: show = GetAvatarSubSelectAction(avatar, typeof(Cloth)); break; case AvatarPerformanceCategory.ClothMaxVertices: show = GetAvatarSubSelectAction(avatar, typeof(Cloth)); break; case AvatarPerformanceCategory.LightCount: show = GetAvatarSubSelectAction(avatar, typeof(Light)); break; case AvatarPerformanceCategory.LineRendererCount: show = GetAvatarSubSelectAction(avatar, typeof(LineRenderer)); break; case AvatarPerformanceCategory.MaterialCount: show = GetAvatarSubSelectAction(avatar, new[] { typeof(MeshRenderer), typeof(SkinnedMeshRenderer) }); break; case AvatarPerformanceCategory.MeshCount: show = GetAvatarSubSelectAction(avatar, new[] { typeof(MeshRenderer), typeof(SkinnedMeshRenderer) }); break; case AvatarPerformanceCategory.ParticleCollisionEnabled: show = GetAvatarSubSelectAction(avatar, typeof(ParticleSystem)); break; case AvatarPerformanceCategory.ParticleMaxMeshPolyCount: show = GetAvatarSubSelectAction(avatar, typeof(ParticleSystem)); break; case AvatarPerformanceCategory.ParticleSystemCount: show = GetAvatarSubSelectAction(avatar, typeof(ParticleSystem)); break; case AvatarPerformanceCategory.ParticleTotalCount: show = GetAvatarSubSelectAction(avatar, typeof(ParticleSystem)); break; case AvatarPerformanceCategory.ParticleTrailsEnabled: show = GetAvatarSubSelectAction(avatar, typeof(ParticleSystem)); break; case AvatarPerformanceCategory.PhysicsColliderCount: show = GetAvatarSubSelectAction(avatar, typeof(Collider)); break; case AvatarPerformanceCategory.PhysicsRigidbodyCount: show = GetAvatarSubSelectAction(avatar, typeof(Rigidbody)); break; case AvatarPerformanceCategory.PolyCount: show = GetAvatarSubSelectAction(avatar, new[] { typeof(MeshRenderer), typeof(SkinnedMeshRenderer) }); break; case AvatarPerformanceCategory.SkinnedMeshCount: show = GetAvatarSubSelectAction(avatar, typeof(SkinnedMeshRenderer)); break; case AvatarPerformanceCategory.TrailRendererCount: show = GetAvatarSubSelectAction(avatar, typeof(TrailRenderer)); break; } // we can only show these buttons if DynamicBone is installed Type dynamicBoneType = typeof(AvatarValidation).Assembly.GetType("DynamicBone"); Type dynamicBoneColliderType = typeof(AvatarValidation).Assembly.GetType("DynamicBoneCollider"); if ((dynamicBoneType != null) && (dynamicBoneColliderType != null)) { switch (perfCategory) { case AvatarPerformanceCategory.DynamicBoneColliderCount: show = GetAvatarSubSelectAction(avatar, dynamicBoneColliderType); break; case AvatarPerformanceCategory.DynamicBoneCollisionCheckCount: show = GetAvatarSubSelectAction(avatar, dynamicBoneColliderType); break; case AvatarPerformanceCategory.DynamicBoneComponentCount: show = GetAvatarSubSelectAction(avatar, dynamicBoneType); break; case AvatarPerformanceCategory.DynamicBoneSimulatedBoneCount: show = GetAvatarSubSelectAction(avatar, dynamicBoneType); break; } } OnGUIPerformanceInfo(avatar, perfStats, perfCategory, show, null); } _builder.OnGUILink(avatar, "Avatar Optimization Tips", VRCSdkControlPanel.AVATAR_OPTIMIZATION_TIPS_URL); }