// https://github.com/vrm-c/UniVRM/issues/474 public static IEnumerable <Validation> Validate(GameObject root) { if (root == null) { yield break; } var hierarchy = root.GetComponentsInChildren <Transform>(true); Dictionary <Transform, List <VRMSpringBone> > rootMap = new Dictionary <Transform, List <VRMSpringBone> >(); foreach (var sb in root.GetComponentsInChildren <VRMSpringBone>()) { for (int i = 0; i < sb.RootBones.Count; ++i) { var springRoot = sb.RootBones[i]; if (springRoot == null) { yield return(Validation.Error($"[VRMSpringBone]{sb.name}.RootBones[{i}] is null")); continue; } if (!hierarchy.Contains(springRoot)) { yield return(Validation.Error($"[VRMSpringBone]{sb.name}.RootBones[{i}] is out of hierarchy")); continue; } if (!springRoot.transform.EnableForExport()) { yield return(Validation.Error($"[VRMSpringBone]{sb.name}.RootBones[{i}] is not active")); continue; } if (!rootMap.TryGetValue(springRoot, out List <VRMSpringBone> list)) { list = new List <VRMSpringBone>(); rootMap.Add(springRoot, list); } list.Add(sb); } for (int i = 0; i < sb.ColliderGroups.Length; ++i) { var c = sb.ColliderGroups[i]; if (c == null) { yield return(Validation.Error($"{sb.name}.ColliderGroups[{i}] is null")); continue; } if (!hierarchy.Contains(c.transform)) { yield return(Validation.Error($"{sb.name}.ColliderGroups[{i}] is out of hierarchy")); continue; } } } foreach (var kv in rootMap) { if (kv.Value.Count > 1) { // * GameObjectが複数回ルートとして指定されてる(SpringBoneが同じでも別でも) var list = string.Join(", ", kv.Value.Select(x => string.IsNullOrEmpty(x.m_comment) ? x.name : x.m_comment)); yield return(Validation.Warning($"{kv.Key} found multiple. {list}")); } var rootInRootMap = new Dictionary <Transform, List <Transform> >(); foreach (var child in kv.Key.GetComponentsInChildren <Transform>()) { // * Rootから子をだどって、別のRootが現れる if (child == kv.Key) { continue; } if (!rootMap.ContainsKey(child)) { continue; } if (!rootInRootMap.TryGetValue(kv.Key, out List <Transform> rootInRoot)) { rootInRoot = new List <Transform>(); rootInRootMap.Add(kv.Key, rootInRoot); } rootInRoot.Add(child); } foreach (var rootList in rootInRootMap) { var list = string.Join(", ", rootList.Value.Select(x => x.name)); yield return(Validation.Warning($"{rootList.Key} hierarchy contains other root: {list}")); } } }
protected override bool DoGUI(bool isValid) { if (State.ExportRoot == null) { return(false); } // // T-Pose // if (State.ExportRoot.GetComponent <Animator>() != null) { var backup = GUI.enabled; GUI.enabled = State.ExportRoot.scene.IsValid(); if (s_foldT = EditorGUILayout.Foldout(s_foldT, "T-Pose")) { if (GUI.enabled) { EditorGUILayout.HelpBox(EnableTPose.ENALBE_TPOSE_BUTTON.Msg(), MessageType.Info); } else { EditorGUILayout.HelpBox(EnableTPose.DISABLE_TPOSE_BUTTON.Msg(), MessageType.Warning); } // // T-Pose // if (GUILayout.Button(VRMExportOptions.DO_TPOSE.Msg())) { if (State.ExportRoot != null) { // fallback Undo.RecordObjects(State.ExportRoot.GetComponentsInChildren <Transform>(), "tpose"); VRMBoneNormalizer.EnforceTPose(State.ExportRoot); Repaint(); } } if (GUILayout.Button(VRMExportOptions.DO_TPOSE.Msg() + "(unity internal)")) { if (State.ExportRoot != null) { Undo.RecordObjects(State.ExportRoot.GetComponentsInChildren <Transform>(), "tpose.internal"); if (InternalTPose.TryMakePoseValid(State.ExportRoot)) { // done Repaint(); } else { Debug.LogWarning("not found"); } } } } GUI.enabled = backup; } if (!isValid) { return(false); } // // GUI // _tab = TabBar.OnGUI(_tab); foreach (var meshInfo in m_meshes.Meshes) { switch (meshInfo.VertexColor) { case UniGLTF.VertexColorState.ExistsAndMixed: Validation.Warning($"{meshInfo.Renderers}: Both vcolor.multiply and not multiply unlit materials exist").DrawGUI(); break; } } return(DrawWizardGUI()); }
public static IEnumerable <Validation> Validate(this VRMBlendShapeProxy p) { if (p == null) { yield return(Validation.Error("VRMBlendShapeProxy is null")); yield break; } if (p.BlendShapeAvatar == null) { yield return(Validation.Error("BlendShapeAvatar is null")); yield break; } // presetがユニークか var used = new HashSet <BlendShapeKey>(); foreach (var c in p.BlendShapeAvatar.Clips) { var key = c.Key; if (used.Contains(key)) { yield return(Validation.Error($"duplicated BlendShapeKey: {key}")); } else { used.Add(key); } } var materialNames = new HashSet <string>(); foreach (var r in p.GetComponentsInChildren <Renderer>(true)) { foreach (var m in r.sharedMaterials) { if (!materialNames.Contains(m.name)) { materialNames.Add(m.name); } } } // 参照が生きているか foreach (var c in p.BlendShapeAvatar.Clips) { for (int i = 0; i < c.Values.Length; ++i) { var v = c.Values[i]; var target = p.transform.Find(v.RelativePath); if (target == null) { yield return(Validation.Warning($"{c}.Values[{i}].RelativePath({v.RelativePath} is not found")); } } for (int i = 0; i < c.MaterialValues.Length; ++i) { var v = c.MaterialValues[i]; if (!materialNames.Contains(v.MaterialName)) { yield return(Validation.Warning($"{c}.MaterialValues[{i}].MaterialName({v.MaterialName} is not found")); } } } }
private void OnGUI() { if (m_tmpMeta == null) { // OnDisable return; } EditorGUIUtility.labelWidth = 150; // lang M17N.Getter.OnGuiSelectLang(); EditorGUILayout.LabelField("ExportRoot"); { var root = (GameObject)EditorGUILayout.ObjectField(ExportRoot, typeof(GameObject), true); UpdateRoot(root); } // // ここでも validate している。ここで失敗して return した場合は Export UI を表示しない // // // root // if (ExportRoot == null) { Validation.Error(Msg(VRMExporterWizardMessages.ROOT_EXISTS)).DrawGUI(); return; } if (ExportRoot.transform.parent != null) { Validation.Error(Msg(VRMExporterWizardMessages.NO_PARENT)).DrawGUI(); return; } if (ExportRoot.transform.localRotation != Quaternion.identity || ExportRoot.transform.localScale != Vector3.one) { Validation.Error(Msg(VRMExporterWizardMessages.ROOT_WITHOUT_ROTATION_AND_SCALING_CHANGED)).DrawGUI(); return; } var renderers = ExportRoot.GetComponentsInChildren <Renderer>(); if (renderers.All(x => !x.EnableForExport())) { Validation.Error(Msg(VRMExporterWizardMessages.NO_ACTIVE_MESH)).DrawGUI(); return; } if (HasRotationOrScale(ExportRoot)) { if (m_settings.PoseFreeze) { EditorGUILayout.HelpBox("Root OK", MessageType.Info); } else { Validation.Warning(Msg(VRMExporterWizardMessages.ROTATION_OR_SCALEING_INCLUDED_IN_NODE)).DrawGUI(); } } else { if (m_settings.PoseFreeze) { Validation.Warning(Msg(VRMExporterWizardMessages.IS_POSE_FREEZE_DONE)).DrawGUI(); } else { EditorGUILayout.HelpBox("Root OK", MessageType.Info); } } // // animator // var animator = ExportRoot.GetComponent <Animator>(); if (animator == null) { Validation.Error(Msg(VRMExporterWizardMessages.NO_ANIMATOR)).DrawGUI(); return; } var avatar = animator.avatar; if (avatar == null) { Validation.Error(Msg(VRMExporterWizardMessages.NO_AVATAR_IN_ANIMATOR)).DrawGUI(); return; } if (!avatar.isValid) { Validation.Error(Msg(VRMExporterWizardMessages.AVATAR_IS_NOT_VALID)).DrawGUI(); return; } if (!avatar.isHuman) { Validation.Error(Msg(VRMExporterWizardMessages.AVATAR_IS_NOT_HUMANOID)).DrawGUI(); return; } { var l = animator.GetBoneTransform(HumanBodyBones.LeftUpperLeg); var r = animator.GetBoneTransform(HumanBodyBones.RightUpperLeg); var f = GetForward(l, r); if (Vector3.Dot(f, Vector3.forward) < 0.8f) { Validation.Error(Msg(VRMExporterWizardMessages.FACE_Z_POSITIVE_DIRECTION)).DrawGUI(); return; } } var jaw = animator.GetBoneTransform(HumanBodyBones.Jaw); if (jaw != null) { Validation.Warning(Msg(VRMExporterWizardMessages.JAW_BONE_IS_INCLUDED)).DrawGUI(); } else { EditorGUILayout.HelpBox("Animator OK", MessageType.Info); } // validation foreach (var v in m_validations) { v.DrawGUI(); } // Render contents using Generic Inspector GUI m_ScrollPosition = BeginVerticalScrollView(m_ScrollPosition, false, GUI.skin.verticalScrollbar, "OL Box"); GUIUtility.GetControlID(645789, FocusType.Passive); bool modified = DrawWizardGUI(); EditorGUILayout.EndScrollView(); // Create and Other Buttons { // errors GUILayout.BeginVertical(); // GUILayout.FlexibleSpace(); { GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUI.enabled = m_IsValid; const BindingFlags kInstanceInvokeFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy; if (m_OtherButton != "" && GUILayout.Button(m_OtherButton, GUILayout.MinWidth(100))) { MethodInfo method = GetType().GetMethod("OnWizardOtherButton", kInstanceInvokeFlags); if (method != null) { method.Invoke(this, null); GUIUtility.ExitGUI(); } else { Debug.LogError("OnWizardOtherButton has not been implemented in script"); } } if (m_CreateButton != "" && GUILayout.Button(m_CreateButton, GUILayout.MinWidth(100))) { MethodInfo method = GetType().GetMethod("OnWizardCreate", kInstanceInvokeFlags); if (method != null) { method.Invoke(this, null); } else { Debug.LogError("OnWizardCreate has not been implemented in script"); } Close(); GUIUtility.ExitGUI(); } GUI.enabled = true; GUILayout.EndHorizontal(); } GUILayout.EndVertical(); } GUILayout.Space(8); }
/// <summary> /// エクスポート可能か検証する /// </summary> /// <returns></returns> public IEnumerable <Validation> Validate() { if (ExportRoot == null) { yield break; } if (DuplicateBoneNameExists()) { yield return(Validation.Warning(Msg(VRMExporterWizardMessages.DUPLICATE_BONE_NAME_EXISTS))); } if (m_settings.ReduceBlendshape && ExportRoot.GetComponent <VRMBlendShapeProxy>() == null) { yield return(Validation.Error(Msg(VRMExporterWizardMessages.NEEDS_VRM_BLENDSHAPE_PROXY))); } var vertexColor = ExportRoot.GetComponentsInChildren <SkinnedMeshRenderer>().Any(x => x.sharedMesh.colors.Length > 0); if (vertexColor) { yield return(Validation.Warning(Msg(VRMExporterWizardMessages.VERTEX_COLOR_IS_INCLUDED))); } var renderers = ExportRoot.GetComponentsInChildren <Renderer>(); var materials = renderers.SelectMany(x => x.sharedMaterials).Distinct(); foreach (var material in materials) { if (material.shader.name == "Standard") { // standard continue; } if (VRMMaterialExporter.UseUnlit(material.shader.name)) { // unlit continue; } if (VRMMaterialExporter.VRMExtensionShaders.Contains(material.shader.name)) { // VRM supported continue; } yield return(Validation.Warning($"Material: {material.name}. Unknown Shader: \"{material.shader.name}\" is used. {Msg(VRMExporterWizardMessages.UNKNOWN_SHADER)}")); } foreach (var material in materials) { if (IsFileNameLengthTooLong(material.name)) { yield return(Validation.Error(Msg(VRMExporterWizardMessages.FILENAME_TOO_LONG) + material.name)); } } var textureNameList = new List <string>(); foreach (var material in materials) { var shader = material.shader; int propertyCount = ShaderUtil.GetPropertyCount(shader); for (int i = 0; i < propertyCount; i++) { if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.TexEnv) { if ((material.GetTexture(ShaderUtil.GetPropertyName(shader, i)) != null)) { var textureName = material.GetTexture(ShaderUtil.GetPropertyName(shader, i)).name; if (!textureNameList.Contains(textureName)) { textureNameList.Add(textureName); } } } } } foreach (var textureName in textureNameList) { if (IsFileNameLengthTooLong(textureName)) { yield return(Validation.Error(Msg(VRMExporterWizardMessages.FILENAME_TOO_LONG) + textureName)); } } var vrmMeta = ExportRoot.GetComponent <VRMMeta>(); if (vrmMeta != null && vrmMeta.Meta != null && vrmMeta.Meta.Thumbnail != null) { var thumbnailName = vrmMeta.Meta.Thumbnail.name; if (IsFileNameLengthTooLong(thumbnailName)) { yield return(Validation.Error(Msg(VRMExporterWizardMessages.FILENAME_TOO_LONG) + thumbnailName)); } } var meshFilters = ExportRoot.GetComponentsInChildren <MeshFilter>(); var meshesName = meshFilters.Select(x => x.sharedMesh.name).Distinct(); foreach (var meshName in meshesName) { if (IsFileNameLengthTooLong(meshName)) { yield return(Validation.Error(Msg(VRMExporterWizardMessages.FILENAME_TOO_LONG) + meshName)); } } var skinnedmeshRenderers = ExportRoot.GetComponentsInChildren <SkinnedMeshRenderer>(); var skinnedmeshesName = skinnedmeshRenderers.Select(x => x.sharedMesh.name).Distinct(); foreach (var skinnedmeshName in skinnedmeshesName) { if (IsFileNameLengthTooLong(skinnedmeshName)) { yield return(Validation.Error(Msg(VRMExporterWizardMessages.FILENAME_TOO_LONG) + skinnedmeshName)); } } }
public static bool IsValid(this VRMFirstPerson.RendererFirstPersonFlags r, string name, out Validation validation) { if (r.Renderer == null) { validation = Validation.Error($"{name}.Renderer is null", ValidationContext.Create(extended)); return(false); } if (!Hierarchy.Contains(r.Renderer.transform)) { validation = Validation.Error($"{name}.Renderer is out of hierarchy", ValidationContext.Create(extended)); return(false); } // if (!r.Renderer.EnableForExport()) // { // validation = Validation.Error($"{name}.Renderer is not active", ValidationContext.Create(extended)); // return false; // } validation = default; return(true); }
/// <summary> /// エクスポート可能か検証する /// </summary> /// <returns></returns> public IEnumerable <Validation> Validate() { if (Source == null) { yield return(Validation.Error("Require source")); yield break; } if (Source.transform.position != Vector3.zero || Source.transform.rotation != Quaternion.identity || Source.transform.localScale != Vector3.one) { // EditorUtility.DisplayDialog("Error", "The Root transform should have Default translation, rotation and scale.", "ok"); yield return(Validation.Warning("The Root translation, rotation and scale will be dropped.")); } var animator = Source.GetComponent <Animator>(); if (animator == null) { yield return(Validation.Error("Require animator. ")); } else if (animator.avatar == null) { yield return(Validation.Error("Require animator.avatar. ")); } else if (!animator.avatar.isValid) { yield return(Validation.Error("Animator.avatar is not valid. ")); } else if (!animator.avatar.isHuman) { yield return(Validation.Error("Animator.avatar is not humanoid. Please change model's AnimationType to humanoid. ")); } var jaw = animator.GetBoneTransform(HumanBodyBones.Jaw); if (jaw != null) { yield return(Validation.Warning("Jaw bone is included. It may not be what you intended. Please check the humanoid avatar setting screen")); } if (DuplicateBoneNameExists()) { yield return(Validation.Warning("There is a bone with the same name in the hierarchy. If exported, these bones will be automatically renamed.")); } if (string.IsNullOrEmpty(Title)) { yield return(Validation.Error("Require Title. ")); } if (string.IsNullOrEmpty(Version)) { yield return(Validation.Error("Require Version. ")); } if (string.IsNullOrEmpty(Author)) { yield return(Validation.Error("Require Author. ")); } if (ReduceBlendshape && Source.GetComponent <VRMBlendShapeProxy>() == null) { yield return(Validation.Error("ReduceBlendshapeSize needs VRMBlendShapeProxy. You need to convert to VRM once.")); } var vertexColor = Source.GetComponentsInChildren <SkinnedMeshRenderer>().Any(x => x.sharedMesh.colors.Length > 0); if (vertexColor) { yield return(Validation.Warning("This model contains vertex color")); } var renderers = Source.GetComponentsInChildren <Renderer>(); if (renderers.All(x => !x.gameObject.activeInHierarchy)) { yield return(Validation.Error("No active mesh")); } var materials = renderers.SelectMany(x => x.sharedMaterials).Distinct(); foreach (var material in materials) { if (material.shader.name == "Standard") { // standard continue; } if (VRMMaterialExporter.UseUnlit(material.shader.name)) { // unlit continue; } if (VRMMaterialExporter.VRMExtensionShaders.Contains(material.shader.name)) { // VRM supported continue; } yield return(Validation.Warning(string.Format("{0}: unknown shader '{1}' is used. this will export as `Standard` fallback", material.name, material.shader.name))); } foreach (var material in materials) { if (IsFileNameLengthTooLong(material.name)) { yield return(Validation.Error(string.Format("FileName '{0}' is too long. ", material.name))); } } var textureNameList = new List <string>(); foreach (var material in materials) { var shader = material.shader; int propertyCount = ShaderUtil.GetPropertyCount(shader); for (int i = 0; i < propertyCount; i++) { if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.TexEnv) { if ((material.GetTexture(ShaderUtil.GetPropertyName(shader, i)) != null)) { var textureName = material.GetTexture(ShaderUtil.GetPropertyName(shader, i)).name; if (!textureNameList.Contains(textureName)) { textureNameList.Add(textureName); } } } } } foreach (var textureName in textureNameList) { if (IsFileNameLengthTooLong(textureName)) { yield return(Validation.Error(string.Format("FileName '{0}' is too long. ", textureName))); } } var vrmMeta = Source.GetComponent <VRMMeta>(); if (vrmMeta != null && vrmMeta.Meta != null && vrmMeta.Meta.Thumbnail != null) { var thumbnailName = vrmMeta.Meta.Thumbnail.name; if (IsFileNameLengthTooLong(thumbnailName)) { yield return(Validation.Error(string.Format("FileName '{0}' is too long. ", thumbnailName))); } } var meshFilters = Source.GetComponentsInChildren <MeshFilter>(); var meshesName = meshFilters.Select(x => x.sharedMesh.name).Distinct(); foreach (var meshName in meshesName) { if (IsFileNameLengthTooLong(meshName)) { yield return(Validation.Error(string.Format("FileName '{0}' is too long. ", meshName))); } } var skinnedmeshRenderers = Source.GetComponentsInChildren <SkinnedMeshRenderer>(); var skinnedmeshesName = skinnedmeshRenderers.Select(x => x.sharedMesh.name).Distinct(); foreach (var skinnedmeshName in skinnedmeshesName) { if (IsFileNameLengthTooLong(skinnedmeshName)) { yield return(Validation.Error(string.Format("FileName '{0}' is too long. ", skinnedmeshName))); } } }
public IEnumerable <Validation> CanExport() { if (Source == null) { yield return(Validation.Error("Require source")); yield break; } var animator = Source.GetComponent <Animator>(); if (animator == null) { yield return(Validation.Error("Require animator. ")); } else if (animator.avatar == null) { yield return(Validation.Error("Require animator.avatar. ")); } else if (!animator.avatar.isValid) { yield return(Validation.Error("Animator.avatar is not valid. ")); } else if (!animator.avatar.isHuman) { yield return(Validation.Error("Animator.avatar is not humanoid. Please change model's AnimationType to humanoid. ")); } var jaw = animator.GetBoneTransform(HumanBodyBones.Jaw); if (jaw != null) { yield return(Validation.Warning("Jaw bone is included. It may not be what you intended. Please check the humanoid avatar setting screen")); } if (DuplicateBoneNameExists()) { yield return(Validation.Error("Find duplicate Bone names. Please check model's bone names. ")); } if (string.IsNullOrEmpty(Title)) { yield return(Validation.Error("Require Title. ")); } if (string.IsNullOrEmpty(Version)) { yield return(Validation.Error("Require Version. ")); } if (string.IsNullOrEmpty(Author)) { yield return(Validation.Error("Require Author. ")); } if (ReduceBlendshapeSize && Source.GetComponent <VRMBlendShapeProxy>() == null) { yield return(Validation.Error("ReduceBlendshapeSize is need VRMBlendShapeProxy, you need to convert to VRM once.")); } var renderers = Source.GetComponentsInChildren <Renderer>(); if (renderers.All(x => !x.gameObject.activeInHierarchy)) { yield return(Validation.Error("No active mesh")); } var materials = renderers.SelectMany(x => x.sharedMaterials).Distinct(); foreach (var material in materials) { if (material.shader.name == "Standard") { // standard continue; } if (MaterialExporter.UseUnlit(material.shader.name)) { // unlit continue; } if (VRMMaterialExporter.VRMExtensionShaders.Contains(material.shader.name)) { // VRM supported continue; } yield return(Validation.Warning(string.Format("unknown material '{0}' is used. this will export as `Standard` fallback", material.shader.name))); } }