- Mesh import now builds full node hierarchy and splits geometry by material, with robust normal/tangent handling - Meshlet generation supports material indices for correct assignment - Refactored texture cube map compression and mipmap handling - Updated asset handler registration to new namespace - Enabled asset reimport on import events - Improved code quality, resource management, and formatting
176 lines
5.6 KiB
C#
176 lines
5.6 KiB
C#
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using Ghost.Core.Utilities;
|
|
using Ghost.Editor.Core;
|
|
using Ghost.Editor.Core.Assets;
|
|
using Ghost.Editor.Core.Contracts;
|
|
using Ghost.Editor.Core.Utilities;
|
|
using Ghost.Editor.Models;
|
|
using Ghost.Engine;
|
|
using Microsoft.UI.Dispatching;
|
|
using System.Collections.ObjectModel;
|
|
|
|
namespace Ghost.Editor.ViewModels.Controls;
|
|
|
|
internal partial class ContentBrowserViewModel : ObservableObject
|
|
{
|
|
private readonly IInspectorService _inspectorService;
|
|
private readonly IAssetRegistry _assetRegistry;
|
|
private readonly DispatcherQueue _dispatcherQueue;
|
|
|
|
private readonly Dictionary<string, ExplorerItem> _pathToDirectoryItemMap = new();
|
|
private ExplorerItem? _selectedItem;
|
|
|
|
public ObservableCollection<ExplorerItem> Directories
|
|
{
|
|
get;
|
|
} = new();
|
|
|
|
public ObservableCollection<ExplorerItem> Files
|
|
{
|
|
get;
|
|
} = new();
|
|
|
|
public ExplorerItem? SelectedItem
|
|
{
|
|
get => _selectedItem;
|
|
set
|
|
{
|
|
// TODO: Resolve inspector by reading metadata from selected asset
|
|
_selectedItem = value;
|
|
}
|
|
}
|
|
|
|
public string CurrentDirectoryPath
|
|
{
|
|
get;
|
|
set => field = PathUtility.Normalize(value);
|
|
} = string.Empty;
|
|
|
|
public ContentBrowserViewModel(IInspectorService inspectorService, IAssetRegistry assetRegistry)
|
|
{
|
|
_inspectorService = inspectorService;
|
|
_assetRegistry = assetRegistry;
|
|
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
|
|
|
var assetsRootItem = new ExplorerItem(EditorApplication.ASSETS_FOLDER_NAME, EditorApplication.AssetsFolderPath, true);
|
|
LoadSubFolderRecursive(assetsRootItem);
|
|
|
|
Directories.Add(assetsRootItem);
|
|
|
|
_assetRegistry.OnAssetChanged += OnAssetChanged;
|
|
}
|
|
|
|
private void OnAssetChanged(object? sender, AssetChangedEventArgs e)
|
|
{
|
|
if (e.AssetPath.EndsWith(FileExtensions.META_FILE_EXTENSION))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var fullPath = PathUtility.Normalize(e.AssetPath);
|
|
var dirPath = Path.GetDirectoryName(fullPath);
|
|
|
|
if (string.Equals(dirPath, CurrentDirectoryPath, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
_dispatcherQueue.TryEnqueue(() =>
|
|
{
|
|
if (e.ChangeType == AssetChangeType.Created || e.ChangeType == AssetChangeType.Renamed)
|
|
{
|
|
if (e.ChangeType == AssetChangeType.Renamed && e.OldAssetPath != null)
|
|
{
|
|
var oldFullPath = PathUtility.Normalize(e.OldAssetPath);
|
|
var oldItem = Files.FirstOrDefault(f => string.Equals(f.Path, oldFullPath, StringComparison.OrdinalIgnoreCase));
|
|
if (oldItem != null) Files.Remove(oldItem);
|
|
}
|
|
|
|
if (!Files.Any(f => string.Equals(f.Path, fullPath, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
var isDir = Directory.Exists(fullPath);
|
|
var assetType = AssetType.Unknown;
|
|
if (!isDir)
|
|
{
|
|
var ext = Path.GetExtension(fullPath);
|
|
assetType = AssetHandlerRegistry.GetRuntimeAssetTypeByExtension(ext);
|
|
}
|
|
Files.Add(new ExplorerItem(Path.GetFileName(fullPath), fullPath, isDir, assetType));
|
|
}
|
|
}
|
|
else if (e.ChangeType == AssetChangeType.Deleted)
|
|
{
|
|
var item = Files.FirstOrDefault(f => string.Equals(f.Path, fullPath, StringComparison.OrdinalIgnoreCase));
|
|
if (item != null)
|
|
{
|
|
Files.Remove(item);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
private void LoadSubFolderRecursive(ExplorerItem parentItem)
|
|
{
|
|
foreach (var directory in Directory.EnumerateDirectories(parentItem.Path))
|
|
{
|
|
var item = new ExplorerItem(Path.GetFileName(directory), directory, true);
|
|
LoadSubFolderRecursive(item);
|
|
|
|
_pathToDirectoryItemMap[directory] = item;
|
|
|
|
parentItem.Children ??= new();
|
|
parentItem.Children.Add(item);
|
|
}
|
|
}
|
|
|
|
internal void NavigateToDirectory(string? path)
|
|
{
|
|
Files.Clear();
|
|
|
|
if (!Directory.Exists(path))
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (var directory in Directory.EnumerateDirectories(path))
|
|
{
|
|
var directoryItem = new ExplorerItem(Path.GetFileName(directory), directory, true);
|
|
Files.Add(directoryItem);
|
|
}
|
|
|
|
foreach (var file in Directory.EnumerateFiles(path))
|
|
{
|
|
if (file.EndsWith(FileExtensions.META_FILE_EXTENSION))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var ext = Path.GetExtension(file);
|
|
var assetType = AssetHandlerRegistry.GetRuntimeAssetTypeByExtension(ext);
|
|
|
|
var fileItem = new ExplorerItem(Path.GetFileName(file), file, false, assetType);
|
|
Files.Add(fileItem);
|
|
}
|
|
|
|
CurrentDirectoryPath = Path.GetFullPath(path);
|
|
}
|
|
|
|
internal (ExplorerItem?, int) OpenSelected()
|
|
{
|
|
if (SelectedItem == null)
|
|
{
|
|
return (null, 0);
|
|
}
|
|
|
|
if (SelectedItem.IsDirectory)
|
|
{
|
|
NavigateToDirectory(SelectedItem.Path);
|
|
SelectedItem = _pathToDirectoryItemMap[SelectedItem.Path];
|
|
return (SelectedItem, 0);
|
|
}
|
|
else
|
|
{
|
|
// _assetRegistry.OpenAsset(SelectedItem.FullName);
|
|
return (null, 1);
|
|
}
|
|
}
|
|
}
|