using UnityEngine;
using UnityEngine.Rendering;
using System;
using System.Collections.Generic;
using UnityEngine.UIElements;
using System.Linq;
using System.IO;
using System.Linq.Expressions;
using System.Reflection;
namespace UnityEditor.Rendering.LookDev
{
///
/// Class containing a collection of Environment
///
public class EnvironmentLibrary : BaseEnvironmentLibrary
{
[field: SerializeField]
List environments { get; set; } = new List();
///
/// Number of elements in the collection
///
public int Count => environments.Count;
///
/// Indexer giving access to contained Environment
///
public Environment this[int index] => environments[index];
///
/// Create a new empty Environment at the end of the collection
///
/// The created Environment
public Environment Add()
{
Environment environment = ScriptableObject.CreateInstance();
environment.name = "New Environment";
Undo.RegisterCreatedObjectUndo(environment, "Add Environment");
environments.Add(environment);
// Store this new environment as a subasset so we can reference it safely afterwards.
AssetDatabase.AddObjectToAsset(environment, this);
// Force save / refresh. Important to do this last because SaveAssets can cause effect to become null!
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
return environment;
}
///
/// Remove Environment of the collection at given index
///
/// Index where to remove Environment
public void Remove(int index)
{
Environment environment = environments[index];
Undo.RecordObject(this, "Remove Environment");
environments.RemoveAt(index);
Undo.DestroyObjectImmediate(environment);
// Force save / refresh
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
}
///
/// Duplicate the Environment at given index and add it at the end of the Collection
///
/// Index where to take data for duplication
/// The created Environment
public Environment Duplicate(int fromIndex)
{
Environment environment = ScriptableObject.CreateInstance();
Environment environmentToCopy = environments[fromIndex];
environmentToCopy.CopyTo(environment);
Undo.RegisterCreatedObjectUndo(environment, "Duplicate Environment");
environments.Add(environment);
// Store this new environment as a subasset so we can reference it safely afterwards.
AssetDatabase.AddObjectToAsset(environment, this);
// Force save / refresh. Important to do this last because SaveAssets can cause effect to become null!
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
return environment;
}
///
/// Compute position of given Environment in the collection
///
/// Environment to look at
/// Index of the searched environment. If not found, -1.
public int IndexOf(Environment environment)
=> environments.IndexOf(environment);
}
[CustomEditor(typeof(EnvironmentLibrary))]
class EnvironmentLibraryEditor : Editor
{
VisualElement root;
public sealed override VisualElement CreateInspectorGUI()
{
var library = target as EnvironmentLibrary;
root = new VisualElement();
Button open = new Button(() =>
{
if (!LookDev.open)
LookDev.Open();
LookDev.currentContext.UpdateEnvironmentLibrary(library);
LookDev.currentEnvironmentDisplayer.Repaint();
})
{
text = "Open in LookDev window"
};
root.Add(open);
return root;
}
// Don't use ImGUI
public sealed override void OnInspectorGUI() { }
}
class EnvironmentLibraryCreator : ProjectWindowCallback.EndNameEditAction
{
public override void Action(int instanceId, string pathName, string resourceFile)
{
var newAsset = CreateInstance();
newAsset.name = Path.GetFileName(pathName);
AssetDatabase.CreateAsset(newAsset, pathName);
ProjectWindowUtil.ShowCreatedAsset(newAsset);
}
[MenuItem("Assets/Create/LookDev/Environment Library", priority = 2000)]
public static void Create()
{
var icon = EditorGUIUtility.FindTexture("ScriptableObject Icon");
ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, ScriptableObject.CreateInstance(), "EnvironmentLibrary.asset", icon, null);
}
}
static class EnvironmentLibraryLoader
{
public static void Load(Action onInspectorRedrawRequested)
{
UnityEngine.Object target = LookDev.currentContext.environmentLibrary;
UIElementObjectSelectorWorkaround.Show(target, typeof(EnvironmentLibrary), LoadCallback(onInspectorRedrawRequested));
}
static Action LoadCallback(Action onUpdate)
{
return (UnityEngine.Object newLibrary) =>
{
LookDev.currentContext.UpdateEnvironmentLibrary(newLibrary as EnvironmentLibrary);
onUpdate?.Invoke();
};
}
// As in UIElement.ObjectField we cannot support cancel when closing window
static class UIElementObjectSelectorWorkaround
{
static Action> ShowObjectSelector;
static UIElementObjectSelectorWorkaround()
{
Type playerSettingsType = typeof(PlayerSettings);
Type objectSelectorType = playerSettingsType.Assembly.GetType("UnityEditor.ObjectSelector");
var instanceObjectSelectorInfo = objectSelectorType.GetProperty("get", BindingFlags.Static | BindingFlags.Public);
var showInfo = objectSelectorType.GetMethod("Show", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(UnityEngine.Object), typeof(Type), typeof(SerializedProperty), typeof(bool), typeof(List), typeof(Action), typeof(Action) }, null);
var objectSelectorVariable = Expression.Variable(objectSelectorType, "objectSelector");
var objectParameter = Expression.Parameter(typeof(UnityEngine.Object), "unityObject");
var typeParameter = Expression.Parameter(typeof(Type), "type");
var onChangedObjectParameter = Expression.Parameter(typeof(Action), "onChangedObject");
var showObjectSelectorBlock = Expression.Block(
new[] { objectSelectorVariable },
Expression.Assign(objectSelectorVariable, Expression.Call(null, instanceObjectSelectorInfo.GetGetMethod())),
Expression.Call(objectSelectorVariable, showInfo, objectParameter, typeParameter, Expression.Constant(null, typeof(SerializedProperty)), Expression.Constant(false), Expression.Constant(null, typeof(List)), Expression.Constant(null, typeof(Action)), onChangedObjectParameter)
);
var showObjectSelectorLambda = Expression.Lambda>>(showObjectSelectorBlock, objectParameter, typeParameter, onChangedObjectParameter);
ShowObjectSelector = showObjectSelectorLambda.Compile();
}
public static void Show(UnityEngine.Object obj, Type type, Action onObjectChanged)
{
ShowObjectSelector(obj, type, onObjectChanged);
}
}
}
}