using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using System;
namespace UnityEditor.Polybrush
{
///
/// CustomEditor for PrefabPalette.
///
[CustomEditor(typeof(PrefabPalette))]
public class PrefabPaletteEditor : Editor
{
internal SerializedProperty prefabs;
internal HashSet selected = new HashSet();
internal PrefabLoadoutEditor loadoutEditor;
internal Action> onSelectionChanged = null;
static internal GUIStyle paletteStyle = new GUIStyle();
GUIContent m_GCCurrentPaletteLabel = new GUIContent("Current Palette", "Currently selected Prefabs Palette");
GUIContent m_GCPlacementSettingsLabel = new GUIContent("Placement Settings", "Currently selected Prefab(s) settings");
private void OnEnable()
{
paletteStyle.padding = new RectOffset(8, 8, 8, 8);
prefabs = serializedObject.FindProperty("prefabs");
}
///
/// Creates a New PrefabPalette
///
/// The Newly created PrefabPalette
internal static PrefabPalette AddNew()
{
string path = PolyEditorUtility.UserAssetDirectory + "Prefab Palette";
if (string.IsNullOrEmpty(path))
path = "Assets";
path = AssetDatabase.GenerateUniqueAssetPath(path + "/New Prefab Palette.asset");
if (!string.IsNullOrEmpty(path))
{
PrefabPalette palette = ScriptableObject.CreateInstance();
palette.SetDefaultValues();
AssetDatabase.CreateAsset(palette, path);
AssetDatabase.Refresh();
EditorGUIUtility.PingObject(palette);
return palette;
}
return null;
}
///
/// Overriding function to make a custom IMGUI inspector.
///
public override void OnInspectorGUI()
{
if (loadoutEditor != null)
{
OnInspectorGUI_Internal(64);
}
}
private bool IsDeleteKey(Event e)
{
return e.keyCode == KeyCode.Backspace;
}
///
/// Draw everything concerning a single Prefab Palette in the Polybrush Window
///
/// size of the preview textures
internal void OnInspectorGUI_Internal(int thumbSize)
{
PolyGUILayout.Label(m_GCCurrentPaletteLabel);
serializedObject.Update();
int count = prefabs != null ? prefabs.arraySize : 0;
Rect dropDownZone = EditorGUILayout.BeginVertical(paletteStyle);
dropDownZone.width = EditorGUIUtility.currentViewWidth;
Rect backGroundRect = new Rect(dropDownZone);
if (count != 0)
{
const int pad = 4;
int size = thumbSize + pad;
backGroundRect.x += 8;
backGroundRect.y += 4;
// The backgroundRect is currently as wide as the current view.
// Adjust it to take the size of the vertical scrollbar and padding into account.
backGroundRect.width -= (20 + (int)GUI.skin.verticalScrollbar.fixedWidth);
// size variable will not take into account the padding to the right of all the thumbnails,
// therefore it needs to be substracted from the width.
int container_width = ((int)Mathf.Floor(backGroundRect.width) - (pad + 1));
int columns = (int)Mathf.Floor(container_width / size);
int rows = count / columns + (count % columns == 0 ? 0 : 1);
if (rows < 1) rows = 1;
backGroundRect.height = 8 + rows * thumbSize + (rows - 1) * pad;
EditorGUI.DrawRect(backGroundRect, EditorGUIUtility.isProSkin ? PolyGUI.k_BoxBackgroundDark : PolyGUI.k_BoxBackgroundLight);
int currentIndex = 0;
for (int i = 0; i < rows; i++)
{
var horizontalRect = EditorGUILayout.BeginHorizontal();
for (int j = 0; j < columns; j++)
{
GUILayout.Space(pad);
var prefab = prefabs.GetArrayElementAtIndex(currentIndex);
var previewRectXPos = pad + j * size + horizontalRect.x;
DrawPrefabPreview(prefab, currentIndex, thumbSize, previewRectXPos, horizontalRect.y);
currentIndex++;
if (currentIndex >= count)
break;
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(pad);
}
EditorGUILayout.EndVertical();
if (selected.Count > 0)
{
EditorGUILayout.LabelField(m_GCPlacementSettingsLabel);
GUILayout.Space(pad);
}
EditorGUILayout.BeginVertical();
foreach (var i in selected)
DrawSinglePrefabPlacementSettings(prefabs.GetArrayElementAtIndex(i), i);
/// Little Hack to get the Rect for dropping new prefabs
Rect endDropDownZone = EditorGUILayout.BeginVertical();
dropDownZone.height = endDropDownZone.y - dropDownZone.y;
EditorGUILayout.EndVertical();
}
else
{
dropDownZone.height = thumbSize;
var r = EditorGUILayout.BeginVertical(GUILayout.Height(thumbSize+4));
EditorGUI.DrawRect(r, EditorGUIUtility.isProSkin ? PolyGUI.k_BoxBackgroundDark : PolyGUI.k_BoxBackgroundLight);
GUILayout.FlexibleSpace();
GUILayout.Label("Drag Prefabs Here!", EditorStyles.centeredGreyMiniLabel);
GUILayout.FlexibleSpace();
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndVertical();
Event e = Event.current;
if (dropDownZone.Contains(e.mousePosition) &&
(e.type == EventType.DragUpdated || e.type == EventType.DragPerform) && DragAndDrop.objectReferences.Length > 0)
{
if (PolyEditorUtility.ContainsPrefabAssets(DragAndDrop.objectReferences))
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
else
DragAndDrop.visualMode = DragAndDropVisualMode.Rejected;
if (e.type == EventType.DragPerform)
{
DragAndDrop.AcceptDrag();
IEnumerable dragAndDropReferences = DragAndDrop.objectReferences
.Where(x => x is GameObject && PolyEditorUtility.IsPrefabAsset(x)).Cast();
foreach (GameObject go in dragAndDropReferences)
{
prefabs.InsertArrayElementAtIndex(prefabs.arraySize);
SerializedProperty last = prefabs.GetArrayElementAtIndex(prefabs.arraySize - 1);
SerializedProperty gameObject = last.FindPropertyRelative("gameObject");
gameObject.objectReferenceValue = go;
PlacementSettings.PopulateSerializedProperty(last.FindPropertyRelative("settings"));
last.FindPropertyRelative("name").stringValue = go.name;
}
}
}
if (e.type == EventType.KeyUp)
{
if (IsDeleteKey(e) && !GUI.GetNameOfFocusedControl().Contains("cancelbackspace"))
{
PrefabPalette t = target as PrefabPalette;
t.RemoveRange(selected.ToArray());
selected.Clear();
if (onSelectionChanged != null)
onSelectionChanged(null);
PolybrushEditor.DoRepaint();
}
}
serializedObject.ApplyModifiedProperties();
redrawCounter += 1;
}
private bool shouldopencontextmenu = false;
private int rightClickTime = 0;
private int redrawCounter = 0;
private int idx = -1;
///
/// Draws previews for a prefab in the palette.
///
/// Prefab being previewed
/// index of the prefab in `prefabs`
/// Size of the preview texture
private void DrawPrefabPreview(SerializedProperty prefab, int index, int thumbSize, float x, float y)
{
Rect r = new Rect(x, y, thumbSize, thumbSize);
Rect rightClickZone = new Rect(r);
// Texture Preview
UnityEngine.Object o = prefab.FindPropertyRelative("gameObject").objectReferenceValue;
Texture2D preview = PreviewsDatabase.GetAssetPreview(o);
if (selected.Contains(index))
{
Rect r2 = new Rect(r);
r2.x -= 1;
r2.y -= 1;
r2.width += 2;
r2.height += 2;
EditorGUI.DrawRect(r2, Color.blue);
}
EditorGUI.DrawPreviewTexture(r, preview);
// Those numbers were obtained by empirical experimentation
r.x += thumbSize - 17;
r.y += thumbSize - 17;
r.width = 17;
r.height = 17;
LoadoutInfo li = new LoadoutInfo(target as PrefabPalette, index);
bool isLoaded = loadoutEditor.ContainsPrefab(li);
Event e = Event.current;
bool rightClick = (e.type == EventType.MouseDown || e.type == EventType.ContextClick) && rightClickZone.Contains(e.mousePosition) && e.button == 1;
bool b1 = GUI.Toggle(r, isLoaded, "");
// Reducing the width by 1 to ensure the button is not larger than the thumbnail.
// Otherwise button is slightly too large and horizontal scrollbar may appear.
bool b2 = GUILayout.Button("", GUIStyle.none, GUILayout.Width(thumbSize-1), GUILayout.Height(thumbSize));
// Set the focus to nothing in case the user want to press delete or backspace key
// I dont know why but If we don't do that the Textfield with the name of prefab settings never looses focus
if (b2 || rightClick)
{
GUI.FocusControl(null);
e.Use();
}
if (rightClick)
{
rightClickTime = redrawCounter;
shouldopencontextmenu = true;
idx = index;
if (!selected.Contains(index))
{
selected.Clear();
selected.Add(index);
}
return;
}
else if (shouldopencontextmenu && redrawCounter > rightClickTime)
{
loadoutEditor.OpenCopyPasteMenu(new LoadoutInfo(target as PrefabPalette, idx), selected);
shouldopencontextmenu = false;
idx = -1;
// reset the redraw counter to avoid overflow
redrawCounter = 0;
}
if (b1 && !isLoaded)
{
loadoutEditor.AddPrefabInLoadout(li);
//loadoutEditor.loadouts.Add(li);
}
else if (!b1 && isLoaded)
{
loadoutEditor.RemovePrefabFromLoadout(li);
//loadoutEditor.loadouts.Remove(li);
}
else if (b2)
{
if (Event.current.shift || Event.current.control)
{
if (!selected.Add(index))
selected.Remove(index);
}
else
{
if (selected.Count == 1 && selected.Contains(index))
selected.Remove(index);
else
{
selected.Clear();
selected.Add(index);
}
}
if (onSelectionChanged != null)
onSelectionChanged(selected);
GUI.changed = true;
}
}
///
/// Show the specific placement settings for a prefab in the palette
///
/// The prefab whose settings we're showing
/// The index of the prefab in `prefabs`
private void DrawSinglePrefabPlacementSettings(SerializedProperty prefab, int index)
{
SerializedProperty go = prefab.FindPropertyRelative("gameObject");
SerializedProperty settings = prefab.FindPropertyRelative("settings");
SerializedProperty name = prefab.FindPropertyRelative("name");
SerializedProperty uniformBool = settings.FindPropertyRelative("m_UniformBool");
SerializedProperty strength = settings.FindPropertyRelative("m_Strength");
SerializedProperty minRot = settings.FindPropertyRelative("m_RotationRangeMin");
SerializedProperty maxRot = settings.FindPropertyRelative("m_RotationRangeMax");
SerializedProperty minScale = settings.FindPropertyRelative("m_ScaleRangeMin");
SerializedProperty maxScale = settings.FindPropertyRelative("m_ScaleRangeMax");
SerializedProperty xScaleBool = settings.FindPropertyRelative("m_XScaleBool");
SerializedProperty yScaleBool = settings.FindPropertyRelative("m_YScaleBool");
SerializedProperty zScaleBool = settings.FindPropertyRelative("m_ZScaleBool");
SerializedProperty xRotationBool = settings.FindPropertyRelative("m_XRotationBool");
SerializedProperty yRotationBool = settings.FindPropertyRelative("m_YRotationBool");
SerializedProperty zRotationBool = settings.FindPropertyRelative("m_ZRotationBool");
const int pad = 4;
var settingsBackgroundStyle = new GUIStyle();
settingsBackgroundStyle.normal.background = EditorGUIUtility.whiteTexture;
settingsBackgroundStyle.padding = new RectOffset(0, 0, 0, 0);
PolyGUI.PushBackgroundColor(EditorGUIUtility.isProSkin ? PolyGUI.k_BoxBackgroundDark : PolyGUI.k_BoxBackgroundLight);
EditorGUILayout.BeginVertical(settingsBackgroundStyle);
GUILayout.Space(pad);
PolyGUI.PopBackgroundColor();
using (new GUILayout.HorizontalScope())
{
GUILayout.Space(pad);
/// Name field And Strenght Slider
using (new GUILayout.VerticalScope())
{
GUILayout.Space(EditorGUIUtility.standardVerticalSpacing);
GUI.SetNextControlName("cancelbackspace1" + index);
name.stringValue = GUILayout.TextField(name.stringValue, GUILayout.ExpandWidth(true));
GUILayout.Space(EditorGUIUtility.standardVerticalSpacing);
using (new GUILayout.HorizontalScope())
{
GUI.SetNextControlName("cancelbackspace2" + index);
strength.floatValue = EditorGUILayout.Slider("Frequency (%)", strength.floatValue,
BrushModePrefab.k_PrefabOccurrenceMin, BrushModePrefab.k_PrefabOccurrenceMax, GUILayout.ExpandWidth(true));
}
}
GUILayout.Space(pad);
}
GUILayout.BeginVertical();
float floatfieldWidth = EditorGUIUtility.currentViewWidth / 10;
GUILayoutOption[] floatFieldsOptions = new GUILayoutOption[] { GUILayout.Width(floatfieldWidth), GUILayout.MinWidth(40) };
GUILayoutOption widthConstraint = GUILayout.Width(15);
int slidersMargin = 15;
EditorGUILayout.LabelField("Randomize Scale");
EditorGUILayout.BeginHorizontal();
GUILayout.Space(slidersMargin);
EditorGUILayout.LabelField("Uniform Scale", GUILayout.Width(90));
uniformBool.boolValue = EditorGUILayout.Toggle(uniformBool.boolValue, widthConstraint);
GUILayout.FlexibleSpace();
if (uniformBool.boolValue)
{
SerializedProperty uScale = settings.FindPropertyRelative("m_UniformScale");
Vector2 uniformScale = uScale.vector2Value;
GUILayout.Space(slidersMargin);
GUI.SetNextControlName("cancelbackspace3" + index);
uniformScale.x = EditorGUILayout.FloatField(uniformScale.x, floatFieldsOptions);
EditorGUILayout.MinMaxSlider(ref uniformScale.x, ref uniformScale.y, 0.0f, 10f);
GUI.SetNextControlName("cancelbackspace4" + index);
uniformScale.y = EditorGUILayout.FloatField(uniformScale.y, floatFieldsOptions);
GUILayout.FlexibleSpace();
uScale.vector2Value = uniformScale;
EditorGUILayout.EndHorizontal();
}
else
{
EditorGUILayout.EndHorizontal();
//Random Scale
Vector3 scaleRangeMin = minScale.vector3Value;
Vector3 scaleRangeMax = maxScale.vector3Value;
using (new GUILayout.HorizontalScope())
{
GUILayout.Space(slidersMargin);
xScaleBool.boolValue = EditorGUILayout.Toggle(xScaleBool.boolValue, widthConstraint);
EditorGUILayout.LabelField("X", widthConstraint);
GUI.SetNextControlName("cancelbackspace5" + index);
scaleRangeMin.x = EditorGUILayout.FloatField(scaleRangeMin.x, floatFieldsOptions);
EditorGUILayout.MinMaxSlider(ref scaleRangeMin.x, ref scaleRangeMax.x, 0.0f, 10f);
GUI.SetNextControlName("cancelbackspace6" + index);
scaleRangeMax.x = EditorGUILayout.FloatField(scaleRangeMax.x, floatFieldsOptions);
}
using (new GUILayout.HorizontalScope())
{
GUILayout.Space(slidersMargin);
yScaleBool.boolValue = EditorGUILayout.Toggle(yScaleBool.boolValue, widthConstraint);
EditorGUILayout.LabelField("Y", widthConstraint);
GUI.SetNextControlName("cancelbackspace7" + index);
scaleRangeMin.y = EditorGUILayout.FloatField(scaleRangeMin.y, floatFieldsOptions);
EditorGUILayout.MinMaxSlider(ref scaleRangeMin.y, ref scaleRangeMax.y, 0.0f, 10f);
GUI.SetNextControlName("cancelbackspace8" + index);
scaleRangeMax.y = EditorGUILayout.FloatField(scaleRangeMax.y, floatFieldsOptions);
}
using (new GUILayout.HorizontalScope())
{
GUILayout.Space(slidersMargin);
zScaleBool.boolValue = EditorGUILayout.Toggle(zScaleBool.boolValue, widthConstraint);
EditorGUILayout.LabelField("Z", widthConstraint);
GUI.SetNextControlName("cancelbackspace9" + index);
scaleRangeMin.z = EditorGUILayout.FloatField(scaleRangeMin.z, floatFieldsOptions);
EditorGUILayout.MinMaxSlider(ref scaleRangeMin.z, ref scaleRangeMax.z, 0.0f, 10f);
GUI.SetNextControlName("cancelbackspace10" + index);
scaleRangeMax.z = EditorGUILayout.FloatField(scaleRangeMax.z, floatFieldsOptions);
}
minScale.vector3Value = scaleRangeMin;
maxScale.vector3Value = scaleRangeMax;
}
// Random Rotation
Vector3 rotationRangeMin = minRot.vector3Value;
Vector3 rotationRangeMax = maxRot.vector3Value;
EditorGUILayout.LabelField("Randomize Rotation");
using (new GUILayout.HorizontalScope())
{
GUILayout.Space(slidersMargin);
xRotationBool.boolValue = EditorGUILayout.Toggle(xRotationBool.boolValue, widthConstraint);
EditorGUILayout.LabelField("X", widthConstraint);
GUI.SetNextControlName("cancelbackspace11" + index);
rotationRangeMin.x = EditorGUILayout.FloatField(rotationRangeMin.x, floatFieldsOptions);
EditorGUILayout.MinMaxSlider(ref rotationRangeMin.x, ref rotationRangeMax.x, 0.0f, 360f);
GUI.SetNextControlName("cancelbackspace12" + index);
rotationRangeMax.x = EditorGUILayout.FloatField(rotationRangeMax.x, floatFieldsOptions);
}
using (new GUILayout.HorizontalScope())
{
GUILayout.Space(slidersMargin);
yRotationBool.boolValue = EditorGUILayout.Toggle(yRotationBool.boolValue, widthConstraint);
EditorGUILayout.LabelField("Y", widthConstraint);
GUI.SetNextControlName("cancelbackspace13" + index);
rotationRangeMin.y = EditorGUILayout.FloatField(rotationRangeMin.y, floatFieldsOptions);
EditorGUILayout.MinMaxSlider(ref rotationRangeMin.y, ref rotationRangeMax.y, 0.0f, 360f);
GUI.SetNextControlName("cancelbackspace14" + index);
rotationRangeMax.y = EditorGUILayout.FloatField(rotationRangeMax.y, floatFieldsOptions);
}
using (new GUILayout.HorizontalScope())
{
GUILayout.Space(slidersMargin);
zRotationBool.boolValue = EditorGUILayout.Toggle(zRotationBool.boolValue, widthConstraint);
EditorGUILayout.LabelField("Z", widthConstraint);
GUI.SetNextControlName("cancelbackspace15" + index);
rotationRangeMin.z = EditorGUILayout.FloatField(rotationRangeMin.z, floatFieldsOptions);
EditorGUILayout.MinMaxSlider(ref rotationRangeMin.z, ref rotationRangeMax.z, 0.0f, 360f);
GUI.SetNextControlName("cancelbackspace16" + index);
rotationRangeMax.z = EditorGUILayout.FloatField(rotationRangeMax.z, floatFieldsOptions);
}
minRot.vector3Value = rotationRangeMin;
maxRot.vector3Value = rotationRangeMax;
GUILayout.EndVertical();
GUILayout.Space(pad);
GUILayout.EndVertical();
GUILayout.Space(pad);
}
}
}