#if UNITY_EDITOR /******************************************************************************* The content of this file includes portions of the proprietary AUDIOKINETIC Wwise Technology released in source code form as part of the game integration package. The content of this file may not be used without valid licenses to the AUDIOKINETIC Wwise Technology. Note that the use of the game engine is subject to the Unity(R) Terms of Service at https://unity3d.com/legal/terms-of-service License Usage Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use this file in accordance with the end user license agreement provided with the software or, alternatively, in accordance with the terms contained in a written agreement between you and Audiokinetic Inc. Copyright (c) 2026 Audiokinetic Inc. *******************************************************************************/ using System.Linq; using System.Collections.Generic; using System.Collections.ObjectModel; using UnityEditor.IMGUI.Controls; using AK.Wwise.Unity.Logging; #if UNITY_6000_2_OR_NEWER using WwiseTreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem; using WwiseTreeViewState = UnityEditor.IMGUI.Controls.TreeViewState; #else using WwiseTreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem; using WwiseTreeViewState = UnityEditor.IMGUI.Controls.TreeViewState; #endif public enum BrowserFilter { None, SoundBanksUpToDate = 1 << 0, NewInWwise = 1 << 1, DeletedInWwise = 1 << 2, RenamedInWwise = 1 << 3, MovedInWwise = 1 << 4, SoundBankNeedsUpdate = 1 << 5, Event = 1 << 6, Bus = 1 << 7, SoundBank = 1 << 8, Switch = 1 << 9, State = 1 << 10, Trigger = 1 << 11, AcousticTexture = 1 << 12, GameParameter = 1 << 13 } public class WwiseTreeStringKey { string name; WwiseObjectType objectType; public WwiseTreeStringKey(string name, WwiseObjectType objectType) { this.name = name; this.objectType = objectType; } public override int GetHashCode() { return name.GetHashCode() | objectType.GetHashCode(); } public override bool Equals(object obj) { if (obj is WwiseTreeStringKey) { WwiseTreeStringKey other = (WwiseTreeStringKey)obj; return other.objectType == objectType && other.name == name; } return false; } } public abstract class AkWwiseTreeDataSource { public class TreeItems { public Dictionary ItemDict; public Dictionary ItemNameDict; public TreeItems() { ItemDict = new Dictionary(); ItemNameDict = new Dictionary(); } public void Clear() { ItemDict.Clear(); ItemNameDict.Clear(); } public void Add(AkWwiseTreeViewItem item) { try { ItemDict[item.objectGuid] = item; if (item.name != null) { ItemNameDict[new WwiseTreeStringKey(item.name, item.objectType)] = item; } } catch (System.ArgumentException e) { WwiseLogger.Error(e.Message); } } public void Remove(AkWwiseTreeViewItem item) { try { ItemDict.Remove(item.objectGuid); ItemNameDict.Remove(new WwiseTreeStringKey(item.name, item.objectType)); } catch (System.ArgumentException e) { WwiseLogger.Error(e.Message); } } } static public ReadOnlyDictionary FolderNames = new ReadOnlyDictionary(new Dictionary() { { WwiseObjectType.AuxBus, "Busses" }, { WwiseObjectType.Event, "Events" }, { WwiseObjectType.State, "States"}, { WwiseObjectType.StateGroup, "States"}, { WwiseObjectType.Soundbank, "SoundBanks"}, { WwiseObjectType.Switch, "Switches"}, { WwiseObjectType.SwitchGroup, "Switches"}, { WwiseObjectType.AcousticTexture, "Virtual Acoustics" }, { WwiseObjectType.Trigger, "Triggers" }, { WwiseObjectType.GameParameter, "Game Parameters" }, }); public TreeItems Data; public AkWwiseTreeViewItem ProjectRoot { get; protected set; } public Dictionary wwiseObjectFolders; public AkWwiseTreeViewItem SearchRoot { get; protected set; } public TreeItems SearchData; protected AkWwiseTreeView TreeView { get; set; } public event System.Action modelChanged; public virtual AkWwiseTreeViewItem CreateProjectRootItem() { return new AkWwiseTreeViewItem(System.IO.Path.GetFileNameWithoutExtension(AkWwiseEditorSettings.Instance.WwiseProjectPath), -1, GenerateUniqueID(), System.Guid.Empty, WwiseObjectType.Project); } protected int m_MaxID; public int GenerateUniqueID() { return ++m_MaxID; } public AkWwiseTreeDataSource() { Data = new TreeItems(); wwiseObjectFolders = new Dictionary(); ProjectRoot = CreateProjectRootItem(); } public WwiseTreeViewItem FindByIdRecursive(WwiseTreeViewItem item, int id) { if (item.id == id) { return item; } foreach (var element in item.children) { var found = FindByIdRecursive(element, id); if (found != null) { return found; } } return null; } public AkWwiseTreeViewItem FindById(int id) { if (m_MaxID < id) { return null; } foreach (var element in ProjectRoot.children) { var found = FindByIdRecursive(element, id); if (found != null) { return found as AkWwiseTreeViewItem; } } return Data.ItemDict.Values.FirstOrDefault(element => element.id == id); } public WwiseTreeViewItem FindByNameRecursive(WwiseTreeViewItem item, string name, WwiseObjectType objectType) { if ( (item as AkWwiseTreeViewItem).objectType == objectType && item.displayName == name) { return item; } foreach (var element in item.children) { var found = FindByNameRecursive(element, name, objectType); if (found != null) { return found; } } return null; } public AkWwiseTreeViewItem FindByName(string name, WwiseObjectType objectType) { var key = new WwiseTreeStringKey(name, objectType); if (Data.ItemNameDict.ContainsKey(key)) { return Data.ItemNameDict[key]; } return null; } public AkWwiseTreeViewItem FindByGuid(System.Guid guid) { return TreeUtility.FindByGuid(Data, guid); } public AkWwiseTreeViewItem FindInSearchResults(System.Guid guid) { return TreeUtility.FindByGuid(SearchData, guid); } public IEnumerable GetGuidsFromIds(IEnumerable ids) { if (Data.ItemDict.Count == 0) return new List(); return Data.ItemDict.Values.Where(el => ids.Contains(el.id)).Select(el => el.objectGuid); } public IEnumerable GetIdsFromGuids(IEnumerable guids) { if (Data.ItemDict.Count == 0) return new List(); return Data.ItemDict.Values.Where(el => guids.Contains(el.objectGuid)).Select(el => el.id); } public IList GetAncestors(int id) { var parents = new List(); WwiseTreeViewItem el = FindById(id); if (el != null) { while (el.parent != null) { parents.Add(el.parent.id); el = el.parent; } } return parents; } public IList GetDescendantsThatHaveChildren(int id) { AkWwiseTreeViewItem searchFromThis = FindById(id); if (searchFromThis != null) { return GetParentsBelowStackBased(searchFromThis); } return new List(); } public bool isRefreshing = false; IList GetParentsBelowStackBased(AkWwiseTreeViewItem searchFromThis) { Stack stack = new Stack(); stack.Push(searchFromThis); var parentsBelow = new List(); while (stack.Count > 0) { AkWwiseTreeViewItem current = stack.Pop(); if (current.hasChildren) { parentsBelow.Add(current.id); foreach (AkWwiseTreeViewItem el in current.children) { stack.Push(el); } } } return parentsBelow; } abstract public AkWwiseTreeViewItem GetComponentDataRoot(WwiseObjectType objectType); protected void Changed() { modelChanged?.Invoke(); } public bool IsExpanded(WwiseTreeViewState state, int id) { if (ProjectRoot != null && id == ProjectRoot.id) { return true; } return state.expandedIDs.BinarySearch(id) >= 0; } public abstract void FetchData(); public static int GetDepthFromPath(string path) { return path.Split('\\').Length - 1; } public virtual void ScheduleRebuild() { } public abstract void SaveExpansionStatus(List itemIds); public abstract List LoadExpansionSatus(); public string currentSearchString; public bool isSearching = false; public abstract AkWwiseTreeViewItem GetSearchResults(); public abstract void UpdateSearchResults(string searchString, WwiseObjectType objectType, BrowserFilter Filters); public virtual void SelectItem(System.Guid itemGuid) { bool success = TreeView.ExpandItem(itemGuid, true); if (!success) { UnityEditor.EditorApplication.delayCall += () => { SelectItem(itemGuid); }; } } public virtual void SetWwiseTreeView(AkWwiseTreeView treeView) { TreeView = treeView; } public virtual void LoadComponentData(WwiseObjectType objectType) { } public virtual void ItemSelected(AkWwiseTreeViewItem itemID) { } public virtual void SelectObjectInAuthoring(System.Guid objectGuid) { } public abstract void SetExpanded(IEnumerable ids); } #region Utility Functions public static class TreeUtility { public static void CopyTree(AkWwiseTreeViewItem sourceRoot, AkWwiseTreeViewItem destRoot) { for (int i = 0; i < sourceRoot.children.Count(); i++) { var currItem = sourceRoot.children[i]; var newItem = new AkWwiseTreeViewItem(currItem as AkWwiseTreeViewItem); CopyTree(currItem as AkWwiseTreeViewItem, newItem); destRoot.AddWwiseItemChild(newItem); } } public static void TreeToList(AkWwiseTreeViewItem root, ref AkWwiseTreeDataSource.TreeItems result) { if (root == null) return; if (result == null) return; result.Clear(); Stack stack = new Stack(); stack.Push(root); while (stack.Count > 0) { AkWwiseTreeViewItem current = stack.Pop(); result.Add(current); if (current.children != null && current.children.Count > 0) { for (int i = current.children.Count - 1; i >= 0; i--) { if (current.children[i] != null) { stack.Push((AkWwiseTreeViewItem)current.children[i]); } } } } } public static void SortTreeIfNecessary(AkWwiseTreeViewItem rootElement) { if (rootElement.hasChildren) { if (!rootElement.isSorted) { rootElement.SortChildren(); } foreach (AkWwiseTreeViewItem child in rootElement.children) { SortTreeIfNecessary(child); } } } public static AkWwiseTreeViewItem FindByGuid(IEnumerable data, System.Guid guid) { return data.FirstOrDefault(element => element.objectGuid == guid); } public static AkWwiseTreeViewItem FindByGuid(AkWwiseTreeDataSource.TreeItems data, System.Guid guid) { if (!data.ItemDict.ContainsKey(guid)) { return null; } return data.ItemDict[guid]; } } #endregion #endif