diff --git a/Source/Editor/Modules/SceneEditingModule.cs b/Source/Editor/Modules/SceneEditingModule.cs index 255247671..e1db17eaf 100644 --- a/Source/Editor/Modules/SceneEditingModule.cs +++ b/Source/Editor/Modules/SceneEditingModule.cs @@ -245,6 +245,79 @@ namespace FlaxEditor.Modules } } + private static bool SelectActorsUsingAsset(Guid assetId, ref Guid id, Dictionary scannedAssets) + { + // Check for asset match or try to use cache + if (assetId == id) + return true; + if (scannedAssets.TryGetValue(id, out var result)) + return result; + if (id == Guid.Empty || !FlaxEngine.Content.GetAssetInfo(id, out var assetInfo)) + return false; + scannedAssets.Add(id, false); + + // Skip scene assets + if (assetInfo.TypeName == "FlaxEngine.SceneAsset") + return false; + + // Recursive check if this asset contains direct or indirect reference to the given asset + var asset = FlaxEngine.Content.Load(assetInfo.ID, 1000); + if (asset) + { + var references = asset.GetReferences(); + for (var i = 0; i < references.Length; i++) + { + if (SelectActorsUsingAsset(assetId, ref references[i], scannedAssets)) + { + scannedAssets[id] = true; + return true; + } + } + } + + return false; + } + + private static void SelectActorsUsingAsset(Guid assetId, SceneGraphNode node, List selection, Dictionary scannedAssets) + { + if (node is ActorNode actorNode && actorNode.Actor) + { + // To detect if this actor uses the given asset simply serialize it to json and check used asset ids + // TODO: check scripts too + var json = actorNode.Actor.ToJson(); + JsonAssetBase.GetReferences(json, out var ids); + for (var i = 0; i < ids.Length; i++) + { + if (SelectActorsUsingAsset(assetId, ref ids[i], scannedAssets)) + { + selection.Add(actorNode); + break; + } + } + } + + // Recursive check for children + for (int i = 0; i < node.ChildNodes.Count; i++) + SelectActorsUsingAsset(assetId, node.ChildNodes[i], selection, scannedAssets); + } + + /// + /// Selects the actors using the given asset. + /// + /// The asset ID. + /// if set to true will use additive mode, otherwise will clear previous selection. + public void SelectActorsUsingAsset(Guid assetId, bool additive = false) + { + // TODO: make it async action with progress + Profiler.BeginEvent("SelectActorsUsingAsset"); + var selection = new List(); + var scannedAssets = new Dictionary(); + SelectActorsUsingAsset(assetId, Editor.Scene.Root, selection, scannedAssets); + Profiler.EndEvent(); + + Select(selection, additive); + } + /// /// Spawns the specified actor to the game (with undo). /// diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs index 541517254..7da8f97f1 100644 --- a/Source/Editor/Windows/ContentWindow.ContextMenu.cs +++ b/Source/Editor/Windows/ContentWindow.ContextMenu.cs @@ -99,6 +99,7 @@ namespace FlaxEditor.Windows if (item is AssetItem assetItem) { cm.AddButton("Copy asset ID", () => Clipboard.Text = JsonSerializer.GetStringID(assetItem.ID)); + cm.AddButton("Select actors using this asset", () => Editor.SceneEditing.SelectActorsUsingAsset(assetItem.ID)); } if (Editor.CanExport(item.Path)) diff --git a/Source/Engine/Content/BinaryAsset.cpp b/Source/Engine/Content/BinaryAsset.cpp index f5e71886c..9d5659fdf 100644 --- a/Source/Engine/Content/BinaryAsset.cpp +++ b/Source/Engine/Content/BinaryAsset.cpp @@ -114,10 +114,7 @@ void BinaryAsset::Reimport() const void BinaryAsset::GetImportMetadata(String& path, String& username) const { if (Metadata.IsInvalid()) - { - LOG(Warning, "Missing asset metadata."); return; - } // Parse metadata and try to get import info rapidjson_flax::Document document; diff --git a/Source/Engine/Content/JsonAsset.cpp b/Source/Engine/Content/JsonAsset.cpp index b39fc95f7..91d79abcc 100644 --- a/Source/Engine/Content/JsonAsset.cpp +++ b/Source/Engine/Content/JsonAsset.cpp @@ -83,6 +83,15 @@ void FindIds(ISerializable::DeserializeStream& node, Array& output) } } +void JsonAssetBase::GetReferences(const StringAnsiView& json, Array& output) +{ + ISerializable::SerializeDocument document; + document.Parse(json.Get(), json.Length()); + if (document.HasParseError()) + return; + FindIds(document, output); +} + void JsonAssetBase::GetReferences(Array& output) const { if (Data == nullptr) diff --git a/Source/Engine/Content/JsonAsset.h b/Source/Engine/Content/JsonAsset.h index 4ab180717..8306b0edc 100644 --- a/Source/Engine/Content/JsonAsset.h +++ b/Source/Engine/Content/JsonAsset.h @@ -53,6 +53,15 @@ public: /// API_PROPERTY() String GetData() const; +#if USE_EDITOR + /// + /// Parses Json string to find any object references inside it. It can produce list of references to assets and/or scene objects. Supported only in Editor. + /// + /// The Json string. + /// The output list of object IDs references by the asset (appended, not cleared). + API_FUNCTION() static void GetReferences(const StringAnsiView& json, API_PARAM(Out) Array& output); +#endif + public: // [Asset]