Merge branch 'FlaxEngine:master' into master

This commit is contained in:
nothingTVatYT
2023-11-24 11:28:58 +01:00
committed by GitHub
17 changed files with 198 additions and 90 deletions

View File

@@ -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;
}

View File

@@ -386,6 +386,7 @@ namespace FlaxEditor.Windows
{
_viewport.Bounds = new Rectangle(Width * (1 - scaleWidth) / 2, 0, Width * scaleWidth, Height);
}
_viewport.SyncBackbufferSize();
PerformLayout();
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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));
}

View File

@@ -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)
{

View File

@@ -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<BinaryAsset*>(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;

View File

@@ -54,8 +54,7 @@ namespace
// Assets
CriticalSection AssetsLocker;
Dictionary<Guid, Asset*> Assets(2048);
CriticalSection LoadCallAssetsLocker;
Array<Guid> LoadCallAssets(64);
Array<Guid> LoadCallAssets(PLATFORM_THREADS_LIMIT);
CriticalSection LoadedAssetsToInvokeLocker;
Array<Asset*> LoadedAssetsToInvoke(64);
Array<Asset*> 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;
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -75,6 +75,9 @@ public:
/// </summary>
static void EnsureUnlocked();
// Formats path into valid format used by the storage system (normalized and absolute).
static void FormatPath(String& path);
public:
/// <summary>
/// Determines whether the specified path can be a binary asset file (based on it's extension).

View File

@@ -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<PostFxVolume*>(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);

View File

@@ -21,6 +21,7 @@ class PostProcessEffect;
struct RenderContext;
class Camera;
class Actor;
class Scene;
/// <summary>
/// Allows to perform custom rendering using graphics pipeline.
@@ -174,6 +175,11 @@ API_ENUM(Attributes="Flags") enum class ActorsSources
/// </summary>
CustomActors = 2,
/// <summary>
/// The scenes from the custom collection.
/// </summary>
CustomScenes = 4,
/// <summary>
/// The actors from the loaded scenes and custom collection.
/// </summary>
@@ -267,9 +273,14 @@ public:
public:
/// <summary>
/// The custom set of actors to render.
/// The custom set of actors to render. Used when ActorsSources::CustomActors flag is active.
/// </summary>
Array<Actor*> CustomActors;
API_FIELD() Array<Actor*> CustomActors;
/// <summary>
/// The custom set of scenes to render. Used when ActorsSources::CustomScenes flag is active.
/// </summary>
API_FIELD() Array<Scene*> CustomScenes;
/// <summary>
/// Adds the custom actor to the rendering.

View File

@@ -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<AnimatedModel*>(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())

View File

@@ -229,6 +229,22 @@ public:
/// <param name="worldSpace">True if convert matrices into world-space, otherwise returned values will be in local-space of the actor.</param>
API_FUNCTION() void GetNodeTransformation(const StringView& nodeName, API_PARAM(Out) Matrix& nodeTransformation, bool worldSpace = false) const;
/// <summary>
/// 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.
/// </summary>
/// <param name="nodeIndex">The index of the skinned model skeleton node.</param>
/// <param name="nodeTransformation">The final node transformation matrix.</param>
/// <param name="worldSpace">True if convert matrices from world-space, otherwise values will be in local-space of the actor.</param>
API_FUNCTION() void SetNodeTransformation(int32 nodeIndex, const Matrix& nodeTransformation, bool worldSpace = false);
/// <summary>
/// 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.
/// </summary>
/// <param name="nodeName">The name of the skinned model skeleton node.</param>
/// <param name="nodeTransformation">The final node transformation matrix.</param>
/// <param name="worldSpace">True if convert matrices from world-space, otherwise values will be in local-space of the actor.</param>
API_FUNCTION() void SetNodeTransformation(const StringView& nodeName, const Matrix& nodeTransformation, bool worldSpace = false);
/// <summary>
/// Finds the closest node to a given location.
/// </summary>

View File

@@ -28,7 +28,7 @@ public:
/// </summary>
Win32CriticalSection()
{
Windows::InitializeCriticalSectionEx(&_criticalSection, 100, 0x01000000);
Windows::InitializeCriticalSectionEx(&_criticalSection, 4000, 0x01000000);
}
/// <summary>

View File

@@ -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<class Texture>)
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;
}