using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.SettingsManagement;
using UnityEngine;
namespace UnityEditor.Polybrush
{
///
/// An intermediary between the BrushModePrefab and multiple PrefabPaletteEditors
/// to be able to keep a list of loadouts for painting
///
internal class PrefabLoadoutEditor
{
///
/// Storage for user loadouts.
///
[UserSetting]
static Pref s_UserLoadout = new Pref("ScatteringEditor.userLoadout", new PrefabLoadout(new List()), SettingsScope.Project);
static class Styles
{
public static readonly GUIContent brushLoadoutLabel = new GUIContent("Brush Loadout", "Currently loaded prefabs for painting");
public static readonly GUIContent copyPrefabSettingsLabel = new GUIContent("Copy prefab settings", "");
public static readonly GUIContent pastePrefabSettingsLabel = new GUIContent("Paste prefab settings", "");
public static GUIStyle deleteButtonStyle = null;
public static bool initialized = false;
public static void Initialize()
{
deleteButtonStyle = new GUIStyle(GUI.skin.button);
deleteButtonStyle.normal.background = IconUtility.GetIcon("PaintPrefabs/Delete");
deleteButtonStyle.padding = new RectOffset(0, 0, 0, 0);
deleteButtonStyle.fixedHeight = deleteButtonStyle.normal.background.width;
deleteButtonStyle.fixedWidth = deleteButtonStyle.normal.background.width;
deleteButtonStyle.hover.background = deleteButtonStyle.active.background = null;
initialized = true;
}
}
internal Dictionary prefabPaletteEditors;
internal PrefabPalette currentPalette;
internal LoadoutInfo copyPastePrefabSettings = null;
internal LoadoutInfo toUnload;
List m_CurrentLoadouts;
internal List CurrentLoadout
{
get { return m_CurrentLoadouts; }
}
public PrefabLoadoutEditor(List palettes, PrefabPalette startingPalette)
{
currentPalette = startingPalette;
prefabPaletteEditors = new Dictionary();
m_CurrentLoadouts = s_UserLoadout.value.infos;
RefreshPalettesList(palettes);
}
void SaveUserCurrentLoadouts()
{
s_UserLoadout.value = new PrefabLoadout(m_CurrentLoadouts);
PolybrushSettings.Save();
}
internal void OnInspectorGUI_Internal(int thumbSize)
{
if (!Styles.initialized)
Styles.Initialize();
DrawLoadoutList(thumbSize);
if (prefabPaletteEditors[currentPalette] != null)
prefabPaletteEditors[currentPalette].OnInspectorGUI_Internal(thumbSize);
}
internal void RefreshPalettesList(List palettes)
{
prefabPaletteEditors.Clear();
foreach (PrefabPalette p in palettes)
{
var editor = (PrefabPaletteEditor)Editor.CreateEditor(p);
editor.loadoutEditor = this;
prefabPaletteEditors.Add(p, editor);
}
SyncLoadoutWithPalettes();
}
///
/// Show the list of current loadouts
///
/// Size of the preview texture
internal void DrawLoadoutList(int thumbSize)
{
SyncLoadoutWithPalettes();
int count = m_CurrentLoadouts.Count;
PolyGUILayout.Label(Styles.brushLoadoutLabel);
Rect backGroundRect = EditorGUILayout.BeginVertical(PrefabPaletteEditor.paletteStyle);
backGroundRect.width = EditorGUIUtility.currentViewWidth;
if (count == 0)
{
var r = EditorGUILayout.BeginVertical(GUILayout.Height(thumbSize+4));
EditorGUI.DrawRect(r, EditorGUIUtility.isProSkin ? PolyGUI.k_BoxBackgroundDark : PolyGUI.k_BoxBackgroundLight);
GUILayout.FlexibleSpace();
GUILayout.Label("Select items from the Palette below", EditorStyles.centeredGreyMiniLabel);
GUILayout.FlexibleSpace();
EditorGUILayout.EndVertical();
EditorGUILayout.EndVertical();
return;
}
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);
if (columns == 0) columns = 1;
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++)
{
LoadoutInfo loadoutInfo = m_CurrentLoadouts[currentIndex];
PrefabPaletteEditor prefabEditor = prefabPaletteEditors[loadoutInfo.palette];
SerializedProperty prefabs = prefabEditor.prefabs;
SerializedProperty prefab = prefabs.GetArrayElementAtIndex(loadoutInfo.palette.FindIndex(loadoutInfo.prefab));
var previewRectXPos = pad + j * size + horizontalRect.x;
DrawSingleLoadout(prefab, thumbSize, loadoutInfo, previewRectXPos, horizontalRect.y);
if (j != columns - 1)
GUILayout.Space(pad);
currentIndex++;
if (currentIndex >= count)
break;
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(4);
}
EditorGUILayout.EndVertical();
if (toUnload != null)
{
RemovePrefabFromLoadout(toUnload);
toUnload = null;
SaveUserCurrentLoadouts();
}
}
///
/// Draw a single loadout
///
/// The loadout being drawn
/// Size of the preview
/// additionnal infos about the loadout being drawn (parent prefab palette and index in it)
void DrawSingleLoadout(SerializedProperty loadout, int thumbSize, LoadoutInfo infos, float x, float y)
{
var editor = prefabPaletteEditors[infos.palette];
editor.serializedObject.Update();
Rect r = new Rect(x, y, thumbSize, thumbSize);
// Texture Preview
Texture2D preview = PreviewsDatabase.GetAssetPreview(loadout.FindPropertyRelative("gameObject").objectReferenceValue);
EditorGUI.DrawPreviewTexture(r, preview);
float pad = thumbSize * 0.05f;
GUILayoutUtility.GetRect(thumbSize, thumbSize);
Rect r3 = new Rect(r);
r3.width = Styles.deleteButtonStyle.fixedWidth;
r3.height = Styles.deleteButtonStyle.fixedHeight;
r3.x += thumbSize - pad - r3.width;
r3.y += pad;
if (GUI.Button(r3, "", Styles.deleteButtonStyle))
{
toUnload = infos;
GUI.changed = true;
}
r.y += thumbSize - pad - 10;
r.x += pad;
r.width = thumbSize - (2 * pad);
var prefabOccurence = loadout.FindPropertyRelative("settings").FindPropertyRelative("m_Strength");
prefabOccurence.floatValue = GUI.HorizontalSlider(r, prefabOccurence.floatValue, BrushModePrefab.k_PrefabOccurrenceMin, BrushModePrefab.k_PrefabOccurrenceMax);
editor.serializedObject.ApplyModifiedProperties();
}
///
/// Switch to a new Palette Edition, which means switching paletteEditor too
///
/// the target palette
internal void ChangePalette(PrefabPalette palette)
{
if (!prefabPaletteEditors.ContainsKey(palette))
{
var editor = (PrefabPaletteEditor)Editor.CreateEditor(palette);
editor.loadoutEditor = this;
prefabPaletteEditors.Add(palette, editor);
}
currentPalette = palette;
}
///
/// Returns a random PrefabAndSettings from the loadout list
///
///
internal PrefabAndSettings GetRandomLoadout()
{
if (m_CurrentLoadouts.Count < 1)
return null;
// Weighted random implementation
List weights = new List() { 0.0f };
float totalWeights = 0.0f;
foreach (LoadoutInfo info in m_CurrentLoadouts)
{
float strength = info.palette.Get(info.prefab).settings.strength;
weights.Add(totalWeights + strength);
totalWeights += strength;
}
float random = UnityEngine.Random.Range(0.0f + Mathf.Epsilon, totalWeights);
int resultIndex = -1;
for(int i = 0; i < weights.Count - 1; i++)
{
if(weights[i] < random && random < weights[i + 1])
{
resultIndex = i;
break;
}
}
if (resultIndex == -1)
return null;
LoadoutInfo loadout = m_CurrentLoadouts[resultIndex];
return loadout.palette.Get(loadout.prefab);
}
internal bool ContainsPrefabInstance(GameObject instance)
{
GameObject prefab = PrefabUtility.GetCorrespondingObjectFromSource(instance);
if (prefab == null)
return false;
return ContainsPrefab(prefab);
}
internal bool ContainsPrefab(GameObject prefab)
{
foreach (var loadoutInfo in m_CurrentLoadouts)
if (loadoutInfo.palette.Contains(prefab))
return true;
return false;
}
///
/// Show the Menu to copu/paste prefab placement settings
///
/// The PrefabAndSettings which was right clicked
/// The list of selected PlacementSettings in the current PrefabPalette
internal void OpenCopyPasteMenu(LoadoutInfo info, HashSet selected)
{
GenericMenu menu = new GenericMenu();
if(selected.Count > 1)
menu.AddDisabledItem(Styles.copyPrefabSettingsLabel);
else
menu.AddItem(Styles.copyPrefabSettingsLabel, false, () => { CopyPasteSettings(info, true, selected); });
menu.AddItem(Styles.pastePrefabSettingsLabel, false, () => { CopyPasteSettings(info, false, selected); });
menu.ShowAsContext();
}
///
/// Copy or paste settings to selected PlacementSettings
///
/// The loadout that got clicked
/// do we copy or paste ?
/// the list of currently selected PlacementSettings for edition in the current PrefabPalette
private void CopyPasteSettings(LoadoutInfo loadout, bool copy, HashSet selected)
{
if (copy)
{
copyPastePrefabSettings = loadout;
}else
{
if (copyPastePrefabSettings == null)
return;
PrefabPaletteEditor sourceEditor = prefabPaletteEditors[copyPastePrefabSettings.palette];
PrefabPaletteEditor destEditor = prefabPaletteEditors[loadout.palette];
SerializedProperty srcPAS = sourceEditor.prefabs.GetArrayElementAtIndex(copyPastePrefabSettings.palette.FindIndex(copyPastePrefabSettings.prefab));
SerializedProperty srcPS = srcPAS.FindPropertyRelative("settings");
destEditor.serializedObject.Update();
List destPSs = new List();
foreach (int i in selected)
{
SerializedProperty destPAS = destEditor.prefabs.GetArrayElementAtIndex(i);
destPSs.Add(destPAS.FindPropertyRelative("settings"));
}
PlacementSettings.CopySerializedProperty(srcPS, destPSs);
destEditor.serializedObject.ApplyModifiedProperties();
}
}
internal void AddPrefabInLoadout(LoadoutInfo loadoutInfo)
{
if (!m_CurrentLoadouts.Contains(loadoutInfo))
m_CurrentLoadouts.Add(loadoutInfo);
SaveUserCurrentLoadouts();
}
internal bool ContainsPrefab(LoadoutInfo loadoutInfo)
{
return m_CurrentLoadouts.Contains(loadoutInfo);
}
internal void RemovePrefabFromLoadout(LoadoutInfo loadoutInfo)
{
if (m_CurrentLoadouts.Contains(loadoutInfo))
m_CurrentLoadouts.Remove(loadoutInfo);
SaveUserCurrentLoadouts();
}
///
/// Remove null references if palettes have been modified.
///
private void SyncLoadoutWithPalettes()
{
if (m_CurrentLoadouts != null)
{
m_CurrentLoadouts.RemoveAll(x => x.palette == null || !x.IsValid());
SaveUserCurrentLoadouts();
}
}
}
}