From fdf680508dd99c982d26a96f09b9fd065bf3ba13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Sun, 1 Jan 2023 18:25:36 +0100 Subject: [PATCH] Don't recreate the entire asset tree --- .../src/EditWindows/ContentBrowserWindow.bf | 2 + GlitchyEditor/src/EditorContentManager.bf | 205 +++++++++++------- GlitchyEngine/src/Collections/TreeNode.bf | 5 + 3 files changed, 139 insertions(+), 73 deletions(-) diff --git a/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf b/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf index 6e90727..8a13af0 100644 --- a/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf +++ b/GlitchyEditor/src/EditWindows/ContentBrowserWindow.bf @@ -111,6 +111,8 @@ namespace GlitchyEditor.EditWindows ImGui.Style* style = ImGui.GetStyle(); + // TODO: crashes when _currentDirectoryNode was deleted, obviously + float window_visible_x2 = ImGui.GetWindowPos().x + ImGui.GetWindowContentRegionMax().x; for (var entry in _currentDirectoryNode.Children) { diff --git a/GlitchyEditor/src/EditorContentManager.bf b/GlitchyEditor/src/EditorContentManager.bf index daece58..fdec24b 100644 --- a/GlitchyEditor/src/EditorContentManager.bf +++ b/GlitchyEditor/src/EditorContentManager.bf @@ -103,7 +103,7 @@ class EditorContentManager : IContentManager { // TODO: Get from workspace //const String ContentDirectory = "./content"; - + FileSystemWatcher fsw ~ { _.StopRaisingEvents(); delete _; @@ -111,7 +111,7 @@ class EditorContentManager : IContentManager bool _fileSystemDirty = false; - internal TreeNode _assetHierarchy = null /*new TreeNode()*/ ~ DeleteTreeAndChildren!(_); + internal TreeNode _assetHierarchy = null ~ DeleteTreeAndChildren!(_); private append String _contentDirectory = .(); @@ -134,6 +134,53 @@ class EditorContentManager : IContentManager SetupFileSystemWatcher(); } + private TreeNode FileFileNode(StringView filePath) + { + //String relativeFilePath = scope String(filePath.Length); + + //Path.GetRelativePath(filePath, ContentDirectory, relativeFilePath); + + // Having a Path -> AssetNode dictionary would make this a lot easier! + + TreeNode walker = _assetHierarchy; + + Log.EngineLogger.AssertDebug(filePath.StartsWith(walker->Path), "filePath not in walker :("); + + Walking: while (true) + { + for (TreeNode child in walker.Children) + { + if (filePath.StartsWith(child->Path)) + { + walker = child; + + if (walker->Path == filePath) + return walker; + + continue Walking; + } + } + + // If we make it here, no child matched + Runtime.FatalError("Failed to find treeNode for given path"); + //Log.EngineLogger.Assert(false, "Failed to find ") + } + + /*// Check whether the new file is a directory of a file. + if (Directory.Exists(filePath)) + { + + } + else if (File.Exists(filePath)) + { + + } + else + { + Log.EngineLogger.Assert(false, scope $"The given path \"{filePath}\" doesn't exist."); + }*/ + } + /// Initializes the FSW for the current ContentDirectory and registers the events. private void SetupFileSystemWatcher() { @@ -142,18 +189,28 @@ class EditorContentManager : IContentManager fsw.IncludeSubdirectories = true; fsw.OnChanged.Add(new (filename) => { - _fileSystemDirty = true; + // Note: Gets fired for a directory if a file inside it is created/removed + + Log.EngineLogger.Trace($"File content changed (\"{filename}\")"); + //_fileSystemDirty = true; + // TODO: Handle file changes (reload asset, etc...) }); fsw.OnCreated.Add(new (filename) => { + Log.EngineLogger.Trace($"File created (\"{filename}\")"); + _fileSystemDirty = true; }); fsw.OnDeleted.Add(new (filename) => { + Log.EngineLogger.Trace($"File deleted (\"{filename}\")"); + _fileSystemDirty = true; }); fsw.OnRenamed.Add(new (newName, oldName) => { + Log.EngineLogger.Trace($"File renamed (From \"{oldName}\" to \"{newName}\")"); + _fileSystemDirty = true; }); @@ -180,8 +237,7 @@ class EditorContentManager : IContentManager /// Rebuilds the asset file hierarchy. private void UpdateFiles() { - // TODO: we don't need to rebuild the entire tree! - //DeleteTreeAndChildren!(_assetHierarchy); + Log.EngineLogger.Trace($"Updating asset hierarchy"); if (_assetHierarchy == null) { @@ -190,118 +246,121 @@ class EditorContentManager : IContentManager _assetHierarchy = new TreeNode(new AssetNode()); _assetHierarchy->Path = new String(ContentDirectory); _assetHierarchy->Name = new String("Content"); + + Log.EngineLogger.Trace($"Created directory node for: \"{_assetHierarchy->Path}\""); } - String str = scope .(ContentDirectory); - - /*void GrabSubAssets(TreeNode assetNode) - { - // TODO: Dedicated asset loaders that register their extensions, etc....! - - if (assetNode->Name.EndsWith(".png", .OrdinalIgnoreCase)) - { - assetNode->SubAssets = new:allocator List(); - assetNode->SubAssets.Add(new:allocator Asset() - { - Asset = assetNode.Value, - Name = new:allocator String(assetNode->Name) - }); - } - else if (assetNode->Name.EndsWith(".gltf", .OrdinalIgnoreCase) || assetNode->Name.EndsWith(".glb", .OrdinalIgnoreCase)) - { - List meshNames = scope List(); - ModelLoader.GetMeshNames(assetNode->Path, meshNames); - - assetNode->SubAssets = new:allocator List(); - - for (var meshName in meshNames) - { - assetNode->SubAssets.Add(new:allocator Asset() - { - Asset = assetNode.Value, - // Pass ownership of meshName to SubAsset -> save a copy and delete - Name = meshName - }); - } - } - }*/ - void HandleFile(AssetNode node) { node.AssetFile = new AssetFile(this, node.Path, node.IsDirectory); } + /// Determines the files that belong to the given directory and adds them to the tree. void AddFilesOfDirectory(TreeNode directory) { + // Filter that accepts all files. String filter = scope $"{directory->Path}/*"; - - String buffer = scope String(); - String extensionBuffer = scope String(); + + // Buffer used to hold the path of the files iterated below. + String filepathBuffer = scope String(256); + // Buffer used to hold the file extension of the files iterated below. + String extensionBuffer = scope String(16); for (var entry in Directory.Enumerate(filter, .Files)) { - entry.GetFilePath(buffer..Clear()); + entry.GetFilePath(filepathBuffer..Clear()); - Path.GetExtension(buffer, extensionBuffer..Clear()); + Path.GetExtension(filepathBuffer, .. extensionBuffer..Clear()); - // Don't add meta-files - if (extensionBuffer == ".ass") + // Ignore meta files. + if (extensionBuffer.Equals(AssetFile.ConfigFileExtension, .OrdinalIgnoreCase)) continue; - AssetNode e = new AssetNode(); - e.Name = new String(); - Path.GetFileName(buffer, e.Name); + TreeNode treeNode = directory.Children.Where(scope (node) => node.Value.Path == filepathBuffer).FirstOrDefault(); - e.Path = new String(buffer); + if (treeNode == null) + { + AssetNode assetNode = new AssetNode(); + assetNode.Name = new String(); + Path.GetFileName(filepathBuffer, assetNode.Name); + + assetNode.Path = new String(filepathBuffer); + assetNode.IsDirectory = false; + + treeNode = directory.AddChild(assetNode); - e.IsDirectory = entry.IsDirectory; + //GrabSubAssets(node); + HandleFile(treeNode.Value); - var node = directory.AddChild(e); - //GrabSubAssets(node); - HandleFile(node.Value); + Log.EngineLogger.Trace($"Created file node for: \"{assetNode.Path}\""); + } } } + void RemoveOrphanedEntries(TreeNode node) + { + for (TreeNode child in node.Children) + { + if (!Directory.Exists(child->Path) && !File.Exists(child->Path)) + { + Log.EngineLogger.Trace($"Removed orphaned node for: \"{child->Path}\""); + + @child.Remove(); + + DeleteTreeAndChildren!(child); + } + } + } + + /// Adds the given directory to the specified tree. + /// Recursively adds all Files and Subdirectories. void AddDirectoryToTree(String path, TreeNode parentNode) { - bool b (AssetNode node) + // Try to find the node for the specified path in the given parent + TreeNode treeNode = parentNode.Children.Where(scope (node) => node.Value.Path == path).FirstOrDefault(); + + // Create new Node for the Directory, if no TreeNode exists. + if (treeNode == null) { - return false; + AssetNode assetNode = new AssetNode(); + assetNode.Path = new String(path); + assetNode.Name = new String(); + assetNode.IsDirectory = true; + Path.GetFileName(assetNode.Path, assetNode.Name); + treeNode = parentNode.AddChild(assetNode); + + Log.EngineLogger.Trace($"Created directory node for: \"{assetNode.Path}\""); } - //void V() : - - var v = parentNode.Children.First(); - - AssetNode node = new AssetNode(); - node.Path = new String(path); - node.Name = new String(); - node.IsDirectory = true; - - Path.GetFileName(node.Path, node.Name); - - var newNode = parentNode.AddChild(node); + String directoryNameBuffer = scope String(256); + // Filter that finds all entries of a directory. String filter = scope $"{path}/*"; for (var directory in Directory.Enumerate(filter, .Directories)) { - directory.GetFilePath(str..Clear()); + directory.GetFilePath(directoryNameBuffer..Clear()); - AddDirectoryToTree(str, newNode); + AddDirectoryToTree(directoryNameBuffer, treeNode); } - AddFilesOfDirectory(newNode); + AddFilesOfDirectory(treeNode); + + RemoveOrphanedEntries(treeNode); } String filter = scope $"{ContentDirectory}/*"; + + String directoryNameBuffer = scope String(256); for (var directory in Directory.Enumerate(filter, .Directories)) { - directory.GetFilePath(str..Clear()); + directory.GetFilePath(directoryNameBuffer..Clear()); - AddDirectoryToTree(str, _assetHierarchy); + AddDirectoryToTree(directoryNameBuffer, _assetHierarchy); } + + RemoveOrphanedEntries(_assetHierarchy); _fileSystemDirty = false; } diff --git a/GlitchyEngine/src/Collections/TreeNode.bf b/GlitchyEngine/src/Collections/TreeNode.bf index 2324f36..e933631 100644 --- a/GlitchyEngine/src/Collections/TreeNode.bf +++ b/GlitchyEngine/src/Collections/TreeNode.bf @@ -2,6 +2,11 @@ using System.Collections; namespace GlitchyEngine.Collections { + // TODO: TreeNode is very bare minimum + // Destructor? + // RemoveChild? + // Remove in enumerator? + public class TreeNode { public T Value;