bool CreateMultiMaterialDataButton(bool changed) { EditorGUI.BeginDisabledGroup(m_Renderer == null); if (GUILayout.Button("Create From Renderer")) { var saveMultiMaterialData = CreateInstance <MultiMaterialData>(); var rendererMaterialArray = new MaterialArray { materials = m_Renderer.sharedMaterials }; saveMultiMaterialData.materialArrayData = rendererMaterialArray; var path = EditorUtility.SaveFilePanelInProject("Multi Material Data Save Window", m_Renderer.gameObject.name + " Multi Material Data", "asset", "Enter a file name to save the multi material data to"); if (!string.IsNullOrEmpty(path)) { AssetDatabase.CreateAsset(saveMultiMaterialData, path); AssetDatabase.Refresh(); } var loadMultiMaterialData = AssetDatabase.LoadAssetAtPath <MultiMaterialData>(path); if (loadMultiMaterialData != null) { serializedObject.Update(); var dataProp = serializedObject.FindProperty(MultiMaterial.multiMaterialDataPub); dataProp.objectReferenceValue = loadMultiMaterialData; serializedObject.ApplyModifiedProperties(); } ValidateEditorData(); return(true); } EditorGUI.EndDisabledGroup(); return(changed); }
public static void SetCheckMaterialShaders(MaterialArray materialArray, Material mat) { var matSerial = new SerializedObject(mat); matSerial.Update(); var shaderSerial = matSerial.FindProperty("m_Shader"); matSerial.ApplyModifiedProperties(); foreach (var material in materialArray.materials) { if (material == null || material.name == k_DefaultMaterial) { continue; } var targetMatSerial = new SerializedObject(material); var targetShader = targetMatSerial.FindProperty("m_Shader"); if (shaderSerial != targetShader) { targetMatSerial.Update(); targetShader.objectReferenceValue = shaderSerial.objectReferenceValue; targetMatSerial.ApplyModifiedProperties(); } } }
/// <summary> /// Checks that the individual Material Editor's material in the 'materialEditors' array matches the material /// at the corresponding index in the 'materialArray' /// </summary> /// <param name="materialEditors">Material Editor Array that will be checked against 'materialArray'</param> /// <param name="materialArray">Material Array that the 'materialEditors' should match</param> /// <returns></returns> public static bool CheckMaterialEditors(MaterialEditor[] materialEditors, MaterialArray materialArray) { if (materialEditors == null || materialEditors.Length < 1) { return(false); } if (materialArray == null || materialArray.materials.Length < 1) { return(false); } if (materialEditors.Length != materialArray.materials.Length) { return(false); } for (var i = 0; i < materialArray.materials.Length; i++) { if (materialArray.materials[i] == null && materialEditors[i] == null) { continue; } if (materialArray.materials[i] != null && materialEditors[i] == null || materialEditors[i] != null && materialArray.materials[i] == null || (Material)materialEditors[i].target != materialArray.materials[i]) { return(false); } } return(true); }
public static bool AddSelectedButton(SerializedObject serializedObject, MaterialArray materialArray, string text, bool includeChildren, bool includeInactive) { if (GUILayout.Button(text)) { serializedObject.Update(); var matHash = new HashSet <Material>(); if (materialArray.materials.Length > 0) { foreach (var mat in materialArray.materials) { matHash.Add(mat); } } foreach (var obj in Selection.objects) { var mat = obj as Material; if (mat != null) { matHash.Add(mat); } var go = obj as GameObject; if (go != null) { var meshRenderers = includeChildren? go.GetComponentsInChildren <MeshRenderer>(includeInactive) : go.GetComponents <MeshRenderer>(); foreach (var meshRenderer in meshRenderers) { foreach (var sharedMaterial in meshRenderer.sharedMaterials) { matHash.Add(sharedMaterial); } } var skinnedMeshRenderers = includeChildren? go.GetComponentsInChildren <SkinnedMeshRenderer>(includeInactive) : go.GetComponents <SkinnedMeshRenderer>(); foreach (var skinnedMeshRenderer in skinnedMeshRenderers) { foreach (var sharedMaterial in skinnedMeshRenderer.sharedMaterials) { matHash.Add(sharedMaterial); } } } } Undo.RecordObject(serializedObject.targetObject, "add selected"); materialArray.materials = matHash.ToArray(); serializedObject.ApplyModifiedProperties(); return(true); } return(false); }
public static bool AddSelectedButtons(SerializedObject serializedObject, MaterialArray materialArray) { EditorGUILayout.BeginVertical(); var changed = AddSelectedButton(serializedObject, materialArray, "Add Selected", false, false); EditorGUILayout.BeginHorizontal(); changed = AddSelectedButton(serializedObject, materialArray, "Include Children", true, false) || changed; changed = AddSelectedButton(serializedObject, materialArray, "Include Inactive", true, true) || changed; EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); return(changed); }
void ValidateEditorData() { if (m_MultiMaterial.multiMaterialData != null && m_MultiMaterialData == null) { m_MultiMaterialData = new SerializedObject(m_MultiMaterial.multiMaterialData); } if (useRenderer) { if (m_Renderer == null) { m_Renderer = m_MultiMaterial.GetComponent <Renderer>(); } m_RendererMaterialArray = m_Renderer != null ? new MaterialArray { materials = m_Renderer.sharedMaterials } : null; m_MaterialProperties = null; } else { m_Renderer = null; m_RendererMaterialArray = null; } if (m_Renderer == null) { m_Materials = m_MultiMaterialData.FindProperty(string.Format("{0}.{1}", MultiMaterialData.materialArrayPub, MaterialArray.materialsPub)); var materialPropList = new List <SerializedProperty>(); for (var i = 0; i < m_Materials.arraySize; ++i) { materialPropList.Add(m_Materials.GetArrayElementAtIndex(i)); } m_MaterialProperties = materialPropList.ToArray(); } }
/// <summary> /// Popup Shader selector that mimics the material shader popup in the material header /// </summary> /// <param name="material">The material that is the parent of the shader for popup.</param> /// <param name="materialArray">Material array the shader popup operates on.</param> public static void ShaderPopup(Material material, MaterialArray materialArray) { var index = Array.FindIndex(shaderNames, s => s == material.shader.name); // Have to use our own popup since you cannot use the material editor popup if (index < 0 || index > shaderNames.Length) { UpdateShaderNames(material); EditorGUILayout.Popup(index, shaderNameGUIContents); return; } index = EditorGUILayout.Popup(index, shaderNameGUIContents); if (shaderNames[index] != material.shader.name) { var matSerial = new SerializedObject(material); matSerial.Update(); var shaderSerial = matSerial.FindProperty("m_Shader"); shaderSerial.objectReferenceValue = Shader.Find(shaderNames[index]); matSerial.ApplyModifiedProperties(); MultiMaterialEditorUtilities.SetCheckMaterialShaders(materialArray, material); } }
public static void UpdateMaterials(MaterialArray materialArray, Material controlMaterial, bool syncAll = false) { if (materialArray.materials.Length < 1 && controlMaterial == null) { return; } SetCheckMaterialShaders(materialArray, controlMaterial); var controlMaterialObject = new SerializedObject(controlMaterial); SerializedObject checkedMaterialObject = null; if (!syncAll) { Material checkedMaterial = null; foreach (var material in materialArray.materials) { if (material != controlMaterial && material.name != k_DefaultMaterial) { checkedMaterial = material; break; } } if (checkedMaterial == null) { return; } checkedMaterialObject = new SerializedObject(checkedMaterial); } // Find the Property (Properties) that changed in the Material Array var propertiesToChange = GetPropertiesToChange(controlMaterialObject, checkedMaterialObject, syncAll); var matHash = new HashSet <Material>(materialArray.materials); //Convert each material in the hash into a list of serialized accessors. var matObjs = (from mat in matHash where mat != null && mat.name != k_DefaultMaterial select new SerializedObject(mat)).ToList(); if (propertiesToChange != null && propertiesToChange.Count > 0) { foreach (var matObj in matObjs) { matObj.Update(); } foreach (var propertyToChange in propertiesToChange) { foreach (var matObj in matObjs) { var serializedProperty = matObj.FindProperty(propertyToChange.Key); if (serializedProperty != null) { switch (serializedProperty.propertyType) { case SerializedPropertyType.AnimationCurve: serializedProperty.animationCurveValue = propertyToChange.Value.animationCurveValue; break; case SerializedPropertyType.ArraySize: if (serializedProperty.isArray) { serializedProperty.arraySize = propertyToChange.Value.arraySize; } break; case SerializedPropertyType.Boolean: serializedProperty.boolValue = propertyToChange.Value.boolValue; break; case SerializedPropertyType.Bounds: serializedProperty.boundsValue = propertyToChange.Value.boundsValue; break; case SerializedPropertyType.Character: break; case SerializedPropertyType.Color: serializedProperty.colorValue = propertyToChange.Value.colorValue; break; case SerializedPropertyType.Enum: serializedProperty.enumValueIndex = propertyToChange.Value.enumValueIndex; break; #if UNITY_2017_1_OR_NEWER case SerializedPropertyType.ExposedReference: serializedProperty.exposedReferenceValue = propertyToChange.Value.exposedReferenceValue; break; case SerializedPropertyType.FixedBufferSize: // SerializedProperty.fixedBufferSize is read only break; #endif case SerializedPropertyType.Generic: break; case SerializedPropertyType.Gradient: break; case SerializedPropertyType.Float: serializedProperty.floatValue = propertyToChange.Value.floatValue; break; case SerializedPropertyType.Integer: serializedProperty.intValue = propertyToChange.Value.intValue; break; case SerializedPropertyType.String: serializedProperty.stringValue = propertyToChange.Value.stringValue; break; case SerializedPropertyType.Rect: serializedProperty.rectValue = propertyToChange.Value.rectValue; break; case SerializedPropertyType.Quaternion: serializedProperty.quaternionValue = propertyToChange.Value.quaternionValue; break; case SerializedPropertyType.Vector2: serializedProperty.vector2Value = propertyToChange.Value.vector2Value; break; case SerializedPropertyType.Vector3: serializedProperty.vector3Value = propertyToChange.Value.vector3Value; break; case SerializedPropertyType.Vector4: serializedProperty.vector4Value = propertyToChange.Value.vector4Value; break; case SerializedPropertyType.ObjectReference: serializedProperty.objectReferenceValue = propertyToChange.Value.objectReferenceValue; break; case SerializedPropertyType.LayerMask: break; } } } } foreach (var matObj in matObjs) { matObj.ApplyModifiedProperties(); } } }
/// <summary> /// Draw the Multi Material Inspector GUI using Material Editors for each material in Material Array /// </summary> /// <param name="serializedObject">Target serialized object from the inspector</param> /// <param name="materialArray">Material array to be used in inspector</param> /// <param name="materialEditors">Material Editors for each material in materialArray</param> /// <param name="changed">Editor property changed from outside of this method</param> /// <param name="materialProperties">Array of serialized properties that are the materials in the Material /// Array. Used for property drawer in material header</param> public static void OnInspectorGUI(SerializedObject serializedObject, MaterialArray materialArray, ref MaterialEditor[] materialEditors, bool changed = false, SerializedProperty[] materialProperties = null) { var materialEditorReady = true; if (changed || !CheckMaterialEditors(materialEditors, materialArray)) { materialEditorReady = RebuildMaterialEditors(ref materialEditors, materialArray) && Event.current.type == EventType.Layout; } if (materialEditorReady) { for (var i = 0; i < materialEditors.Length; i++) { if (materialArray.materials[i] != null && materialEditors[i] != null) { var material = materialEditors[i].target as Material; OnMiniMaterialArrayHeaderGUI(serializedObject, ref materialEditors[i], materialArray, materialProperties != null && materialProperties.Length > i && materialProperties[i] != null? materialProperties[i] : null); EditorGUI.BeginDisabledGroup(material != null && material.name == k_DefaultMaterial); // Draw the Material Editor Body if (materialEditors[i].isVisible) { EditorGUI.BeginChangeCheck(); if (GUILayout.Button("Sync to Material")) { MultiMaterialEditorUtilities.UpdateMaterials(materialArray, material, true); } materialEditors[i].OnInspectorGUI(); if (EditorGUI.EndChangeCheck()) { MultiMaterialEditorUtilities.UpdateMaterials(materialArray, material); } } EditorGUI.EndDisabledGroup(); } else { if (materialProperties != null) { EditorGUI.BeginChangeCheck(); materialProperties[i].serializedObject.Update(); EditorGUILayout.PropertyField(materialProperties[i], new GUIContent("Material")); materialProperties[i].serializedObject.ApplyModifiedProperties(); if (EditorGUI.EndChangeCheck()) { RebuildMaterialEditors(ref materialEditors, materialArray); } } else { EditorGUILayout.LabelField(k_NullMaterialWarning, s_RichTextStyle); } } } } }
/// <summary> /// Rebuilds material editor if materials in editor do not match those in the material array /// </summary> /// <param name="materialEditors">Material Editors for each material in materialArray</param> /// <param name="materialArray">Material array to be used in inspector</param> /// <returns></returns> public static bool RebuildMaterialEditors(ref MaterialEditor[] materialEditors, MaterialArray materialArray) { // If check fails try to rebuild editors then recheck if (!CheckMaterialEditors(materialEditors, materialArray)) { if (materialEditors != null) { for (var i = 0; i < materialEditors.Length; i++) { if (materialEditors[i] == null) { continue; } UnityObject.DestroyImmediate(materialEditors[i]); materialEditors[i] = null; } } var rebuildShaders = true; if (materialArray != null && materialArray.materials != null && materialArray.materials.Length > 0) { materialEditors = new MaterialEditor[materialArray.materials.Length]; for (var i = 0; i < materialArray.materials.Length; i++) { var material = materialArray.materials[i]; if (material == null) { continue; } materialEditors[i] = Editor.CreateEditor(material) as MaterialEditor; if (!rebuildShaders) { continue; } UpdateShaderNames(material); rebuildShaders = false; } } else { materialEditors = new MaterialEditor[0]; } // Need to try and recheck after rebuild to avoid change in gui between layout and repaint return(CheckMaterialEditors(materialEditors, materialArray)); } return(true); }
/// <summary> /// Draws a custom gui that mimics the Material Editor Header. /// We need to use custom gui since the normal Material Header does not respect all the editor gui functions /// that can surround it and since we cannot detect changes in the Shader Foldout /// </summary> /// <param name="serializedObject">Target serialized object from the inspector</param> /// <param name="materialEditor">Material Editors for each material in materialArray</param> /// <param name="materialArray">Material array to be used in inspector</param> /// <param name="serializedMaterial">Serialized property that represents the material field in the header /// If null the material name is drawn in place of the field.</param> public static void OnMiniMaterialArrayHeaderGUI(SerializedObject serializedObject, ref MaterialEditor materialEditor, MaterialArray materialArray, SerializedProperty serializedMaterial = null) { if (materialEditor == null || !(materialEditor.target is Material)) { return; } EditorGUILayout.BeginHorizontal(EditorStyles.textArea); // Begin Header Area if (s_Foldout == null || s_FoldoutPreDrop == null) { FindMaterialArrayIcons(); } // Material Editor body is only drawn when 'm_IsVisible' == true // this is normally set in the Material Editor Inspector's Header with the foldout // We need to be able to read and write to private field to see the material editor body. var isVisibleField = typeof(MaterialEditor).GetField("m_IsVisible", BindingFlags.NonPublic | BindingFlags.Instance); var isVisibleValue = isVisibleField.GetValue(materialEditor) as bool? ?? false; if (GUILayout.Button(isVisibleValue? s_Foldout: s_FoldoutPreDrop, GUIStyle.none, GUILayout.ExpandWidth(false))) { isVisibleField.SetValue(materialEditor, !isVisibleValue); } var material = (Material)materialEditor.target; var iconRect = EditorGUILayout.GetControlRect(false, k_IconSize, GUILayout.MaxWidth(k_IconSize)); OnHeaderIconGUI(ref materialEditor, iconRect); EditorGUILayout.BeginVertical(); // Begin Title and Shader Area if (serializedMaterial == null) { EditorGUILayout.LabelField(new GUIContent(material.name)); } else { serializedMaterial.serializedObject.Update(); EditorGUILayout.PropertyField(serializedMaterial, GUIContent.none); serializedMaterial.serializedObject.ApplyModifiedProperties(); } EditorGUI.BeginDisabledGroup(material != null && material.name == k_DefaultMaterial); ShaderPopup(material, materialArray); EditorGUI.EndDisabledGroup(); EditorGUILayout.EndVertical(); // End Title and Shader Area EditorGUILayout.EndHorizontal(); // End Header Area }