Added player character and step in/out of dialogue in scene 101

This commit is contained in:
2026-08-15 15:22:47 +02:00
parent a42f25d5fa
commit 38ba2b0eff
2019 changed files with 181227 additions and 265 deletions
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 274744740648f42a19d3e77602101b06
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.ProBuilder.Editor")]
[assembly: InternalsVisibleTo("Unity.ProBuilder.Debug.Editor")]
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a84ca2d947f34a09bd44bc592e22d49c
timeCreated: 1516811154
@@ -0,0 +1,379 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using UObject = UnityEngine.Object;
namespace UnityEngine.ProBuilder.AssetIdRemapUtility
{
[Serializable]
sealed class AssetIdentifierTuple
{
public AssetId source;
public AssetId destination;
public AssetIdentifierTuple()
{
source = null;
destination = null;
}
public AssetIdentifierTuple(AssetId src, AssetId dest)
{
source = src ?? new AssetId();
destination = dest ?? new AssetId();
}
public bool AssetEquals(AssetIdentifierTuple other)
{
return AssetId.IsValid(source) == AssetId.IsValid(other.source) &&
source.AssetEquals(other.source) &&
AssetId.IsValid(destination) == AssetId.IsValid(other.destination) &&
destination.AssetEquals(other.destination);
}
}
[Serializable]
class StringTuple
{
public string key;
public string value;
public StringTuple(string k, string v)
{
key = k;
value = v;
}
}
enum Origin
{
Source,
Destination
}
[Serializable]
class AssetIdRemapObject
{
public List<string> sourceDirectory = new List<string>();
public string destinationDirectory = null;
public List<AssetIdentifierTuple> map = new List<AssetIdentifierTuple>();
public AssetIdentifierTuple this[int i]
{
get { return map[i]; }
set { map[i] = value; }
}
public void Clear(Origin origin)
{
switch (origin)
{
case Origin.Source:
sourceDirectory.Clear();
for (int i = 0, c = map.Count; i < c; i++)
map[i].source.Clear();
break;
case Origin.Destination:
destinationDirectory = "";
for (int i = 0, c = map.Count; i < c; i++)
map[i].destination.Clear();
break;
}
map = map.Where(x => AssetId.IsValid(x.source) || AssetId.IsValid(x.destination)).ToList();
}
public void Delete(IEnumerable<AssetIdentifierTuple> entries)
{
map.RemoveAll(entries.Contains);
}
public void Merge(IEnumerable<AssetIdentifierTuple> entries)
{
var types = entries.SelectMany(x => new[] { x.source.assetType, x.destination.assetType });
if (types.Where(x => !string.IsNullOrEmpty(x)).Distinct().Count() > 1)
{
Debug.LogError("Attempting to map entries of multiple types! This is not allowed.");
return;
}
var arr = entries as AssetIdentifierTuple[] ?? entries.ToArray();
var src = arr.Where(x => AssetId.IsValid(x.source) && !AssetId.IsValid(x.destination)).ToArray();
var dst = arr.Where(x => AssetId.IsValid(x.destination)).ToArray();
if (dst.Length != 1)
{
Debug.LogError("Merging AssetId entries requires only one valid destination entry be selected.");
return;
}
var d = dst.First().destination;
foreach (var s in src)
map.Add(new AssetIdentifierTuple(new AssetId(s.source), new AssetId(d)));
map.RemoveAll(src.Contains);
map.RemoveAll(x => dst.Contains(x) && !AssetId.IsValid(x.source));
}
}
[Serializable]
class AssetId : IEquatable<AssetId>
{
const string k_MonoScriptTypeString = "UnityEditor.MonoScript";
static readonly string[] k_MonoScriptTypeSplit = new string[1] {"::"};
enum AssetType
{
Unknown = 0,
Default = 1,
MonoScript = 2,
// add more as special cases require
}
/// <summary>
/// A path relative to the root asset directory (ex, ProBuilder/About/Hello.cs).
/// Stored per-asset because the path may change between upgrades. A single file name is stored at the tuple
/// level.
/// </summary>
public string localPath
{
get { return m_LocalPath; }
}
public string name
{
get { return m_Name; }
}
/// <summary>
/// Return the backing type of this asset. If the asset is a MonoScript, the associated mono class will be
/// returned. To get the Unity asset type use assetType.
/// </summary>
public string type
{
get { return IsMonoScript() ? m_MonoScriptClass : m_Type; }
}
public string assetType
{
get { return IsMonoScript() ? k_MonoScriptTypeString : m_Type; }
}
/// <summary>
/// File Ids associated with this asset.
/// </summary>
public string fileId
{
get { return m_FileId; }
}
/// <summary>
/// Asset GUID.
/// </summary>
public string guid
{
get { return m_Guid; }
}
[SerializeField]
string m_Guid;
[SerializeField]
string m_FileId;
[SerializeField]
string m_LocalPath;
[SerializeField]
string m_Name;
[SerializeField]
string m_Type;
// the remaining properties are only relevant to monoscript files
AssetType m_InternalType = AssetType.Unknown;
string m_MonoScriptClass = null;
#pragma warning disable 414
bool m_IsEditorScript = false;
#pragma warning restore 414
public AssetId()
{
Clear();
}
public AssetId(AssetId other)
{
m_Guid = other.m_Guid;
m_FileId = other.m_FileId;
m_LocalPath = other.m_LocalPath;
m_Name = other.m_Name;
m_Type = other.m_Type;
}
public AssetId(UObject obj, string file, string guid, string localPath = null)
{
if (obj == null)
throw new SystemException("Cannot initialize an AssetIdentifier with a null object");
if (string.IsNullOrEmpty(guid))
throw new SystemException("Cannot initialize an AssetIdentifier without a GUID");
if (string.IsNullOrEmpty(file))
throw new SystemException("Cannot initialize an AssetIdentifier without a FileId");
m_FileId = file;
m_Guid = guid;
m_Name = obj.name;
m_LocalPath = localPath;
m_Type = GetUObjectTypeString(obj);
}
static string GetUObjectTypeString(UObject obj)
{
MonoScript ms = obj as MonoScript;
if (ms != null)
return string.Format("{0}{1}{2}", obj.GetType().ToString(), k_MonoScriptTypeSplit[0], ms.GetClass());
return obj.GetType().ToString();
}
public bool Equals(AssetId other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(m_Guid, other.m_Guid) &&
string.Equals(m_FileId, other.m_FileId) &&
string.Equals(m_LocalPath, other.m_LocalPath);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
var id = obj as AssetId;
if (id != null) return Equals(id);
return false;
}
public override int GetHashCode()
{
int hash = 0;
unchecked
{
hash = (hash * 7) + (string.IsNullOrEmpty(m_Guid) ? 0 : m_Guid.GetHashCode());
hash = (hash * 7) + (string.IsNullOrEmpty(m_FileId) ? 0 : m_FileId.GetHashCode());
hash = (hash * 7) + (string.IsNullOrEmpty(m_LocalPath) ? 0 : m_LocalPath.GetHashCode());
}
return hash;
}
public void Clear()
{
m_Guid = "";
m_FileId = "";
m_LocalPath = "";
m_Name = "";
m_Type = "";
m_InternalType = AssetType.Unknown;
m_MonoScriptClass = null;
m_IsEditorScript = false;
}
public void SetPathRelativeTo(string dir)
{
m_LocalPath = m_LocalPath.Replace(dir, "");
}
public static bool IsValid(AssetId id)
{
return !string.IsNullOrEmpty(id == null ? null : id.m_Guid);
}
public bool IsMonoScript()
{
if (m_InternalType == AssetType.Unknown)
{
if (m_Type.StartsWith(k_MonoScriptTypeString))
{
m_InternalType = AssetType.MonoScript;
try
{
m_MonoScriptClass = m_Type.Split(k_MonoScriptTypeSplit, StringSplitOptions.RemoveEmptyEntries)[1];
if (m_LocalPath.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase))
m_IsEditorScript = m_LocalPath.StartsWith("Editor/") || m_LocalPath.Contains("/Editor/") || PackageImporter.IsEditorPluginEnabled(guid);
else
m_IsEditorScript = m_LocalPath.StartsWith("Editor/") || m_LocalPath.Contains("/Editor/");
}
catch
{
m_MonoScriptClass = "null";
// pb_Log.Debug("Failed parsing type from monoscript \"" + m_Name + "\" (" + m_Type + ")");
}
}
else
{
m_InternalType = AssetType.Default;
}
}
return m_InternalType == AssetType.MonoScript;
}
bool GetNamespaceAndType(string classType, out string namespaceString, out string typeString)
{
namespaceString = null;
typeString = null;
if (string.IsNullOrEmpty(classType))
return false;
int last = classType.LastIndexOf('.');
if (last < 0)
{
typeString = classType;
return true;
}
namespaceString = classType.Substring(0, last);
typeString = classType.Substring(last + 1, (classType.Length - last) - 1);
return true;
}
public bool AssetEquals(AssetId other)
{
if (!assetType.Equals(other.assetType))
return false;
return localPath.Equals(other.localPath);
}
/// <summary>
/// Does the object this id reference exist in the project?
/// </summary>
/// <returns></returns>
public bool ExistsInProject()
{
string assetPath = AssetDatabase.GUIDToAssetPath(m_Guid);
if (string.IsNullOrEmpty(assetPath))
return false;
var assetObj = AssetDatabase.LoadAssetAtPath<UObject>(assetPath);
if (assetObj == null)
return false;
return m_Type.Equals(GetUObjectTypeString(assetObj));
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4ce1c67454d65d14eb3eae2f44b0ffa7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,487 @@
#if PROBUILDER_DEBUG
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UObject = UnityEngine.Object;
using UnityEditor.IMGUI.Controls;
#if UNITY_6000_3_OR_NEWER
using BaseTreeViewState = UnityEditor.IMGUI.Controls.TreeViewState<int>;
#else
using BaseTreeViewState = UnityEditor.IMGUI.Controls.TreeViewState;
#endif
namespace UnityEngine.ProBuilder.AssetIdRemapUtility
{
/// <summary>
/// Utility class for creating GUID remap files.
/// </summary>
sealed class AssetIdRemapBuilderEditor : EditorWindow
{
const string k_RemapFilePath = "AssetIdRemap.json";
const string k_NamespaceRemapFilePath = "NamespaceRemap.json";
static string remapFilePath
{
get { return "Assets/" + k_RemapFilePath; }
}
static string namespaceRemapFilePath
{
get { return "Assets/" + k_NamespaceRemapFilePath; }
}
static readonly string[] k_DirectoryExcludeFilter = new string[]
{
"ProBuilder/About",
"ProBuilder/AssetIdRemapUtility",
"ProBuilder/API Examples",
"ProBuilder/Data",
"ProBuilder/Icons",
"ProBuilder/Upgrade",
};
static GUIContent m_SourceGuiContent = new GUIContent("Source", "The old GUID and FileId.");
static GUIContent m_DestinationGuiContent = new GUIContent("Destination", "The new GUID and FileId.");
[SerializeField] TextAsset m_RemapTextAsset = null;
[SerializeField] TextAsset m_NamespaceRemapTextAsset = null;
[SerializeField] string m_SourceDirectory;
[SerializeField] string m_DestinationDirectory;
[SerializeField] BaseTreeViewState m_TreeViewState;
[SerializeField] MultiColumnHeaderState m_MultiColumnHeaderState;
[SerializeField] bool m_DetailsExpanded;
MultiColumnHeader m_MultiColumnHeader;
AssetIdRemapBuilderTreeView m_TreeView;
SearchField m_SearchField;
[MenuItem("Tools/GUID Remap Editor")]
static void MenuOpenGuidEditor()
{
var win = GetWindow<AssetIdRemapBuilderEditor>(true, "GUID Remap Editor", true);
win.m_DetailsExpanded = true;
}
static class Styles
{
static GUIStyle m_Container = null;
public static GUIStyle container
{
get
{
if (m_Container == null)
{
m_Container = new GUIStyle(EditorStyles.helpBox);
m_Container.padding = new RectOffset(4, 4, 4, 4);
}
return m_Container;
}
}
}
void OnEnable()
{
// Check whether there is already a serialized view state (state
// that survived assembly reloading)
if (m_TreeViewState == null)
m_TreeViewState = new BaseTreeViewState();
if (m_MultiColumnHeaderState == null)
m_MultiColumnHeaderState = new MultiColumnHeaderState(new MultiColumnHeaderState.Column[]
{
new MultiColumnHeaderState.Column() {
headerContent = m_SourceGuiContent,
autoResize = true
},
new MultiColumnHeaderState.Column() {
headerContent = m_DestinationGuiContent,
autoResize = true
},
});
m_MultiColumnHeader = new MultiColumnHeader(m_MultiColumnHeaderState);
m_MultiColumnHeader.ResizeToFit();
m_TreeView = new AssetIdRemapBuilderTreeView(m_TreeViewState, m_MultiColumnHeader);
m_TreeView.remapObject = GetGuidRemapObject();
m_TreeView.Reload();
m_SearchField = new SearchField();
}
void OnDestroy()
{
if (m_TreeView.isDirty)
{
if (EditorUtility.DisplayDialog("Unsaved Changes", "There are unsaved changes to the remap file. Save these changes?", "Save", "Discard"))
Save();
}
}
string GetRemapFilePath()
{
if (m_RemapTextAsset != null)
return AssetDatabase.GetAssetPath(m_RemapTextAsset);
return remapFilePath;
}
void Save()
{
File.WriteAllText(GetRemapFilePath(), JsonUtility.ToJson(m_TreeView.remapObject, true));
AssetDatabase.ImportAsset(GetRemapFilePath());
EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath<TextAsset>(GetRemapFilePath()));
m_TreeView.isDirty = false;
}
void OnGUI()
{
GUILayout.BeginHorizontal(EditorStyles.toolbar);
if (GUILayout.Button(m_DetailsExpanded ? "Hide" : "Show", EditorStyles.toolbarButton))
m_DetailsExpanded = !m_DetailsExpanded;
GUILayout.FlexibleSpace();
GUI.enabled = m_TreeView.isDirty;
if (GUILayout.Button("Revert", EditorStyles.toolbarButton))
{
m_TreeView.remapObject = null;
m_TreeView.remapObject = GetGuidRemapObject();
m_TreeView.Reload();
m_TreeView.isDirty = false;
}
if (GUILayout.Button("Save", EditorStyles.toolbarButton))
Save();
GUI.enabled = true;
GUILayout.EndHorizontal();
if (m_DetailsExpanded)
{
EditorGUI.BeginChangeCheck();
m_RemapTextAsset = (TextAsset)EditorGUILayout.ObjectField("Remap", m_RemapTextAsset, typeof(TextAsset), false);
m_NamespaceRemapTextAsset = (TextAsset)EditorGUILayout.ObjectField("Namespace", m_NamespaceRemapTextAsset, typeof(TextAsset), false);
if (EditorGUI.EndChangeCheck())
{
m_TreeView.remapObject = null;
m_TreeView.remapObject = GetGuidRemapObject();
m_TreeView.Reload();
Repaint();
}
EditorGUILayout.BeginVertical(Styles.container);
GUILayout.Label("Package Directories", EditorStyles.boldLabel);
EditorGUILayout.BeginVertical(Styles.container);
m_SourceDirectory = DoDirectoryField("Source", m_SourceDirectory);
EditorGUI.BeginChangeCheck();
if (GUILayout.Button("Collect Source (Old) Asset Identifiers"))
GetRemapSource(m_SourceDirectory);
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical(Styles.container);
m_DestinationDirectory = DoDirectoryField("Destination", m_DestinationDirectory);
if (GUILayout.Button("Collect Destination (New) Asset Identifiers"))
GetRemapDestination(m_DestinationDirectory);
if (EditorGUI.EndChangeCheck())
m_TreeView.Reload();
EditorGUILayout.EndVertical();
EditorGUILayout.EndVertical();
}
GUILayout.BeginHorizontal();
GUILayout.Label("Asset Id Mapping", EditorStyles.boldLabel);
GUILayout.FlexibleSpace();
if (GUILayout.Button("..."))
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent("Clear Source", ""), false, () =>
{
GetGuidRemapObject().Clear(Origin.Source);
m_TreeView.Reload();
m_TreeView.isDirty = true;
});
menu.AddItem(new GUIContent("Clear Destination", ""), false, () =>
{
GetGuidRemapObject().Clear(Origin.Destination);
m_TreeView.Reload();
m_TreeView.isDirty = true;
});
menu.ShowAsContext();
}
GUILayout.EndHorizontal();
Rect last = GUILayoutUtility.GetLastRect();
m_TreeView.searchString = m_SearchField.OnGUI(new Rect(last.x, last.y + last.height + 4, position.width - last.x * 2f, 20f),
m_TreeView.searchString);
Vector2 treeStart = new Vector2(last.x, last.y + last.height + 4 + 20f + 4f);
m_TreeView.SetRowHeight();
m_TreeView.OnGUI(new Rect(treeStart.x, treeStart.y, position.width - treeStart.x * 2, position.height - treeStart.y));
}
static string DoDirectoryField(string title, string value)
{
GUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
value = EditorGUILayout.TextField(title, value);
if (GUILayout.Button("Select", GUILayout.MaxWidth(60)))
value = GetSelectedDirectory();
bool didChange = EditorGUI.EndChangeCheck();
bool doOpenFolderPanel = GUILayout.Button("...", GUILayout.MaxWidth(32));
GUILayout.EndHorizontal();
if (doOpenFolderPanel)
{
value = EditorUtility.OpenFolderPanel(title, value, "");
didChange = true;
}
return didChange ? value = value.Replace("\\", "/").Replace(Application.dataPath, "Assets") : value;
}
void GetRemapSource(string directory)
{
if (string.IsNullOrEmpty(directory) || !Directory.Exists(directory))
{
Debug.LogWarning("No source directory selected.");
return;
}
var remapObject = GetGuidRemapObject();
string localDirectory = directory.Replace("\\", "/").Replace(Application.dataPath, "Assets") + "/";
if (!remapObject.sourceDirectory.Contains(localDirectory))
remapObject.sourceDirectory.Add(localDirectory);
List<AssetIdentifierTuple> map = remapObject.map;
foreach (var id in GetAssetIdentifiersInDirectory(localDirectory, k_DirectoryExcludeFilter))
{
id.SetPathRelativeTo(localDirectory);
if (map.Any(x => x.source != null && x.source.Equals(id)))
continue;
// the only time where a destination can exist with a null source is when a single destination is in the
// map, so it's okay to grab the first and not bother searching for more dangling destination entries
AssetIdentifierTuple matchingDestination =
map.FirstOrDefault(x =>
{
return x.destination != null &&
x.destination.AssetEquals(id);
});
if (matchingDestination != null)
{
if (AssetId.IsValid(matchingDestination.source))
map.Add(new AssetIdentifierTuple(id, matchingDestination.destination));
else
matchingDestination.source = id;
}
else
{
map.Add(new AssetIdentifierTuple(id, null));
}
}
m_TreeView.isDirty = true;
}
void GetRemapDestination(string directory)
{
if (string.IsNullOrEmpty(directory) || !Directory.Exists(directory))
{
Debug.LogWarning("No destination directory selected.");
return;
}
var remapObject = GetGuidRemapObject();
if (!string.IsNullOrEmpty(remapObject.destinationDirectory))
{
if (!EditorUtility.DisplayDialog("Destination Directory Already Mapped",
"The destination directory has already been mapped. Continuing will overwrite the existing data. Are you sure you wish to continue?",
"Continue", "Cancel"))
return;
}
string localDirectory = directory.Replace("\\", "/").Replace(Application.dataPath, "Assets") + "/";
remapObject.destinationDirectory = localDirectory;
List<AssetIdentifierTuple> map = remapObject.map;
foreach (var id in GetAssetIdentifiersInDirectory(localDirectory, k_DirectoryExcludeFilter))
{
if (map.Any(x => x.destination.Equals(id)))
continue;
id.SetPathRelativeTo(localDirectory);
IEnumerable<AssetIdentifierTuple> matchingSources =
map.Where(x => x.source != null && x.source.AssetEquals(id));
if (matchingSources.Any())
{
foreach (var tup in matchingSources)
tup.destination = id;
}
else
{
map.Add(new AssetIdentifierTuple(null, id));
}
}
m_TreeView.isDirty = true;
}
/// <summary>
/// Collect asset identifier information from all files in a directory.
/// </summary>
/// <param name="directory"></param>
/// <param name="directoryIgnoreFilter"></param>
static List<AssetId> GetAssetIdentifiersInDirectory(string directory, string[] directoryIgnoreFilter)
{
List<AssetId> ids = new List<AssetId>();
string unixPath = directory.Replace("\\", "/");
string packageAbsolutePath = null;
string packageRelativePath = null;
if (directory.StartsWith("Packages/"))
{
// +1 because we want the trailing "/" to be with the packageRelativePath
var en = unixPath.IndexOf("/", "Packages/".Length, StringComparison.InvariantCulture) + 1;
packageRelativePath = unixPath.Substring(0, en);
var targetPathRelative = unixPath.Substring(en, unixPath.Length - en);
packageAbsolutePath = Path.GetFullPath(unixPath).Replace("\\", "/");
if (!string.IsNullOrEmpty(targetPathRelative))
packageAbsolutePath = packageAbsolutePath.Replace(targetPathRelative, "");
}
if (directoryIgnoreFilter != null && directoryIgnoreFilter.Any(x => unixPath.Contains(x)))
return ids;
foreach (string file in Directory.GetFiles(Path.GetFullPath(directory), "*", SearchOption.TopDirectoryOnly))
{
if (file.EndsWith(".meta") || Path.GetFileName(file).StartsWith("."))
continue;
string localPath = file.Replace("\\", "/").Replace(Application.dataPath, "Assets");
if (!string.IsNullOrEmpty(packageAbsolutePath))
localPath = localPath.Replace(packageAbsolutePath, packageRelativePath);
ids.AddRange(GetAssetIdentifiers(localPath));
}
foreach (string dir in Directory.GetDirectories(directory, "*", SearchOption.TopDirectoryOnly))
{
if (Path.GetDirectoryName(dir).StartsWith("."))
continue;
string path = !string.IsNullOrEmpty(packageAbsolutePath)
? dir.Replace("\\", "/").Replace(packageAbsolutePath, packageRelativePath)
: dir;
ids.AddRange(GetAssetIdentifiersInDirectory(path, directoryIgnoreFilter));
}
return ids;
}
static List<AssetId> GetAssetIdentifiers(string assetPath)
{
List<AssetId> ids = new List<AssetId>();
if (assetPath.EndsWith(".unity"))
return ids;
foreach (UnityEngine.Object o in AssetDatabase.LoadAllAssetsAtPath(assetPath))
{
string g;
long file;
if (o != null && AssetDatabase.TryGetGUIDAndLocalFileIdentifier(o, out g, out file))
ids.Add(new AssetId(o, file.ToString(), g.ToString(), assetPath));
}
return ids;
}
/// <summary>
/// Load a remap json file from a relative path (Assets/MyRemapFile.json).
/// </summary>
/// <returns>A GuidRemapObject from the path, or if not found, a new GuidRemapObject</returns>
AssetIdRemapObject GetGuidRemapObject()
{
var remapObject = m_TreeView.remapObject;
if (remapObject != null)
return remapObject;
if (m_RemapTextAsset == null)
m_RemapTextAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(remapFilePath);
remapObject = new AssetIdRemapObject();
if (m_RemapTextAsset != null)
JsonUtility.FromJsonOverwrite(m_RemapTextAsset.text, remapObject);
return remapObject;
}
static string GetSelectedDirectory()
{
UObject o = Selection.activeObject;
if (o != null)
{
#if UNITY_6000_4_OR_NEWER
string path = AssetDatabase.GetAssetPath(o.GetEntityId());
#else
#pragma warning disable 0618
string path = AssetDatabase.GetAssetPath(o.GetInstanceID());
#pragma warning restore 0618
#endif
if (!string.IsNullOrEmpty(path))
{
if (Directory.Exists(path))
return Path.GetFullPath(path);
string res = Path.GetDirectoryName(path);
if (!string.IsNullOrEmpty(res) && System.IO.Directory.Exists(res))
return Path.GetFullPath(res);
}
}
return Path.GetFullPath("Assets");
}
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 20257f54bb007a547b7cb64d633a1664
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,228 @@
#if PROBUILDER_DEBUG
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
#if UNITY_6000_3_OR_NEWER
using BaseTreeView = UnityEditor.IMGUI.Controls.TreeView<int>;
using BaseTreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem<int>;
using BaseTreeViewState = UnityEditor.IMGUI.Controls.TreeViewState<int>;
#else
using BaseTreeView = UnityEditor.IMGUI.Controls.TreeView;
using BaseTreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem;
using BaseTreeViewState = UnityEditor.IMGUI.Controls.TreeViewState;
#endif
namespace UnityEngine.ProBuilder.AssetIdRemapUtility
{
sealed class AssetIdRemapBuilderTreeView : BaseTreeView
{
AssetIdRemapObject m_RemapObject = null;
const float k_RowHeight = 20f;
const float k_RowHeightSearching = 76f;
public bool isDirty = false;
public AssetIdRemapObject remapObject
{
get { return m_RemapObject; }
set { m_RemapObject = value; }
}
public AssetIdRemapBuilderTreeView(BaseTreeViewState state, MultiColumnHeader header)
: base(state, header)
{
rowHeight = 20f;
showAlternatingRowBackgrounds = true;
showBorder = true;
extraSpaceBeforeIconAndLabel = 18f;
}
protected override BaseTreeViewItem BuildRoot()
{
StringTupleTreeElement root = new StringTupleTreeElement(0, -1, -1, "Root", "", "");
var all = new List<BaseTreeViewItem>();
int index = 1;
for (int i = 0, c = remapObject.map.Count; i < c; i++)
{
all.Add(new StringTupleTreeElement(index++, 0, i, "Remap Entry", remapObject[i].source.name, remapObject[i].destination.name));
all.Add(new StringTupleTreeElement(index++, 1, i, "Local Path", remapObject[i].source.localPath, remapObject[i].destination.localPath));
all.Add(new StringTupleTreeElement(index++, 1, i, "GUID", remapObject[i].source.guid, remapObject[i].destination.guid));
all.Add(new StringTupleTreeElement(index++, 1, i, "File ID", remapObject[i].source.fileId, remapObject[i].destination.fileId));
all.Add(new StringTupleTreeElement(index++, 1, i, "Type", remapObject[i].source.type, remapObject[i].destination.type));
}
SetupParentsAndChildrenFromDepths(root, all);
return root;
}
public void SetRowHeight()
{
rowHeight = hasSearch ? k_RowHeightSearching : k_RowHeight;
}
protected override void RowGUI(RowGUIArgs args)
{
StringTupleTreeElement item = args.item as StringTupleTreeElement;
for (int i = 0; i < args.GetNumVisibleColumns(); ++i)
{
CellGUI(args.GetCellRect(i), item, i, ref args);
}
}
GUIContent m_CellContents = new GUIContent();
void CellGUI(Rect rect, StringTupleTreeElement item, int visibleColumn, ref RowGUIArgs args)
{
if (hasSearch)
{
AssetId id = visibleColumn == 0 ? m_RemapObject[item.index].source : m_RemapObject[item.index].destination;
m_CellContents.text = "<b>Name: </b>" + id.name +
"\n<b>Path: </b>" + id.localPath +
"\n<b>Guid: </b>" + id.guid +
"\n<b>FileId: </b>" + id.fileId +
"\n<b>Type: </b>" + id.type + (string.IsNullOrEmpty(id.assetType) ? "" : " (" + id.assetType + ")");
}
else
{
m_CellContents.text = item.GetLabel(visibleColumn);
}
rect.x += foldoutWidth + 4;
rect.width -= (foldoutWidth + 4);
if (hasSearch)
{
float textHeight = GUI.skin.label.CalcHeight(m_CellContents, rect.width);
rect.y += (rect.height - textHeight) * .5f;
rect.height = textHeight;
}
else
{
CenterRectUsingSingleLineHeight(ref rect);
}
GUI.skin.label.richText = true;
GUI.Label(rect, m_CellContents);
}
protected override bool DoesItemMatchSearch(BaseTreeViewItem element, string search)
{
StringTupleTreeElement tup = element as StringTupleTreeElement;
if (tup == null || tup.depth > 0)
return false;
var o = m_RemapObject[tup.index];
try
{
var patterns = search.Split(new[] { "\\&" }, StringSplitOptions.None).Select(x => new Regex(x));
foreach (var pattern in patterns)
{
if (!(pattern.IsMatch(o.source.localPath) ||
pattern.IsMatch(o.source.guid) ||
pattern.IsMatch(o.source.fileId) ||
pattern.IsMatch(o.source.type) ||
pattern.IsMatch(o.destination.localPath) ||
pattern.IsMatch(o.destination.guid) ||
pattern.IsMatch(o.destination.fileId) ||
pattern.IsMatch(o.destination.type)))
return false;
}
return true;
}
catch
{
return o.source.localPath.Contains(search) ||
o.source.guid.Contains(search) ||
o.source.fileId.Contains(search) ||
o.source.type.Contains(search) ||
o.destination.localPath.Contains(search) ||
o.destination.guid.Contains(search) ||
o.destination.fileId.Contains(search) ||
o.destination.type.Contains(search);
}
}
protected override void ContextClicked()
{
GenericMenu menu = new GenericMenu();
menu.AddItem(new GUIContent("Combine", ""), false, () =>
{
m_RemapObject.Merge(GetAssetIdSelection());
isDirty = true;
Reload();
SetSelection(new int[] {});
});
menu.ShowAsContext();
}
IEnumerable<AssetIdentifierTuple> GetAssetIdSelection()
{
return GetSelection().Select(x => m_RemapObject[((StringTupleTreeElement)FindItem(x, rootItem)).index]);
}
protected override void CommandEventHandling()
{
var evt = Event.current;
var selected = GetSelection();
if (selected.Count > 0)
{
if (evt.type == EventType.ValidateCommand)
{
if (evt.commandName.Equals("SoftDelete"))
evt.Use();
}
else if (evt.type == EventType.ExecuteCommand)
{
if (evt.commandName.Equals("SoftDelete")
&& EditorUtility.DisplayDialog("Delete", "Delete Selected Entries?", "Delete", "Cancel"))
{
m_RemapObject.Delete(GetAssetIdSelection());
evt.Use();
Reload();
isDirty = true;
SetSelection(new int[] {});
}
}
}
base.CommandEventHandling();
}
}
class StringTupleTreeElement : BaseTreeViewItem
{
public string item1;
public string item2;
public int index;
public StringTupleTreeElement(int id, int depth, int sourceIndex, string displayName, string key, string value) : base(id, depth, displayName)
{
item1 = key;
item2 = value;
index = sourceIndex;
}
public string GetLabel(int column)
{
return column < 1 ? item1 : item2;
}
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: daa26ef7aa9c82a47a06ddb6eefc0674
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,727 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
#if UNITY_6000_3_OR_NEWER
using BaseTreeViewState = UnityEditor.IMGUI.Controls.TreeViewState<int>;
using BaseTreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem<int>;
#else
using BaseTreeViewState = UnityEditor.IMGUI.Controls.TreeViewState;
using BaseTreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem;
#endif
namespace UnityEngine.ProBuilder.AssetIdRemapUtility
{
sealed class AssetIdRemapEditor : EditorWindow
{
const string k_ProBuilder2DllName = "ProBuilderCore-Unity5.dll";
const string k_ProBuilder3DllName = "ProBuilderCore.dll";
const string k_ConversionLogPath = "Temp/ProBuilderConversionLog.txt";
static readonly string[] k_RemapFilePaths = new string[]
{
"Packages/com.unity.probuilder/Content/Upgrade/AssetIdRemap-4_0_0.json"
};
static readonly string[] k_AssetExtensionsToRemap = new string[]
{
"*.meta",
"*.unity",
"*.asset",
"*.anim",
"*.animset",
"*.blendtree",
"*.buildreport",
"*.colors",
"*.controller",
"*.cubemap",
"*.curves",
"*.curvesNormalized",
"*.flare",
"*.fontsettings",
"*.giparams",
"*.gradients",
"*.guiskin",
"*.ht",
"*.mask",
"*.mat",
"*.mesh",
"*.mixer",
"*.overrideController",
"*.particleCurves",
"*.particleCurvesSigned",
"*.particleDoubleCurves",
"*.particleDoubleCurvesSigned",
"*.physicMaterial",
"*.physicsMaterial2D",
"*.playable",
"*.prefab",
"*.preset",
"*.renderTexture",
"*.shadervariants",
"*.spriteatlas",
"*.state",
"*.statemachine",
"*.texture2D",
"*.transition",
"*.webCamTexture",
};
static readonly string[] k_AssetStoreDirectorySuggestedDeleteIgnoreFilter = new string[]
{
"(^|(?<=/))Data(/|)$",
"(^|(?<=/))ProBuilderMeshCache(/|)$",
".meta$",
"^\\."
};
static readonly string[] k_AssetStoreSuggestedFileDeleteIgnoreFilter = new string[]
{
".meta$",
".asset$",
"^\\."
};
static readonly string[] k_AssetStoreMustDelete = new string[]
{
"pb_Object.cs",
"pb_Entity.cs",
"ProBuilder/Classes",
"ProBuilder/Editor",
"ProBuilderCore-Unity5.dll",
"ProBuilderMeshOps-Unity5.dll",
"ProBuilderEditor-Unity5.dll"
};
// @todo show a warning when any code is not getting deleted
static readonly string[] k_AssetStoreShouldDelete = new string[]
{
"ProBuilder/API Examples",
"API Examples/Editor",
"ProBuilder/About",
"ProBuilder/Shader",
};
[Flags]
enum ConversionReadyState
{
Ready = 0,
NoActionRequired = 1 << 0,
SerializationError = 1 << 1,
AssetStoreDeleteError = 1 << 2,
AssetStoreDeleteWarning = 1 << 3,
AssetStoreInstallFound = 1 << 4,
DeprecatedAssetIdsFound = 1 << 5,
MissingRemapFile = 1 << 6,
ConversionRan = 1 << 7
};
TextAsset m_RemapFile = null;
[SerializeField]
string m_DeprecatedProBuilderDirectory;
[SerializeField]
bool m_DeprecatedProBuilderFound;
[SerializeField]
ConversionReadyState m_ConversionReadyState = ConversionReadyState.Ready;
string m_ConversionLog;
AssetTreeView m_AssetsToDeleteTreeView;
MultiColumnHeader m_MultiColumnHeader;
Rect m_AssetTreeRect = new Rect(0, 0, 0, 0);
[SerializeField]
BaseTreeViewState m_TreeViewState = null;
[SerializeField]
MultiColumnHeaderState m_MultiColumnHeaderState = null;
GUIContent m_AssetTreeSettingsContent = null;
Vector2 m_ConversionLogScroll = Vector2.zero;
static class Styles
{
public static GUIStyle settingsIcon { get { return m_SettingsIcon; } }
public static GUIStyle convertButton { get { return m_ConvertButton; } }
static bool m_Init = false;
static GUIStyle m_SettingsIcon;
static GUIStyle m_ConvertButton;
public static void Init()
{
if (!m_Init)
m_Init = true;
else
return;
m_SettingsIcon = GUI.skin.GetStyle("IconButton");
m_ConvertButton = new GUIStyle(GUI.skin.GetStyle("AC Button"));
m_ConvertButton.margin.bottom += 4;
}
}
internal static void OpenConversionEditor()
{
GetWindow<AssetIdRemapEditor>(true, "ProBuilder Update Utility", true);
}
void OnEnable()
{
m_AssetTreeSettingsContent = EditorGUIUtility.IconContent("_Popup");
for (int i = 0, c = k_RemapFilePaths.Length; m_RemapFile == null && i < c; i++)
m_RemapFile = AssetDatabase.LoadAssetAtPath<TextAsset>(k_RemapFilePaths[i]);
if (m_RemapFile == null)
{
Debug.LogWarning("Could not find a valid asset id remap file!");
}
if (m_TreeViewState == null)
m_TreeViewState = new BaseTreeViewState();
if (m_MultiColumnHeaderState == null)
m_MultiColumnHeaderState = new MultiColumnHeaderState(new MultiColumnHeaderState.Column[]
{
new MultiColumnHeaderState.Column()
{
headerContent = new GUIContent("Obsolete Files to Delete")
}
});
m_MultiColumnHeader = new MultiColumnHeader(m_MultiColumnHeaderState) { height = 0 };
m_AssetsToDeleteTreeView = new AssetTreeView(m_TreeViewState, m_MultiColumnHeader);
ResetAssetsToDelete();
try
{
if (File.Exists(k_ConversionLogPath))
m_ConversionLog = File.ReadAllText(k_ConversionLogPath);
else
m_ConversionLog = null;
}
catch
{
m_ConversionLog = null;
}
// if the project just contains deprecated guids, and is already in text serialization mode, we can skip the dialog
// and just run the conversion immediately.
if (m_ConversionReadyState == (ConversionReadyState.Ready | ConversionReadyState.DeprecatedAssetIdsFound))
DoConversion();
}
void OnGUI()
{
Styles.Init();
if (m_ConversionReadyState == ConversionReadyState.NoActionRequired ||
m_ConversionReadyState == ConversionReadyState.ConversionRan)
{
if (!string.IsNullOrEmpty(m_ConversionLog))
{
m_ConversionLogScroll = EditorGUILayout.BeginScrollView(m_ConversionLogScroll);
GUILayout.Label(m_ConversionLog, EditorStyles.wordWrappedLabel);
EditorGUILayout.EndScrollView();
}
else
{
GUI.Label(new Rect(0, 0, position.width, position.height), "ProBuilder is up to date!",
EditorStyles.centeredGreyMiniLabel);
if (Event.current.type == EventType.ContextClick)
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent("Find and replace deprecated Asset IDs"), false, () =>
{
var log = new StringBuilder();
RemapAssetIds(log);
Debug.Log(log);
});
menu.ShowAsContext();
}
}
return;
}
else if ((m_ConversionReadyState & ConversionReadyState.AssetStoreInstallFound) > 0)
{
GUILayout.Label("Obsolete Files to Delete", EditorStyles.boldLabel);
m_AssetTreeRect = GUILayoutUtility.GetRect(position.width, 128, GUILayout.ExpandHeight(true));
EditorGUI.BeginChangeCheck();
DrawTreeSettings();
m_AssetsToDeleteTreeView.OnGUI(m_AssetTreeRect);
if (EditorGUI.EndChangeCheck())
m_ConversionReadyState = GetReadyState();
}
else if ((m_ConversionReadyState & ConversionReadyState.DeprecatedAssetIdsFound) > 0)
{
var deprecatedIdsRect = GUILayoutUtility.GetRect(position.width, 32, GUILayout.ExpandHeight(true));
GUI.Label(deprecatedIdsRect, "References to old ProBuilder install found.\n\nProject is ready for conversion.",
EditorStyles.centeredGreyMiniLabel);
}
if ((m_ConversionReadyState & ConversionReadyState.SerializationError) > 0)
{
EditorGUILayout.HelpBox(
"Cannot update project with binary or mixed serialization.\n\nPlease swith to ForceText serialization to proceed (you may switch back to ForceBinary or Mixed after the conversion process).",
MessageType.Error);
SerializationMode serializationMode = EditorSettings.serializationMode;
EditorGUI.BeginChangeCheck();
serializationMode = (SerializationMode)EditorGUILayout.EnumPopup("Serialization Mode", serializationMode);
if (EditorGUI.EndChangeCheck())
{
EditorSettings.serializationMode = serializationMode;
m_ConversionReadyState = GetReadyState();
}
}
if ((m_ConversionReadyState & ConversionReadyState.AssetStoreDeleteError) > 0)
EditorGUILayout.HelpBox(
"Cannot update project without removing ProBuilder/Classes and ProBuilder/Editor directories.", MessageType.Error);
if ((m_ConversionReadyState & ConversionReadyState.AssetStoreDeleteWarning) > 0)
EditorGUILayout.HelpBox(
"Some old ProBuilder files are not marked for deletion. This may cause errors after the conversion process is complete.\n\nTo clear this error use the settings icon to reset the Assets To Delete tree.",
MessageType.Warning);
GUI.enabled =
(m_ConversionReadyState & (ConversionReadyState.AssetStoreDeleteError | ConversionReadyState.SerializationError)) ==
ConversionReadyState.Ready;
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Convert to ProBuilder 4", Styles.convertButton))
{
DoConversion();
GUIUtility.ExitGUI();
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.Space(16);
GUI.enabled = true;
}
void DoConversion()
{
var log = new StringBuilder();
try
{
// Set serialization mode to mixed then back to force-text to reserialize any assets in binary form
// that may have somehow persisted (version control makes a state where "Force Text" is enabled with
// some existing binary assets persisting possible). this happened during testing.
// mixed doesn't refresh anything, but setting to ForceText re-iterates all assets and double-checks
// that they are in the correct format.
EditorSettings.serializationMode = SerializationMode.Mixed;
EditorSettings.serializationMode = SerializationMode.ForceText;
EditorApplication.LockReloadAssemblies();
log.AppendLine("ProBuilder 4 Conversion Log");
log.AppendLine("");
if ((m_ConversionReadyState & ConversionReadyState.AssetStoreInstallFound) == ConversionReadyState.AssetStoreInstallFound)
{
log.AppendLine("Removing existing ProBuilder files...");
if (RemoveAssetStoreFiles(m_AssetsToDeleteTreeView.GetRoot(), log))
{
log.AppendLine("\nRemapping Asset Ids...");
RemapAssetIds(log);
}
}
else
{
log.AppendLine("Remapping Asset Ids...");
RemapAssetIds(log);
}
}
finally
{
m_ConversionLog = log.ToString();
try
{
Directory.CreateDirectory("Temp");
File.WriteAllText(k_ConversionLogPath, m_ConversionLog);
}
catch
{
Debug.Log(m_ConversionLog);
}
EditorApplication.UnlockReloadAssemblies();
m_ConversionReadyState = ConversionReadyState.ConversionRan;
EditorApplication.delayCall += AssetDatabase.Refresh;
}
}
void DrawTreeSettings()
{
Vector2 iconSize = Styles.settingsIcon.CalcSize(m_AssetTreeSettingsContent);
Rect settingsRect = new Rect(
position.width - iconSize.x - 4,
4,
iconSize.x,
iconSize.y);
if (EditorGUI.DropdownButton(settingsRect, m_AssetTreeSettingsContent, FocusType.Passive, Styles.settingsIcon))
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent("Reset"), false, ResetAssetsToDelete);
menu.ShowAsContext();
}
}
ConversionReadyState GetReadyState()
{
var state = ConversionReadyState.Ready;
if (ProjectContainsOldAssetIds())
state |= ConversionReadyState.DeprecatedAssetIdsFound;
if (PackageImporter.IsPreProBuilder4InProject())
state |= ConversionReadyState.AssetStoreInstallFound;
if (state == ConversionReadyState.Ready && PackageImporter.IsProBuilder4OrGreaterLoaded())
return ConversionReadyState.NoActionRequired;
state |= ValidateProjectTextSerialized();
state |= ValidateAssetStoreRemoval();
return state;
}
const int k_DialogOkay = 0;
const int k_DialogAlt = 1;
const int k_DialogCancel = 2;
void ResetAssetsToDelete()
{
m_DeprecatedProBuilderFound = PackageImporter.IsPreProBuilder4InProject();
if (m_DeprecatedProBuilderFound && !ValidateAssetStoreProBuilderRoot(m_DeprecatedProBuilderDirectory))
m_DeprecatedProBuilderDirectory = FindAssetStoreProBuilderInstall();
// If still no old folder found (and PackageImporter tells us one exists), ask the user to point it out
if (m_DeprecatedProBuilderFound && !ValidateAssetStoreProBuilderRoot(m_DeprecatedProBuilderDirectory))
{
int res = EditorUtility.DisplayDialogComplex(
"Could Not Find Existing ProBuilder Directory",
"Would you like to manually select the Asset Store installed ProBuilder folder and continue with the conversion process, or continue without removing old ProBuilder files (not recommended)?",
"Select Folder",
"Continue",
"Cancel");
if (res == k_DialogOkay)
{
// if they don't get it right after 3 attempts it's probably not going to happen and they'll be annoyed at
// an inescapable dialog loop
int userDirectoryPickAttempts = 0;
while (userDirectoryPickAttempts++ < 3)
{
m_DeprecatedProBuilderDirectory = EditorUtility.OpenFolderPanel("Select ProBuilder Directory", "Assets", "");
if (string.IsNullOrEmpty(m_DeprecatedProBuilderDirectory))
{
UnityEngine.Debug.LogWarning("Canceling ProBuilder Asset Store to Package Manager conversion. You may start this process again at any time by accessing the 'Tools>ProBuilder>Repair>Convert to ProBuilder 4' menu item.");
EditorApplication.delayCall += Close;
break;
}
if (ValidateAssetStoreProBuilderRoot(m_DeprecatedProBuilderDirectory))
{
// got a good directory, continue with process
break;
}
else
{
if (!EditorUtility.DisplayDialog("Selected Folder is not a ProBuilder Install",
"The folder selected not an old version of ProBuilder. Would you like to select a different directory?", "Yes",
"Cancel"))
{
EditorApplication.delayCall += Close;
break;
}
}
}
}
else if (res == k_DialogCancel)
{
UnityEngine.Debug.LogWarning("Canceling ProBuilder Asset Store to Package Manager conversion. You may start this process again at any time by accessing the 'Tools>ProBuilder>Repair>Convert to ProBuilder 4' menu item.");
EditorApplication.delayCall += Close;
}
}
m_AssetsToDeleteTreeView.directoryRoot = m_DeprecatedProBuilderDirectory;
m_AssetsToDeleteTreeView.SetDirectoryIgnorePatterns(k_AssetStoreDirectorySuggestedDeleteIgnoreFilter);
m_AssetsToDeleteTreeView.SetFileIgnorePatterns(k_AssetStoreSuggestedFileDeleteIgnoreFilter);
m_AssetsToDeleteTreeView.Reload();
m_AssetsToDeleteTreeView.ExpandAll();
m_MultiColumnHeader.ResizeToFit();
m_ConversionReadyState = GetReadyState();
}
bool RemoveAssetStoreFiles(BaseTreeViewItem root, StringBuilder log)
{
AssetTreeItem node = root as AssetTreeItem;
if (node != null && (node.enabled && !node.isMixedState))
{
if (!AssetDatabase.MoveAssetToTrash(node.fullPath))
{
if (!AssetDatabase.DeleteAsset(node.fullPath))
{
if (Directory.Exists(node.fullPath))
{
Directory.Delete(node.fullPath, true);
if (Directory.Exists(node.fullPath))
{
Debug.LogError("Directory.Delete failed, giving up. (" + node.fullPath + ")");
return false;
}
else
{
File.Delete(node.fullPath.Trim('/') + ".meta");
}
}
else if (File.Exists(node.fullPath))
{
File.Delete(node.fullPath);
if (File.Exists(node.fullPath))
{
Debug.LogError("File.Delete failed, giving up (" + node.fullPath + ")");
return true;
}
else
{
File.Delete(node.fullPath + ".meta");
}
}
}
}
log.AppendLine(" - " + node.fullPath);
return true;
}
if (node.children != null)
{
bool success = true;
foreach (var branch in node.children)
if (!RemoveAssetStoreFiles(branch, log))
success = false;
return success;
}
return true;
}
void RemapAssetIds(StringBuilder log)
{
AssetIdRemapObject remapObject = new AssetIdRemapObject();
JsonUtility.FromJsonOverwrite(m_RemapFile.text, remapObject);
int remappedReferences = 0;
int modifiedFiles = 0;
string[] assets = k_AssetExtensionsToRemap.SelectMany(x => Directory.GetFiles("Assets", x, SearchOption.AllDirectories)).ToArray();
var failures = new StringBuilder();
var successes = new StringBuilder();
int failCount = 0;
for (int i = 0, c = assets.Length; i < c; i++)
{
EditorUtility.DisplayProgressBar("Asset Id Remap", assets[i], i / (float)c);
int modified;
if (!DoAssetIdentifierRemap(assets[i], remapObject.map, out modified))
{
failures.AppendLine(" - " + assets[i]);
failCount++;
}
else
{
if (modified > 0)
{
successes.AppendLine(string.Format(" - ({0}) references in {1}", modified.ToString(), assets[i]));
modifiedFiles++;
}
}
remappedReferences += modified;
}
EditorUtility.ClearProgressBar();
log.AppendLine(string.Format("Remapped {0} references in {1} files.", remappedReferences.ToString(), modifiedFiles.ToString()));
log.AppendLine(string.Format("\nFailed remapping {0} files:", failCount.ToString()));
log.AppendLine(failCount > 0 ? failures.ToString() : " - (none)");
log.AppendLine("\nSuccessfully remapped files:");
log.Append(successes);
PackageImporter.Reimport(PackageImporter.EditorCorePackageManager);
AssetDatabase.Refresh();
}
static bool DoAssetIdentifierRemap(string path, IEnumerable<AssetIdentifierTuple> map, out int modified, bool remapSourceGuid = false)
{
modified = 0;
try
{
var sr = new StreamReader(path);
var sw = new StreamWriter(path + ".remap", false);
List<StringTuple> replace = new List<StringTuple>();
IEnumerable<AssetIdentifierTuple> assetIdentifierTuples = map as AssetIdentifierTuple[] ?? map.ToArray();
foreach (var kvp in assetIdentifierTuples)
{
if (kvp.source.fileId.Equals(kvp.destination.fileId) && kvp.source.guid.Equals(kvp.destination.guid))
continue;
replace.Add(new StringTuple(
string.Format("{{fileID: {0}, guid: {1}, type:", kvp.source.fileId, kvp.source.guid),
string.Format("{{fileID: {0}, guid: {1}, type:", kvp.destination.fileId, kvp.destination.guid)));
}
// If remapping in-place (ie, changing a package guids) then also apply to metadata
if (remapSourceGuid)
{
HashSet<string> used = new HashSet<string>();
foreach (var kvp in assetIdentifierTuples)
{
// AssetIdentifier list will contain duplicate guids (assets can contain sub-assets, separated by fileId)
// when swapping meta file guids we don't need multiple entries
if (used.Add(kvp.source.guid))
replace.Add(new StringTuple(
string.Format("guid: {0}", kvp.source.guid),
string.Format("guid: {0}", kvp.destination.guid)));
}
}
while (sr.Peek() > -1)
{
var line = sr.ReadLine();
foreach (var kvp in replace)
{
if (line.Contains(kvp.key))
{
modified++;
line = line.Replace(kvp.key, kvp.value);
break;
}
}
sw.WriteLine(line);
}
sr.Close();
sw.Close();
if (modified > 0)
{
File.Delete(path);
File.Move(path + ".remap", path);
}
else
{
File.Delete(path + ".remap");
}
}
catch
{
return false;
}
return true;
}
bool ProjectContainsOldAssetIds()
{
// todo this should only check with the loaded remap file, but for now it's hard-coded
return PackageImporter.IsPreProBuilder4InProject() || PackageImporter.DoesProjectContainDeprecatedGUIDs();
}
static string FindAssetStoreProBuilderInstall()
{
string[] matches = Directory.GetDirectories("Assets", "ProBuilder", SearchOption.AllDirectories);
foreach (var match in matches)
{
string dir = match.Replace("\\", "/") + "/";
if (ValidateAssetStoreProBuilderRoot(dir))
return dir;
}
return null;
}
/// <summary>
/// Is the ProBuilder folder an Asset Store installed version?
/// </summary>
/// <param name="dir"></param>
/// <returns></returns>
static bool ValidateAssetStoreProBuilderRoot(string dir)
{
return !string.IsNullOrEmpty(dir) &&
File.Exists(dir + "/Classes/ProBuilderCore-Unity5.dll") &&
File.Exists(dir + "/Editor/ProBuilderEditor-Unity5.dll");
}
ConversionReadyState ValidateAssetStoreRemoval()
{
ConversionReadyState state = (ConversionReadyState)0;
List<AssetTreeItem> assets = m_AssetsToDeleteTreeView.GetAssetList();
if (assets.Any(x => !x.enabled && k_AssetStoreMustDelete.Any(y => x.fullPath.Contains(y))))
state |= ConversionReadyState.AssetStoreDeleteError;
else
state |= ConversionReadyState.Ready;
if (assets.Any(x => !x.enabled && k_AssetStoreShouldDelete.Any(y => x.fullPath.Contains(y))))
state |= ConversionReadyState.AssetStoreDeleteWarning;
return state;
}
static ConversionReadyState ValidateProjectTextSerialized()
{
return EditorSettings.serializationMode == SerializationMode.ForceText
? ConversionReadyState.Ready
: ConversionReadyState.SerializationError;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2fa4bbd1a7ccee24783998770d56522e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,316 @@
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UObject = UnityEngine.Object;
#if UNITY_6000_3_OR_NEWER
using BaseTreeView = UnityEditor.IMGUI.Controls.TreeView<int>;
using BaseTreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem<int>;
using BaseTreeViewState = UnityEditor.IMGUI.Controls.TreeViewState<int>;
#else
using BaseTreeView = UnityEditor.IMGUI.Controls.TreeView;
using BaseTreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem;
using BaseTreeViewState = UnityEditor.IMGUI.Controls.TreeViewState;
#endif
namespace UnityEngine.ProBuilder.AssetIdRemapUtility
{
sealed class AssetTreeItem : BaseTreeViewItem
{
string m_RelativePath;
string m_FullPath;
bool m_IsEnabled;
bool m_IsDirectory;
bool m_IsMixedState;
public AssetTreeItem(int id, string fullPath, string relativePath) : base(id, 0)
{
m_IsDirectory = Directory.Exists(fullPath);
m_FullPath = fullPath;
m_RelativePath = relativePath;
m_IsEnabled = true;
displayName = m_FullPath.Replace("\\", "/").Replace(Application.dataPath, "Assets/");
}
public bool enabled
{
get { return m_IsEnabled; }
set { m_IsEnabled = value; }
}
public bool isDirectory
{
get { return m_IsDirectory; }
set { m_IsDirectory = value; }
}
public string fullPath
{
get { return m_FullPath; }
}
public string relativePath
{
get { return m_RelativePath; }
}
public bool isMixedState { get { return m_IsMixedState; } }
public void SetEnabled(bool isEnabled)
{
enabled = isEnabled;
if (children != null)
{
foreach (var child in children)
{
AssetTreeItem asset = child as AssetTreeItem;
if (asset != null)
asset.SetEnabled(isEnabled);
}
}
var upstream = parent;
while (upstream != null)
{
var up = upstream as AssetTreeItem;
if (up != null && up.children != null)
{
AssetTreeItem firstChild = up.children.FirstOrDefault() as AssetTreeItem;
if (firstChild != null)
{
up.m_IsMixedState = up.children.Any(x =>
{
var y = x as AssetTreeItem;
return y.enabled != firstChild.enabled;
});
if (!up.m_IsMixedState)
up.enabled = firstChild.enabled;
}
else
{
up.m_IsMixedState = false;
}
}
upstream = upstream.parent;
}
}
}
class AssetTreeView : BaseTreeView
{
string m_RootDirectory = null;
GUIContent m_TempContent = new GUIContent();
Rect m_ToggleRect = new Rect(0, 0, 0, 0);
public string directoryRoot
{
get { return m_RootDirectory; }
set { m_RootDirectory = value; }
}
public AssetTreeItem GetRoot()
{
return rootItem.hasChildren
? rootItem.children.First() as AssetTreeItem
: null;
}
IEnumerable<Regex> m_DirectoryIgnorePatterns;
IEnumerable<Regex> m_FileIgnorePatterns;
public void SetDirectoryIgnorePatterns(string[] regexStrings)
{
m_DirectoryIgnorePatterns = regexStrings.Select(x => new Regex(x));
}
public void SetFileIgnorePatterns(string[] regexStrings)
{
m_FileIgnorePatterns = regexStrings.Select(x => new Regex(x));
}
public AssetTreeView(BaseTreeViewState state, MultiColumnHeader header) : base(state, header)
{
showBorder = true;
columnIndexForTreeFoldouts = 0;
rowHeight = 18f;
}
protected override BaseTreeViewItem BuildRoot()
{
AssetTreeItem root = new AssetTreeItem(0, Application.dataPath, "")
{
depth = -1,
enabled = false
};
if (string.IsNullOrEmpty(m_RootDirectory) || !Directory.Exists(m_RootDirectory))
{
// if root has no children and you SetupDepthsFromParentsAndChildren nullrefs are thrown
var list = new List<BaseTreeViewItem>() {};
SetupParentsAndChildrenFromDepths(root, list);
}
else
{
int nodeIndex = 0;
PopulateAssetTree(m_RootDirectory, root, ref nodeIndex);
SetupDepthsFromParentsAndChildren(root);
ApplyEnabledFilters(root);
}
return root;
}
void PopulateAssetTree(string directory, AssetTreeItem parent, ref int nodeIdIndex)
{
string unixDirectory = directory.Replace("\\", "/");
AssetTreeItem leaf = new AssetTreeItem(
nodeIdIndex++,
unixDirectory,
unixDirectory.Replace(parent.fullPath, "").Trim('/'))
{
enabled = true
};
parent.AddChild(leaf);
foreach (string path in Directory.GetFiles(directory, "*", SearchOption.TopDirectoryOnly))
{
if (path.StartsWith(".") || path.EndsWith(".meta"))
continue;
string unixPath = path.Replace("\\", "/");
leaf.AddChild(new AssetTreeItem(
nodeIdIndex++,
unixPath,
unixPath.Replace(unixDirectory, "").Trim('/')) { enabled = true });
}
foreach (string dir in Directory.GetDirectories(directory, "*", SearchOption.TopDirectoryOnly))
PopulateAssetTree(dir, leaf, ref nodeIdIndex);
}
public void ApplyEnabledFilters(BaseTreeViewItem root)
{
AssetTreeItem node = root as AssetTreeItem;
AssetTreeItem parent = root.parent as AssetTreeItem;
if (node != null)
{
if (node.isDirectory)
node.SetEnabled(
(parent == null || parent.enabled) &&
(m_DirectoryIgnorePatterns == null || !m_DirectoryIgnorePatterns.Any(x => x.IsMatch(node.fullPath))));
else
node.SetEnabled(
(parent == null || parent.enabled) &&
(m_FileIgnorePatterns == null || !m_FileIgnorePatterns.Any(x => x.IsMatch(node.fullPath))));
}
if (root.children != null)
foreach (var child in root.children)
ApplyEnabledFilters(child);
}
protected override void AfterRowsGUI()
{
if (rootItem.hasChildren)
return;
if (Event.current.type == EventType.Repaint)
{
Rect r = new Rect(treeViewRect);
// not sure why there is a two row pad to the treeViewRect
r.y -= rowHeight * 2;
GUI.Label(r, "No Existing ProBuilder Install Found", EditorStyles.centeredGreyMiniLabel);
}
}
protected override void RowGUI(RowGUIArgs args)
{
CenterRectUsingSingleLineHeight(ref args.rowRect);
args.rowRect.xMin += GetContentIndent(args.item);
AssetTreeItem item = args.item as AssetTreeItem;
m_TempContent.text = item.relativePath;
m_TempContent.tooltip = item.fullPath;
m_ToggleRect.x = args.rowRect.xMin;
m_ToggleRect.y = args.rowRect.yMin;
m_ToggleRect.width = 16;
m_ToggleRect.height = args.rowRect.height;
args.rowRect.xMin += m_ToggleRect.width;
EditorGUI.BeginChangeCheck();
EditorGUI.showMixedValue = item.isMixedState;
item.enabled = EditorGUI.Toggle(m_ToggleRect, "", item.enabled);
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
if (GetSelection().Any(x => FindItem(x, rootItem) == item))
{
foreach (int id in GetSelection())
{
var sel = FindItem(id, rootItem) as AssetTreeItem;
if (sel != null)
sel.SetEnabled(item.enabled);
}
}
else
{
item.SetEnabled(item.enabled);
}
}
bool guiEnabled = GUI.enabled;
GUI.enabled = item.enabled;
GUI.Label(args.rowRect, m_TempContent);
GUI.enabled = guiEnabled;
}
protected override void ContextClickedItem(int id)
{
var clicked = FindItem(id, rootItem) as AssetTreeItem;
if (clicked != null)
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent("Show Asset"), false, () =>
{
EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath<UObject>(clicked.fullPath));
});
menu.ShowAsContext();
}
}
public List<AssetTreeItem> GetAssetList()
{
List<AssetTreeItem> assets = new List<AssetTreeItem>();
GatherTreeItems(rootItem as AssetTreeItem, assets);
return assets;
}
void GatherTreeItems(AssetTreeItem node, List<AssetTreeItem> list)
{
if (node == null)
return;
list.Add(node);
if (node.children != null)
foreach (var child in node.children)
if (child is AssetTreeItem)
GatherTreeItems(child as AssetTreeItem, list);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 709975e9e21e4a52a8ea44cd4a779feb
timeCreated: 1512405263
@@ -0,0 +1,146 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
#if UNITY_6000_5_OR_NEWER
using UnityEngine.Assemblies;
#endif
namespace UnityEngine.ProBuilder.AssetIdRemapUtility
{
static class PackageImporter
{
const string k_PackageManagerEditorCore = "e98d45d69e2c4936a7382af00fd45e58";
const string k_AssetStoreEditorCore = "4df21bd079886d84699ca7be1316c7a7";
const string k_ProBuilder2CoreGUID = "0472bdc8d6d15384d98f22ee34302f9c";
const string k_ProBuilder3CoreGUID = "4f0627da958b4bb78c260446066f065f";
static readonly string[] k_AssetStoreInstallGuids = new string[]
{
"0472bdc8d6d15384d98f22ee34302f9c", // ProBuilderCore
"b617d7797480df7499f141d87e13ebc5", // ProBuilderMeshOps
"4df21bd079886d84699ca7be1316c7a7" // ProBuilderEditor
};
static readonly string[] k_PackageManagerInstallGuids = new string[]
{
"4f0627da958b4bb78c260446066f065f", // Core
"9b27d8419276465b80eb88c8799432a1", // Mesh Ops
"e98d45d69e2c4936a7382af00fd45e58", // Editor
};
internal static string EditorCorePackageManager { get { return k_PackageManagerEditorCore; } }
internal static string EditorCoreAssetStore { get { return k_AssetStoreEditorCore; } }
internal static void SetEditorDllEnabled(string guid, bool isEnabled)
{
string dllPath = AssetDatabase.GUIDToAssetPath(guid);
var importer = AssetImporter.GetAtPath(dllPath) as PluginImporter;
if (importer != null)
{
importer.SetCompatibleWithAnyPlatform(false);
importer.SetCompatibleWithEditor(isEnabled);
}
}
internal static bool IsEditorPluginEnabled(string guid)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
var importer = AssetImporter.GetAtPath(path) as PluginImporter;
if (!importer)
return false;
return importer.GetCompatibleWithEditor() && !importer.GetCompatibleWithAnyPlatform();
}
internal static void Reimport(string guid)
{
string dllPath = AssetDatabase.GUIDToAssetPath(guid);
if (!string.IsNullOrEmpty(dllPath))
AssetDatabase.ImportAsset(dllPath);
}
static bool AreAnyAssetsAreLoaded(string[] guids)
{
foreach (var id in guids)
{
if (AssetImporter.GetAtPath(AssetDatabase.GUIDToAssetPath(id)) != null)
return true;
}
return false;
}
static bool FileContainsString(string path, string a, string b)
{
using (var sr = new StreamReader(path))
{
while (sr.Peek() > -1)
{
var line = sr.ReadLine();
if (line.Contains(a) || line.Contains(b))
return true;
}
}
return false;
}
internal static bool DoesProjectContainDeprecatedGUIDs()
{
var scenes = Directory.GetFiles("Assets", "*.unity", SearchOption.AllDirectories);
var prefabs = Directory.GetFiles("Assets", "*.prefab", SearchOption.AllDirectories);
var count = (scenes.Length + prefabs.Length) - 1f;
var exit = count < 1f;
try
{
for (int n = 0; !exit && n < 2; n++)
{
var arr = n < 1 ? scenes : prefabs;
for (int i = 0, c = arr.Length; !exit && i < c; i++)
{
if (FileContainsString(arr[i], k_ProBuilder2CoreGUID, k_ProBuilder3CoreGUID))
return true;
exit = EditorUtility.DisplayCancelableProgressBar(
"Checking for Broken ProBuilder References",
"Scanning scene " + arr[i],
i / count);
}
}
}
finally
{
EditorUtility.ClearProgressBar();
}
return false;
}
/// <summary>
/// Check if any pre-4.0 ProBuilder package is present in the project
/// </summary>
/// <returns></returns>
internal static bool IsPreProBuilder4InProject()
{
return AreAnyAssetsAreLoaded(k_AssetStoreInstallGuids)
|| AreAnyAssetsAreLoaded(k_PackageManagerInstallGuids);
}
internal static bool IsProBuilder4OrGreaterLoaded()
{
#if UNITY_6000_5_OR_NEWER
return CurrentAssemblies.GetLoadedAssemblies().Any(x => x.ToString().Contains("Unity.ProBuilder"));
#else
return AppDomain.CurrentDomain.GetAssemblies().Any(x => x.ToString().Contains("Unity.ProBuilder"));
#endif
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bb48793c33304c1ca4300c46c9c13b46
timeCreated: 1512069815
@@ -0,0 +1,10 @@
{
"name": "Unity.ProBuilder.AssetIdRemapUtility",
"references": [],
"optionalUnityReferences": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9caa7e155cb0648da84dca829b17620f
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: