예제 #1
0
        /// <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);
        }
예제 #3
0
        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;
                }
            }
        }
예제 #4
0
        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);
    }