/// <summary> /// Questの制限値に関するエラーメッセージを取得します。 /// </summary> /// <param name="prefab"></param> /// <returns></returns> private static IEnumerable <Converter.Message> GenerateQuestLimitationsErrorMessages(GameObject prefab) { var messages = new List <Converter.Message>(); AvatarPerformanceStats statistics = new AvatarPerformanceStats(); AvatarPerformance.CalculatePerformanceStats("", prefab, statistics); AvatarPerformanceStatsLevel performanceStatLimits = VRChatUtility.AvatarPerformanceStatsLevelSets["Quest"].medium; foreach (var limitation in new[] { new { current = statistics.skinnedMeshCount, limit = performanceStatLimits.skinnedMeshCount, message = _("The number of Skinned Mesh Renderer components is {0}.") }, new { current = statistics.meshCount, limit = performanceStatLimits.meshCount, message = _("The number of (non-Skinned) Mesh Renderer components is {0}.") }, new { current = statistics.materialCount, limit = performanceStatLimits.materialCount, message = _("The number of material slots (sub-meshes) is {0}.") }, new { current = statistics.boneCount, limit = performanceStatLimits.boneCount, message = _("The number of Bones is {0}.") }, }) { if (limitation.current > limitation.limit) { messages.Add(new Converter.Message { message = string.Format(limitation.message, limitation.current) + string.Format( _("If this value exceeds {0}, the avatar will not shown under the default user setting."), limitation.limit ), type = MessageType.Error, }); } } return(messages); }
/// <summary> /// DynamicBoneの制限の既定値を超えていた場合、警告メッセージを返します。 /// </summary> /// <seealso cref="AvatarPerformance.AnalyzeDynamicBone"/> /// <param name="prefabInstance"></param> /// <returns></returns> private static IEnumerable <Converter.Message> GetMessagesAboutDynamicBoneLimits(GameObject avatar) { var messages = new List <Converter.Message>(); #if VRC_SDK_VRCSDK2 || VRC_SDK_VRCSDK3 AvatarPerformanceStats statistics = new AvatarPerformanceStats(); AvatarPerformance.CalculatePerformanceStats(avatar.GetComponent <VRMMeta>().Meta.Title, avatar, statistics); AvatarPerformanceStatsLevel mediumPerformanceStatLimits = VRChatUtility.AvatarPerformanceStatsLevelSets["PC"].medium; if (statistics.dynamicBoneSimulatedBoneCount > mediumPerformanceStatLimits.dynamicBoneSimulatedBoneCount) { messages.Add(new Converter.Message { message = string.Format( _("The “Dynamic Bone Simulated Bone Count” is {0}."), statistics.dynamicBoneSimulatedBoneCount ) + string.Format( _("If this value exceeds {0}, the default user setting disable all Dynamic Bones."), mediumPerformanceStatLimits.dynamicBoneSimulatedBoneCount ), type = MessageType.Warning, }); } if (statistics.dynamicBoneCollisionCheckCount > mediumPerformanceStatLimits.dynamicBoneCollisionCheckCount) { messages.Add(new Converter.Message { message = string.Format( _("The “Dynamic Bone Collision Check Count” is {0}."), statistics.dynamicBoneCollisionCheckCount ) + string.Format( _("If this value exceeds {0}, the default user setting disable all Dynamic Bones."), mediumPerformanceStatLimits.dynamicBoneCollisionCheckCount ), type = MessageType.Warning, }); } #endif return(messages); }
public PumkinsAvatarInfo(GameObject o) : base() { if (o == null) { return; } try { AvatarPerformance.CalculatePerformanceStats(o.name, o, perfStats); } catch { } Name = o.name; var shaderHash = new HashSet <Shader>(); var matList = new List <Material>(); var matList_total = new List <Material>(); var ts = o.GetComponentsInChildren <Transform>(true); foreach (var t in ts) { GameObjects_Total += 1; if (t.gameObject.activeInHierarchy) { GameObjects += 1; } } var sRenders = o.GetComponentsInChildren <SkinnedMeshRenderer>(true); var bonesList = new List <string>(); foreach (var r in sRenders) { SkinnedMeshRenderers_Total += 1; if (r.sharedMesh) { Polygons_Total += r.sharedMesh.triangles.Length / 3; } if (r.bones != null) { var bones = r.bones.Where(b => b != null).Select(b => b.gameObject.name); bonesList.AddRange(bones); } if (r.gameObject.activeInHierarchy && r.enabled) { SkinnedMeshRenderers += 1; if (r.sharedMesh) { Polygons += r.sharedMesh.triangles.Length / 3; } } foreach (var mat in r.sharedMaterials) { if (mat != null) { shaderHash.Add(mat.shader); matList_total.Add(mat); if (r.gameObject.activeInHierarchy && r.enabled) { matList.Add(mat); } } } } Bones = new HashSet <string>(bonesList).Count; var renders = o.GetComponentsInChildren <MeshRenderer>(true); foreach (var r in renders) { var filter = r.GetComponent <MeshFilter>(); if (filter != null && filter.sharedMesh != null) { MeshRenderers_Total += 1; if (filter.sharedMesh != null) { Polygons_Total += filter.sharedMesh.triangles.Length / 3; } if (r.gameObject.activeInHierarchy && r.enabled) { MeshRenderers += 1; if (filter.sharedMesh != null) { Polygons += filter.sharedMesh.triangles.Length / 3; } } } foreach (var mat in r.sharedMaterials) { if (mat != null) { shaderHash.Add(mat.shader); matList_total.Add(mat); if (r.gameObject.activeInHierarchy && r.enabled) { matList.Add(mat); } } } } MaterialSlots = matList.Count; MaterialSlots_Total = matList_total.Count; UniqueMaterials = new HashSet <Material>(matList).Count; UniqueMaterials_Total = new HashSet <Material>(matList_total).Count; #if PUMKIN_DBONES || PUMKIN_OLD_DBONES var dbColliders = o.GetComponentsInChildren <DynamicBoneCollider>(true); foreach (var c in dbColliders) { DynamicBoneColliders_Total += 1; if (c.gameObject.activeInHierarchy) { DynamicBoneColliders += 1; } } var dbones = o.GetComponentsInChildren <DynamicBone>(true); foreach (var d in dbones) { if (d.m_Root != null) { var exclusions = d.m_Exclusions; var rootChildren = d.m_Root.GetComponentsInChildren <Transform>(true); int affected = 0; int affected_total = 0; foreach (var t in rootChildren) { if (exclusions.IndexOf(t) == -1) { affected_total += 1; if (t.gameObject.activeInHierarchy && d.enabled) { affected += 1; } } else { var childChildren = t.GetComponentsInChildren <Transform>(true); for (int z = 1; z < childChildren.Length; z++) { affected_total -= 1; if (childChildren[z].gameObject.activeInHierarchy && d.enabled) { affected -= 1; } } } } foreach (var c in d.m_Colliders) { if (c != null) { DynamicBoneColliderTransforms += affected; DynamicBoneColliderTransforms_Total += affected_total; break; } } DynamicBoneTransforms += affected; DynamicBoneTransforms_Total += affected_total; } } #endif var ptc = o.GetComponentsInChildren <ParticleSystem>(true); foreach (var p in ptc) { ParticleSystems_Total += 1; MaxParticles_Total += p.main.maxParticles; if (p.gameObject.activeInHierarchy && p.emission.enabled) { ParticleSystems += 1; MaxParticles += p.main.maxParticles; } } ShaderCount = shaderHash.Count; var ikf = o.GetComponentsInChildren <VRC_IKFollower>(true); foreach (var ik in ikf) { IKFollowers_Total += 1; if (ik.gameObject.activeInHierarchy) { IKFollowers += 1; } } }
protected override bool DrawWizardGUI() { base.DrawWizardGUI(); isValid = true; if (this.callbackFunctions) { Type callBackFunctions = this.callbackFunctions.GetClass(); this.swayingParametersConverter = Delegate.CreateDelegate( type: typeof(Converter.SwayingParametersConverter), target: callBackFunctions, method: "SwayingParametersConverter", ignoreCase: false, throwOnBindFailure: false ) as Converter.SwayingParametersConverter; this.postConverting = Delegate.CreateDelegate( type: typeof(Wizard.PostConverting), target: callBackFunctions, method: "PostConverting", ignoreCase: false, throwOnBindFailure: false ) as Wizard.PostConverting; } var indentStyle = new GUIStyle() { padding = new RectOffset() { left = Wizard.Indent } }; EditorGUILayout.LabelField( (this.swayingParametersConverter != null ? "☑" : "☐") + " public static DynamicBoneParameters SwayingParametersConverter(SpringBoneParameters, BoneInfo)", indentStyle ); EditorGUILayout.LabelField( (this.postConverting != null ? "☑" : "☐") + " public static void PostConverting(GameObject, VRMMeta)", indentStyle ); #if VRC_SDK_VRCSDK2 || VRC_SDK_VRCSDK3 foreach (var type in Converter.RequiredComponents) { if (!this.avatar.GetComponent(type)) { EditorGUILayout.HelpBox(string.Format(_("Not set “{0}” component."), type), MessageType.Error); isValid = false; } } IEnumerable <string> excludedSpringBoneComments = this.excludedSpringBoneComments.Except(new[] { "" }); if (excludedSpringBoneComments.Count() > 0) { IEnumerable <string> comments = excludedSpringBoneComments.Except( this.GetSpringBonesWithComments(prefab: this.avatar.gameObject, comments: excludedSpringBoneComments) .Select(commentAndSpringBones => commentAndSpringBones.Key) ); if (comments.Count() > 0) { EditorGUILayout.HelpBox(string.Join(separator: "\n• ", value: new[] { _("VRMSpringBones with the below Comments do not exist.") } .Concat(comments).ToArray()), MessageType.Warning); } } if (this.combineMeshes) { IEnumerable <string> notCombineRendererObjectNames = this.notCombineRendererObjectNames.Except(new[] { "" }); if (notCombineRendererObjectNames.Count() > 0) { IEnumerable <string> names = notCombineRendererObjectNames.Except( this.avatar.GetComponentsInChildren <SkinnedMeshRenderer>() .Concat <Component>(this.avatar.GetComponentsInChildren <MeshRenderer>()) .Select(renderer => renderer.name) ); if (names.Count() > 0) { EditorGUILayout.HelpBox(string.Join(separator: "\n• ", value: new[] { _("Renderers on the below name GameObject do not exist.") } .Concat(names).ToArray()), MessageType.Warning); } } } else { EditorGUILayout.HelpBox(_("If you do not “Combine Meshes”," + " and any of VRMBlendShapes references meshes other than the mesh having most shape keys" + " or the mesh is not direct child of the avatar root," + " the avatar will not be converted correctly."), MessageType.Warning); } if (!string.IsNullOrEmpty(this.blendShapeForFingerpoint) && !VRMUtility.GetUserDefinedBlendShapeClip(this.avatar, this.blendShapeForFingerpoint)) { EditorGUILayout.HelpBox(string.Format( _("There is no user-defined VRMBlensShape with the name “{0}”."), this.blendShapeForFingerpoint ), MessageType.Warning); } string version = VRChatUtility.GetSupportedUnityVersion(); if (version != "" && Application.unityVersion != version) { EditorGUILayout.HelpBox(string.Format( _("Unity {0} is running. If you are using a different version than {1}, VRChat SDK might not work correctly. Recommended using Unity downloaded from {2} ."), Application.unityVersion, version, VRChatUtility.DownloadURL ), MessageType.Warning); } if (!isValid || !this.forQuest) { return(true); } AvatarPerformanceStats statistics = new AvatarPerformanceStats(); AvatarPerformance.CalculatePerformanceStats( avatarName: avatar.GetComponent <VRMMeta>().Meta.Title, avatarObject: this.avatar.gameObject, perfStats: statistics ); int currentPolycount = (int)statistics.polyCount; int maxPolycount = VRChatUtility.AvatarPerformanceStatsLevelSets["Quest"].medium.polyCount; if (currentPolycount > maxPolycount) { EditorGUILayout.HelpBox(string.Format( _("The number of polygons is {0}."), currentPolycount ) + string.Format( _("If this value exceeds {0}, the avatar will not shown under the default user setting."), maxPolycount ), MessageType.Error); } #else EditorGUILayout.HelpBox(_("VRChat SDK2 or SDK3 has not been imported."), MessageType.Error); isValid = false; #endif return(true); }
void OnGUIAvatarCheck(VRCSDK2.VRC_AvatarDescriptor avatar) { AvatarPerformanceStats perfStats = AvatarPerformance.CalculatePerformanceStats(avatar.Name, avatar.gameObject); OnGUIPerformanceInfo(avatar, perfStats, AvatarPerformanceCategory.Overall); OnGUIPerformanceInfo(avatar, perfStats, AvatarPerformanceCategory.PolyCount); OnGUIPerformanceInfo(avatar, perfStats, AvatarPerformanceCategory.AABB); var eventHandler = avatar.GetComponentInChildren <VRCSDK2.VRC_EventHandler>(); if (eventHandler != null) { OnGUIError(avatar, "This avatar contains an EventHandler, which is not currently supported in VRChat."); } if (avatar.lipSync == VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape && avatar.VisemeSkinnedMesh == null) { OnGUIError(avatar, "This avatar uses Visemes but the Face Mesh is not specified."); } var anim = avatar.GetComponent <Animator>(); if (anim == null) { OnGUIWarning(avatar, "This avatar does not contain an animator, and will not animate in VRChat."); } else if (anim.isHuman == false) { OnGUIWarning(avatar, "This avatar is not imported as a humanoid rig and will not play VRChat's provided animation set."); } else if (avatar.gameObject.activeInHierarchy == false) { OnGUIError(avatar, "Your avatar is disabled in the scene hierarchy!"); } else { Transform foot = anim.GetBoneTransform(HumanBodyBones.LeftFoot); Transform shoulder = anim.GetBoneTransform(HumanBodyBones.LeftUpperArm); if (foot == null) { OnGUIError(avatar, "Your avatar is humanoid, but it's feet aren't specified!"); } if (shoulder == null) { OnGUIError(avatar, "Your avatar is humanoid, but it's upper arms aren't specified!"); } if (foot != null && shoulder != null) { Vector3 footPos = foot.position - avatar.transform.position; if (footPos.y < 0) { OnGUIWarning(avatar, "Avatar feet are beneath the avatar's origin (the floor). That's probably not what you want."); } Vector3 shoulderPosition = shoulder.position - avatar.transform.position; if (shoulderPosition.y < 0.2f) { OnGUIError(avatar, "This avatar is too short. The minimum is 20cm shoulder height."); } else if (shoulderPosition.y < 1.0f) { OnGUIWarning(avatar, "This avatar is short. This is probably shorter than you want."); } else if (shoulderPosition.y > 5.0f) { OnGUIWarning(avatar, "This avatar is too tall. The maximum is 5m shoulder height."); } else if (shoulderPosition.y > 2.5f) { OnGUIWarning(avatar, "This avatar is tall. This is probably taller than you want."); } } if (AnalyzeIK(avatar, avatar.gameObject, anim) == false) { OnGUILink(avatar, "See Avatar Rig Requirements for more information.", kAvatarRigRequirementsURL); } } IEnumerable <Component> componentsToRemove = VRCSDK2.AvatarValidation.FindIllegalComponents(avatar.gameObject); HashSet <string> componentsToRemoveNames = new HashSet <string>(); foreach (Component c in componentsToRemove) { if (componentsToRemoveNames.Contains(c.GetType().Name) == false) { componentsToRemoveNames.Add(c.GetType().Name); } } if (componentsToRemoveNames.Count > 0) { OnGUIError(avatar, "The following component types are found on the Avatar and will be removed by the client: " + string.Join(", ", componentsToRemoveNames.ToArray())); } if (VRCSDK2.AvatarValidation.EnforceAudioSourceLimits(avatar.gameObject).Count > 0) { OnGUIWarning(avatar, "Audio sources found on Avatar, they will be adjusted to safe limits, if necessary."); } if (VRCSDK2.AvatarValidation.EnforceAvatarStationLimits(avatar.gameObject).Count > 0) { OnGUIWarning(avatar, "Stations found on Avatar, they will be adjusted to safe limits, if necessary."); } if (avatar.gameObject.GetComponentInChildren <Camera>() != null) { OnGUIWarning(avatar, "Cameras are removed from non-local avatars at runtime."); } foreach (AvatarPerformanceCategory perfCategory in System.Enum.GetValues(typeof(AvatarPerformanceCategory))) { if (perfCategory == AvatarPerformanceCategory.Overall || perfCategory == AvatarPerformanceCategory.PolyCount || perfCategory == AvatarPerformanceCategory.AABB || perfCategory == AvatarPerformanceCategory.AvatarPerformanceCategoryCount) { continue; } OnGUIPerformanceInfo(avatar, perfStats, perfCategory); } OnGUILink(avatar, "Avatar Optimization Tips", kAvatarOptimizationTipsURL); }