diff --git a/Source/Editor/Gizmo/TransformGizmo.cs b/Source/Editor/Gizmo/TransformGizmo.cs index ef7558b8d..4a3fa39ba 100644 --- a/Source/Editor/Gizmo/TransformGizmo.cs +++ b/Source/Editor/Gizmo/TransformGizmo.cs @@ -111,7 +111,8 @@ namespace FlaxEditor.Gizmo if (isSelected) { GetSelectedObjectsBounds(out var selectionBounds, out _); - ray.Position = ray.GetPoint(selectionBounds.Size.Y * 0.5f); + var offset = Mathf.Max(selectionBounds.Size.Y * 0.5f, 1.0f); + ray.Position = ray.GetPoint(offset); continue; } diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 8c6f5dc06..4a8c11176 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -386,6 +386,7 @@ namespace FlaxEditor.Windows { _viewport.Bounds = new Rectangle(Width * (1 - scaleWidth) / 2, 0, Width * scaleWidth, Height); } + _viewport.SyncBackbufferSize(); PerformLayout(); } diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index 268c22581..372505795 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -391,6 +391,25 @@ namespace FlaxEditor.Windows } Editor.Log("Plugin project has been cloned."); + + try + { + // Start git submodule clone + var settings = new CreateProcessSettings + { + FileName = "git", + WorkingDirectory = clonePath, + Arguments = "submodule update --init", + ShellExecute = false, + LogOutput = true, + }; + Platform.CreateProcess(ref settings); + } + catch (Exception e) + { + Editor.LogError($"Failed Git submodule process. {e}"); + return; + } // Find project config file. Could be different then what the user named the folder. var files = Directory.GetFiles(clonePath); diff --git a/Source/Engine/AI/Behavior.cpp b/Source/Engine/AI/Behavior.cpp index a5b24fc8b..98fb9ba22 100644 --- a/Source/Engine/AI/Behavior.cpp +++ b/Source/Engine/AI/Behavior.cpp @@ -114,14 +114,19 @@ void Behavior::UpdateAsync() void Behavior::StartLogic() { + if (_result == BehaviorUpdateResult::Running) + return; PROFILE_CPU(); - // Ensure to have tree loaded on begin play + // Ensure to have tree loaded on play CHECK(Tree && !Tree->WaitForLoaded()); BehaviorTree* tree = Tree.Get(); CHECK(tree->Graph.Root); + // Setup state _result = BehaviorUpdateResult::Running; + _accumulatedTime = 0.0f; + _totalTime = 0; // Init knowledge _knowledge.InitMemory(tree); @@ -135,6 +140,7 @@ void Behavior::StopLogic(BehaviorUpdateResult result) _accumulatedTime = 0.0f; _totalTime = 0; _result = result; + _knowledge.FreeMemory(); } void Behavior::ResetLogic() @@ -170,7 +176,11 @@ void Behavior::OnDisable() bool Behavior::GetNodeDebugRelevancy(const BehaviorTreeNode* node, const Behavior* behavior) { - return node && behavior && node->_executionIndex != -1 && behavior->_knowledge.RelevantNodes.Get(node->_executionIndex); + return node && + behavior && + node->_executionIndex >= 0 && + node->_executionIndex < behavior->_knowledge.RelevantNodes.Count() && + behavior->_knowledge.RelevantNodes.Get(node->_executionIndex); } String Behavior::GetNodeDebugInfo(const BehaviorTreeNode* node, Behavior* behavior) @@ -179,7 +189,7 @@ String Behavior::GetNodeDebugInfo(const BehaviorTreeNode* node, Behavior* behavi return String::Empty; BehaviorUpdateContext context; Platform::MemoryClear(&context, sizeof(context)); - if (behavior && node->_executionIndex != -1 && behavior->_knowledge.RelevantNodes.Get(node->_executionIndex)) + if (GetNodeDebugRelevancy(node, behavior)) { // Pass behavior and knowledge data only for relevant nodes to properly access it context.Behavior = behavior; diff --git a/Source/Engine/AI/BehaviorKnowledge.cpp b/Source/Engine/AI/BehaviorKnowledge.cpp index ffc011818..a73a1cfd4 100644 --- a/Source/Engine/AI/BehaviorKnowledge.cpp +++ b/Source/Engine/AI/BehaviorKnowledge.cpp @@ -83,7 +83,7 @@ bool AccessVariant(Variant& instance, const StringAnsiView& member, Variant& val } } #endif - else + else if (typeName.HasChars()) { LOG(Warning, "Missing scripting type \'{0}\'", String(typeName)); } diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 5cceb31e3..6f0d7661b 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -1342,7 +1342,12 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu { const bool xAxis = Math::IsZero(v0.X) && Math::IsZero(v1.X); const bool yAxis = Math::IsZero(v0.Y) && Math::IsZero(v1.Y); - if (xAxis || yAxis) + if (xAxis && yAxis) + { + // Single animation + value = SampleAnimation(node, loop, data.Length, startTimePos, bucket.TimePosition, newTimePos, aAnim, aData.W); + } + else if (xAxis || yAxis) { if (yAxis) { diff --git a/Source/Engine/Content/BinaryAsset.cpp b/Source/Engine/Content/BinaryAsset.cpp index b04603a96..9519f73df 100644 --- a/Source/Engine/Content/BinaryAsset.cpp +++ b/Source/Engine/Content/BinaryAsset.cpp @@ -323,26 +323,29 @@ bool BinaryAsset::SaveToAsset(const StringView& path, AssetInitData& data, bool { // Ensure path is in a valid format String pathNorm(path); - FileSystem::NormalizePath(pathNorm); + ContentStorageManager::FormatPath(pathNorm); + const StringView filePath = pathNorm; // Find target storage container and the asset - auto storage = ContentStorageManager::TryGetStorage(pathNorm); - auto asset = Content::GetAsset(pathNorm); + auto storage = ContentStorageManager::TryGetStorage(filePath); + auto asset = Content::GetAsset(filePath); auto binaryAsset = dynamic_cast(asset); if (asset && !binaryAsset) { LOG(Warning, "Cannot write to the non-binary asset location."); return true; } + if (!binaryAsset && !storage && FileSystem::FileExists(filePath)) + { + // Force-resolve storage (asset at that path could be not yet loaded into registry) + storage = ContentStorageManager::GetStorage(filePath); + } // Check if can perform write operation to the asset container - if (storage) + if (storage && !storage->AllowDataModifications()) { - if (!storage->AllowDataModifications()) - { - LOG(Warning, "Cannot write to the asset storage container."); - return true; - } + LOG(Warning, "Cannot write to the asset storage container."); + return true; } // Initialize data container @@ -352,6 +355,11 @@ bool BinaryAsset::SaveToAsset(const StringView& path, AssetInitData& data, bool // Use the same asset ID data.Header.ID = binaryAsset->GetID(); } + else if (storage && storage->GetEntriesCount()) + { + // Use the same file ID + data.Header.ID = storage->GetEntry(0).ID; + } else { // Randomize ID @@ -373,8 +381,8 @@ bool BinaryAsset::SaveToAsset(const StringView& path, AssetInitData& data, bool } else { - ASSERT(pathNorm.HasChars()); - result = FlaxStorage::Create(pathNorm, data, silentMode); + ASSERT(filePath.HasChars()); + result = FlaxStorage::Create(filePath, data, silentMode); } if (binaryAsset) binaryAsset->_isSaving = false; diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp index bd27abc5a..982ae599f 100644 --- a/Source/Engine/Content/Content.cpp +++ b/Source/Engine/Content/Content.cpp @@ -54,8 +54,7 @@ namespace // Assets CriticalSection AssetsLocker; Dictionary Assets(2048); - CriticalSection LoadCallAssetsLocker; - Array LoadCallAssets(64); + Array LoadCallAssets(PLATFORM_THREADS_LIMIT); CriticalSection LoadedAssetsToInvokeLocker; Array LoadedAssetsToInvoke(64); Array ToUnload; @@ -449,18 +448,19 @@ Asset* Content::LoadAsync(const StringView& path, const ScriptingTypeHandle& typ { // Ensure path is in a valid format String pathNorm(path); - StringUtils::PathRemoveRelativeParts(pathNorm); + ContentStorageManager::FormatPath(pathNorm); + const StringView filePath = pathNorm; #if USE_EDITOR - if (!FileSystem::FileExists(pathNorm)) + if (!FileSystem::FileExists(filePath)) { - LOG(Error, "Missing file \'{0}\'", pathNorm); + LOG(Error, "Missing file \'{0}\'", filePath); return nullptr; } #endif AssetInfo assetInfo; - if (GetAssetInfo(pathNorm, assetInfo)) + if (GetAssetInfo(filePath, assetInfo)) { return LoadAsync(assetInfo.ID, type); } @@ -910,9 +910,13 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type) return nullptr; // Check if asset has been already loaded - Asset* result = GetAsset(id); + Asset* result = nullptr; + AssetsLocker.Lock(); + Assets.TryGet(id, result); if (result) { + AssetsLocker.Unlock(); + // Validate type if (IsAssetTypeIdInvalid(type, result->GetTypeHandle()) && !result->Is(type)) { @@ -923,57 +927,41 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type) } // Check if that asset is during loading - LoadCallAssetsLocker.Lock(); if (LoadCallAssets.Contains(id)) { - LoadCallAssetsLocker.Unlock(); + AssetsLocker.Unlock(); - // Wait for load end - // TODO: dont use active waiting and prevent deadlocks if running on a main thread - //while (!Engine::ShouldExit()) - while (true) + // Wait for loading end by other thread + bool contains = true; + while (contains) { - LoadCallAssetsLocker.Lock(); - const bool contains = LoadCallAssets.Contains(id); - LoadCallAssetsLocker.Unlock(); - if (!contains) - return GetAsset(id); Platform::Sleep(1); + AssetsLocker.Lock(); + contains = LoadCallAssets.Contains(id); + AssetsLocker.Unlock(); } - } - else - { - // Mark asset as loading - LoadCallAssets.Add(id); - LoadCallAssetsLocker.Unlock(); + Assets.TryGet(id, result); + return result; } - // Load asset - AssetInfo assetInfo; - result = load(id, type, assetInfo); + // Mark asset as loading and release lock so other threads can load other assets + LoadCallAssets.Add(id); + AssetsLocker.Unlock(); - // End loading - LoadCallAssetsLocker.Lock(); - LoadCallAssets.Remove(id); - LoadCallAssetsLocker.Unlock(); +#define LOAD_FAILED() AssetsLocker.Lock(); LoadCallAssets.Remove(id); AssetsLocker.Unlock(); return nullptr - return result; -} - -Asset* Content::load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo& assetInfo) -{ // Get cached asset info (from registry) + AssetInfo assetInfo; if (!GetAssetInfo(id, assetInfo)) { LOG(Warning, "Invalid or missing asset ({0}, {1}).", id, type.ToString()); - return nullptr; + LOAD_FAILED(); } - #if ASSETS_LOADING_EXTRA_VERIFICATION if (!FileSystem::FileExists(assetInfo.Path)) { LOG(Error, "Cannot find file '{0}'", assetInfo.Path); - return nullptr; + LOAD_FAILED(); } #endif @@ -982,28 +970,27 @@ Asset* Content::load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo& if (factory == nullptr) { LOG(Error, "Cannot find asset factory. Info: {0}", assetInfo.ToString()); - return nullptr; + LOAD_FAILED(); } // Create asset object - auto result = factory->New(assetInfo); + result = factory->New(assetInfo); if (result == nullptr) { LOG(Error, "Cannot create asset object. Info: {0}", assetInfo.ToString()); - return nullptr; + LOAD_FAILED(); } - + ASSERT(result->GetID() == id); #if ASSETS_LOADING_EXTRA_VERIFICATION if (IsAssetTypeIdInvalid(type, result->GetTypeHandle()) && !result->Is(type)) { - LOG(Error, "Different loaded asset type! Asset: '{0}'. Expected type: {1}", assetInfo.ToString(), type.ToString()); + LOG(Warning, "Different loaded asset type! Asset: '{0}'. Expected type: {1}", assetInfo.ToString(), type.ToString()); result->DeleteObject(); - return nullptr; + LOAD_FAILED(); } #endif // Register asset - ASSERT(result->GetID() == id); AssetsLocker.Lock(); #if ASSETS_LOADING_EXTRA_VERIFICATION ASSERT(!Assets.ContainsKey(id)); @@ -1011,11 +998,14 @@ Asset* Content::load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo& Assets.Add(id, result); // Start asset loading - // TODO: refactor this to create asset loading task-chain before AssetsLocker.Lock() to allow better parallelization result->startLoading(); + // Remove from the loading queue and release lock + LoadCallAssets.Remove(id); AssetsLocker.Unlock(); +#undef LOAD_FAILED + return result; } diff --git a/Source/Engine/Content/Content.h b/Source/Engine/Content/Content.h index 6393ce48b..cce57194c 100644 --- a/Source/Engine/Content/Content.h +++ b/Source/Engine/Content/Content.h @@ -366,7 +366,6 @@ private: static void onAssetLoaded(Asset* asset); static void onAssetUnload(Asset* asset); static void onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId); - static Asset* load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo& assetInfo); private: static void deleteFileSafety(const StringView& path, const Guid& id); diff --git a/Source/Engine/Content/Storage/ContentStorageManager.cpp b/Source/Engine/Content/Storage/ContentStorageManager.cpp index d3e18e9d0..61e73a3f2 100644 --- a/Source/Engine/Content/Storage/ContentStorageManager.cpp +++ b/Source/Engine/Content/Storage/ContentStorageManager.cpp @@ -6,6 +6,7 @@ #include "Engine/Core/Log.h" #include "Engine/Engine/Engine.h" #include "Engine/Engine/EngineService.h" +#include "Engine/Engine/Globals.h" #include "Engine/Platform/FileSystem.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Threading/TaskGraph.h" @@ -185,6 +186,16 @@ void ContentStorageManager::EnsureUnlocked() Locker.Unlock(); } +void ContentStorageManager::FormatPath(String& path) +{ + StringUtils::PathRemoveRelativeParts(path); + if (FileSystem::IsRelative(path)) + { + // Convert local-project paths into absolute format which is used by Content Storage system + path = Globals::ProjectFolder / path; + } +} + bool ContentStorageManager::IsFlaxStoragePath(const String& path) { auto extension = FileSystem::GetExtension(path).ToLower(); diff --git a/Source/Engine/Content/Storage/ContentStorageManager.h b/Source/Engine/Content/Storage/ContentStorageManager.h index c615632e9..84a6dc07e 100644 --- a/Source/Engine/Content/Storage/ContentStorageManager.h +++ b/Source/Engine/Content/Storage/ContentStorageManager.h @@ -75,6 +75,9 @@ public: /// static void EnsureUnlocked(); + // Formats path into valid format used by the storage system (normalized and absolute). + static void FormatPath(String& path); + public: /// /// Determines whether the specified path can be a binary asset file (based on it's extension). diff --git a/Source/Engine/Graphics/RenderTask.cpp b/Source/Engine/Graphics/RenderTask.cpp index 48e89f59e..8a10a0444 100644 --- a/Source/Engine/Graphics/RenderTask.cpp +++ b/Source/Engine/Graphics/RenderTask.cpp @@ -8,11 +8,12 @@ #include "Engine/Core/Collections/Sorting.h" #include "Engine/Debug/DebugLog.h" #include "Engine/Level/Level.h" +#include "Engine/Level/Scene/Scene.h" #include "Engine/Level/Actors/Camera.h" +#include "Engine/Level/Actors/PostFxVolume.h" #include "Engine/Renderer/Renderer.h" #include "Engine/Render2D/Render2D.h" #include "Engine/Engine/Engine.h" -#include "Engine/Level/Actors/PostFxVolume.h" #include "Engine/Profiler/Profiler.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Threading/Threading.h" @@ -202,15 +203,21 @@ void SceneRenderTask::CollectPostFxVolumes(RenderContext& renderContext) { Level::CollectPostFxVolumes(renderContext); } - if (EnumHasAllFlags(ActorsSource , ActorsSources::CustomActors)) + if (EnumHasAllFlags(ActorsSource, ActorsSources::CustomActors)) { for (Actor* a : CustomActors) { auto* postFxVolume = dynamic_cast(a); if (postFxVolume && a->GetIsActive()) - { postFxVolume->Collect(renderContext); - } + } + } + if (EnumHasAllFlags(ActorsSource, ActorsSources::CustomScenes)) + { + for (Scene* scene : CustomScenes) + { + if (scene && scene->IsActiveInHierarchy()) + scene->Rendering.CollectPostFxVolumes(renderContext); } } } @@ -282,6 +289,14 @@ void SceneRenderTask::OnCollectDrawCalls(RenderContextBatch& renderContextBatch, ASSERT_LOW_LAYER(_customActorsScene); _customActorsScene->Draw(renderContextBatch, (SceneRendering::DrawCategory)category); } + if (EnumHasAllFlags(ActorsSource, ActorsSources::CustomScenes)) + { + for (Scene* scene : CustomScenes) + { + if (scene && scene->IsActiveInHierarchy()) + scene->Rendering.Draw(renderContextBatch, (SceneRendering::DrawCategory)category); + } + } if (EnumHasAllFlags(ActorsSource, ActorsSources::Scenes)) { Level::DrawActors(renderContextBatch, category); diff --git a/Source/Engine/Graphics/RenderTask.h b/Source/Engine/Graphics/RenderTask.h index 9123751bc..864d95214 100644 --- a/Source/Engine/Graphics/RenderTask.h +++ b/Source/Engine/Graphics/RenderTask.h @@ -21,6 +21,7 @@ class PostProcessEffect; struct RenderContext; class Camera; class Actor; +class Scene; /// /// Allows to perform custom rendering using graphics pipeline. @@ -174,6 +175,11 @@ API_ENUM(Attributes="Flags") enum class ActorsSources /// CustomActors = 2, + /// + /// The scenes from the custom collection. + /// + CustomScenes = 4, + /// /// The actors from the loaded scenes and custom collection. /// @@ -267,9 +273,14 @@ public: public: /// - /// The custom set of actors to render. + /// The custom set of actors to render. Used when ActorsSources::CustomActors flag is active. /// - Array CustomActors; + API_FIELD() Array CustomActors; + + /// + /// The custom set of scenes to render. Used when ActorsSources::CustomScenes flag is active. + /// + API_FIELD() Array CustomScenes; /// /// Adds the custom actor to the rendering. diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 88f7e6876..6c67620ae 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -172,6 +172,27 @@ void AnimatedModel::GetNodeTransformation(const StringView& nodeName, Matrix& no GetNodeTransformation(SkinnedModel ? SkinnedModel->FindNode(nodeName) : -1, nodeTransformation, worldSpace); } +void AnimatedModel::SetNodeTransformation(int32 nodeIndex, const Matrix& nodeTransformation, bool worldSpace) +{ + if (GraphInstance.NodesPose.IsEmpty()) + const_cast(this)->PreInitSkinningData(); // Ensure to have valid nodes pose to return + CHECK(nodeIndex >= 0 && nodeIndex < GraphInstance.NodesPose.Count()); + GraphInstance.NodesPose[nodeIndex] = nodeTransformation; + if (worldSpace) + { + Matrix world; + _transform.GetWorld(world); + Matrix invWorld; + Matrix::Invert(world, invWorld); + GraphInstance.NodesPose[nodeIndex] = GraphInstance.NodesPose[nodeIndex] * invWorld; + } + OnAnimationUpdated(); +} +void AnimatedModel::SetNodeTransformation(const StringView& nodeName, const Matrix& nodeTransformation, bool worldSpace) +{ + SetNodeTransformation(SkinnedModel ? SkinnedModel->FindNode(nodeName) : -1, nodeTransformation, worldSpace); +} + int32 AnimatedModel::FindClosestNode(const Vector3& location, bool worldSpace) const { if (GraphInstance.NodesPose.IsEmpty()) diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index 0c5c4d73b..029e17b62 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -229,6 +229,22 @@ public: /// True if convert matrices into world-space, otherwise returned values will be in local-space of the actor. API_FUNCTION() void GetNodeTransformation(const StringView& nodeName, API_PARAM(Out) Matrix& nodeTransformation, bool worldSpace = false) const; + /// + /// Sets the node final transformation. If multiple nodes are to be set within a frame, do not use set worldSpace to true, and do the conversion yourself to avoid recalculation of inv matrices. + /// + /// The index of the skinned model skeleton node. + /// The final node transformation matrix. + /// True if convert matrices from world-space, otherwise values will be in local-space of the actor. + API_FUNCTION() void SetNodeTransformation(int32 nodeIndex, const Matrix& nodeTransformation, bool worldSpace = false); + + /// + /// Sets the node final transformation. If multiple nodes are to be set within a frame, do not use set worldSpace to true, and do the conversion yourself to avoid recalculation of inv matrices. + /// + /// The name of the skinned model skeleton node. + /// The final node transformation matrix. + /// True if convert matrices from world-space, otherwise values will be in local-space of the actor. + API_FUNCTION() void SetNodeTransformation(const StringView& nodeName, const Matrix& nodeTransformation, bool worldSpace = false); + /// /// Finds the closest node to a given location. /// diff --git a/Source/Engine/Platform/Win32/Win32CriticalSection.h b/Source/Engine/Platform/Win32/Win32CriticalSection.h index 95d85efac..97221626c 100644 --- a/Source/Engine/Platform/Win32/Win32CriticalSection.h +++ b/Source/Engine/Platform/Win32/Win32CriticalSection.h @@ -28,7 +28,7 @@ public: /// Win32CriticalSection() { - Windows::InitializeCriticalSectionEx(&_criticalSection, 100, 0x01000000); + Windows::InitializeCriticalSectionEx(&_criticalSection, 4000, 0x01000000); } /// diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 991ff196b..63cf9d2f8 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -145,6 +145,20 @@ namespace Flax.Build.Bindings return $"(void*){result}"; } + + private static void GenerateCppAddFileReference(BuildData buildData, ApiTypeInfo caller, TypeInfo typeInfo, ApiTypeInfo apiType) + { + CppReferencesFiles.Add(apiType?.File); + if (typeInfo.GenericArgs != null) + { + for (int i = 0; i < typeInfo.GenericArgs.Count; i++) + { + var g = typeInfo.GenericArgs[i]; + GenerateCppAddFileReference(buildData, caller, g, FindApiTypeInfo(buildData, g, caller)); + } + } + } + public static string GenerateCppWrapperNativeToVariant(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller, string value) { if (typeInfo.Type == "Variant") @@ -640,15 +654,7 @@ namespace Flax.Build.Bindings // Register any API types usage apiType = FindApiTypeInfo(buildData, typeInfo, caller); - CppReferencesFiles.Add(apiType?.File); - if (typeInfo.GenericArgs != null) - { - for (int i = 0; i < typeInfo.GenericArgs.Count; i++) - { - var t = FindApiTypeInfo(buildData, typeInfo.GenericArgs[i], caller); - CppReferencesFiles.Add(t?.File); - } - } + GenerateCppAddFileReference(buildData, caller, typeInfo, apiType); // Use dynamic array as wrapper container for fixed-size native arrays if (typeInfo.IsArray) @@ -1795,15 +1801,7 @@ namespace Flax.Build.Bindings return true; // Add includes to properly compile bindings (eg. SoftObjectReference) - CppReferencesFiles.Add(apiTypeInfo?.File); - if (typeInfo.GenericArgs != null) - { - for (int i = 0; i < typeInfo.GenericArgs.Count; i++) - { - var t = FindApiTypeInfo(buildData, typeInfo.GenericArgs[i], caller); - CppReferencesFiles.Add(t?.File); - } - } + GenerateCppAddFileReference(buildData, caller, typeInfo, apiTypeInfo); return false; }