Skip to content

Instantly share code, notes, and snippets.

@TobiasPott
Created October 25, 2022 11:29
Show Gist options
  • Save TobiasPott/c794e754b3e148e4c62f38c1efb7a3e5 to your computer and use it in GitHub Desktop.
Save TobiasPott/c794e754b3e148e4c62f38c1efb7a3e5 to your computer and use it in GitHub Desktop.
Extension to allow shortcut-based save and load of selection to disk (.sel files)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SelectionExtensions
{
public const string SelectionFileExt = "sel";
[MenuItem("Edit/Selection/Load Selection from file %&l")]
private static void LoadSelectionFromFile()
{
string filePath = EditorUtility.OpenFilePanel("Select file", Application.dataPath, SelectionFileExt);
if (!string.IsNullOrEmpty(filePath))
SelectFromFile(filePath);
}
[MenuItem("Edit/Selection/Save Selection to file %&s")]
private static void SaveSelectionToFile()
{
// ToDo:
// use "Temp + DateTime" as default anonymous selection identifier
// add method overload with 'shift' modifier to prompt for name/path on save
// add method overload with 'shift' modifier to prompt for file selection on load
// define no-shift modifier and anonymous naming as default behaviour on save
// define no-shift modifier and anonymous naming as default behaviour on load
// add caching of last path to EditorPrefs
// -> should reset to default anonymous location when anonymous save/load is used
// -> default anonymous location should be a subfolder of the project folder (NOT asset folder)
if (Selection.gameObjects.Length != 0)
SelectionToFile(Selection.gameObjects);
else
Debug.LogWarning("No objects are selected. An empty selection cannot be saved to disk.");
}
private static void SelectionToFile(ICollection<GameObject> selection)
{
List<MappedObject> objects = new List<MappedObject>();
foreach (GameObject go in selection)
{
string objectPath = go.transform.GetParentPath();
string hierarchyPath = objectPath.Replace(go.name, "");
objects.Add(new MappedObject(go, objectPath));
}
// writing selection files per material to disk
IEnumerable<IGrouping<string, MappedObject>> groups = objects.OrderBy(x => x.Path).ThenBy(x => x.GameObject.name).GroupBy(x => x.MaterialName);
foreach (IGrouping<string, MappedObject> group in groups)
{
Debug.Log("Material: " + group.Key);
string filename = "Selection_" + group.Key + "." + SelectionFileExt;
StringBuilder sbOutput = new StringBuilder();
foreach (MappedObject obj in group)
sbOutput.AppendLine(obj.Path);
System.IO.File.WriteAllText(filename, sbOutput.ToString());
}
}
private static void SelectFromFile(string filepath)
{
try
{
FileInfo fi = new FileInfo(filepath);
List<GameObject> rootObjects = new List<GameObject>();
for (int i = 0; i < SceneManager.sceneCount; i++)
{
Scene scene = SceneManager.GetSceneAt(i);
if (scene.isLoaded)
rootObjects.AddRange(scene.GetRootGameObjects());
}
List<GameObject> selected = new List<GameObject>();
List<string> missing = new List<string>();
if (fi.Exists)
{
string[] entries = File.ReadAllLines(filepath);
foreach (string e in entries)
{
bool found = false;
for (int i = 0; i < rootObjects.Count; i++)
{
GameObject go = rootObjects[i];
if (e.Equals(go.name))
{
selected.Add(go);
found = true;
break;
}
else if (e.StartsWith(go.name + TransformExtensions.TransformPathSeparatorChar))
{
string childHierarchyPath = e.Replace(go.name + TransformExtensions.TransformPathSeparatorChar, "");
Transform foundChild = go.transform.Find(childHierarchyPath);
if (foundChild != null && !selected.Contains(foundChild.gameObject))
{
selected.Add(foundChild.gameObject);
found = true;
break;
}
}
}
if (!found)
{
missing.Add(e);
}
}
}
string output = $"Loaded {fi.Name}{Environment.NewLine}=> Selected: {selected.Count}; Missing: {missing.Count}";
if (missing.Count > 0)
{
string unfoundOutput = string.Join(Environment.NewLine, missing);
output += Environment.NewLine + "List of missing:" + Environment.NewLine + unfoundOutput;
}
Debug.Log(output);
Selection.objects = selected.ToArray();
}
catch (Exception e)
{
Debug.LogError(e);
}
}
public struct MappedObject
{
// Fields
private GameObject gameObject;
private string path;
private Material material;
// Properties
public GameObject GameObject { get => gameObject; }
public string Path { get => path; }
public string MaterialName { get => material != null ? material.name : String.Empty; }
public MappedObject(GameObject gameObject, string path)
{
this.gameObject = gameObject;
this.path = path;
if (gameObject != null && gameObject.TryGetComponent<Renderer>(out Renderer renderer))
this.material = renderer.sharedMaterial;
else
this.material = null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment