diff --git a/Content/Editor/MaterialTemplates/Terrain.shader b/Content/Editor/MaterialTemplates/Terrain.shader index b60ca2a3c..32395d583 100644 --- a/Content/Editor/MaterialTemplates/Terrain.shader +++ b/Content/Editor/MaterialTemplates/Terrain.shader @@ -269,24 +269,16 @@ float CalcLOD(float2 xy, float4 morph) if ((xy.x + xy.y) > 1) { if (xy.x < xy.y) - { lod = lodCalculated.w; - } else - { lod = lodCalculated.z; - } } else { if (xy.x < xy.y) - { lod = lodCalculated.y; - } else - { lod = lodCalculated.x; - } } return lod; diff --git a/README.md b/README.md index 89adb6d05..473fcb7cb 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ Flax Visual Studio extension provides better programming workflow, C# scripts de * Install Windows 8.1 SDK or newer (via Visual Studio Installer) * Install Microsoft Visual C++ 2015 v140 toolset or newer (via Visual Studio Installer) * Install .Net 7 SDK (via Visual Studio Installer or [from web](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)) +* Install Git with LFS * Clone repo (with LFS) * Run **GenerateProjectFiles.bat** * Open `Flax.sln` and set solution configuration to **Editor.Development** and solution platform to **Win64** @@ -49,11 +50,21 @@ Flax Visual Studio extension provides better programming workflow, C# scripts de * Install Visual Studio Code * Install .Net 7 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/7.0](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)) -* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/)) + * Ubuntu: `sudo snap install dotnet-sdk --classic channel=7.0/stable` +* Install Vulkan SDK + * Ubuntu: see the instructions here: ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/)) + * Arch: `sudo pacman -S spirv-tools vulkan-headers vulkan-tools vulkan-validation-layers` * Install Git with LFS -* Install requried packages: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev nuget autoconf libogg-dev automake build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev` -* Install compiler `sudo apt-get install clang lldb lld` (Clang 6 or newer) -* Clone repo (with LFS) + * Ubuntu: `sudo apt-get install git git-lfs` + * Arch: `sudo pacman -S git git-lfs` + * `git-lfs install` +* Install the required packages: + * Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev` + * Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib` +* Install Clang compiler (version 6 or later): + * Ubuntu: `sudo apt-get install clang lldb lld` + * Arch: `sudo pacman -S clang lldb lld` +* Clone the repository (with LFS) * Run `./GenerateProjectFiles.sh` * Open workspace with Visual Code * Build and run (configuration and task named `Flax|Editor.Linux.Development|x64`) diff --git a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp index 11e437d0a..6610f2295 100644 --- a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp @@ -289,13 +289,14 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) return true; } String androidSdk; - if (!envVars.TryGet(TEXT("ANDROID_SDK"), androidSdk) || !FileSystem::DirectoryExists(androidSdk)) + if (!envVars.TryGet(TEXT("ANDROID_HOME"), androidSdk) || !FileSystem::DirectoryExists(androidSdk)) { - LOG(Error, "Missing or invalid ANDROID_SDK env variable. {0}", androidSdk); - return true; + if (!envVars.TryGet(TEXT("ANDROID_SDK"), androidSdk) || !FileSystem::DirectoryExists(androidSdk)) + { + LOG(Error, "Missing or invalid ANDROID_HOME env variable. {0}", androidSdk); + return true; + } } - if (!envVars.ContainsKey(TEXT("ANDROID_SDK_ROOT"))) - envVars[TEXT("ANDROID_SDK_ROOT")] = androidSdk; // Build Gradle project into package LOG(Info, "Building Gradle project into package..."); diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index 727575a49..2612440a4 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -96,7 +96,8 @@ namespace FlaxEditor.CustomEditors.Editors AnchorPreset = AnchorPresets.TopLeft, TooltipText = "Scale values are linked together.", }; - _linkImage.LocalX += 40; + var x = LinkedLabel.Text.Value.Length * 7 + 5; + _linkImage.LocalX += x; _linkImage.LocalY += 1; LinkedLabel.SetupContextMenu += (label, menu, editor) => diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 878888a73..b2ab47d89 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -1538,7 +1538,7 @@ namespace FlaxEditor if (dockedTo != null && dockedTo.SelectedTab != gameWin && dockedTo.SelectedTab != null) result = dockedTo.SelectedTab.Size * root.DpiScale; else - result = gameWin.Size * root.DpiScale; + result = gameWin.Viewport.Size * root.DpiScale; result = Float2.Round(result); } diff --git a/Source/Editor/GUI/ContextMenu/ContextMenu.cs b/Source/Editor/GUI/ContextMenu/ContextMenu.cs index e652bc98d..f2e5d30e3 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenu.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenu.cs @@ -365,6 +365,7 @@ namespace FlaxEditor.GUI.ContextMenu float maxWidth = 0; float height = _itemsAreaMargin.Height; int itemsLeft = MaximumItemsInViewCount; + int overflowItemCount = 0; for (int i = 0; i < _panel.Children.Count; i++) { if (_panel.Children[i] is ContextMenuItem item && item.Visible) @@ -374,11 +375,27 @@ namespace FlaxEditor.GUI.ContextMenu height += item.Height + _itemsMargin.Height; itemsLeft--; } + else + { + overflowItemCount++; + } maxWidth = Mathf.Max(maxWidth, item.MinimumWidth); } } maxWidth = Mathf.Max(maxWidth + 20, MinimumWidth); + // Move child arrows to accommodate scroll bar showing + if (overflowItemCount > 0) + { + foreach (var child in _panel.Children) + { + if (child is ContextMenuChildMenu item && item.Visible) + { + item.AdjustArrowAmount = -_panel.VScrollBar.Width; + } + } + } + // Resize container Size = new Float2(Mathf.Ceil(maxWidth), Mathf.Ceil(height)); diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs b/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs index ebf0a3b4a..0256d2bee 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs @@ -17,6 +17,11 @@ namespace FlaxEditor.GUI.ContextMenu /// public readonly ContextMenu ContextMenu = new ContextMenu(); + /// + /// The amount to adjust the arrow image by in x coordinates. + /// + public float AdjustArrowAmount = 0; + /// /// Initializes a new instance of the class. /// @@ -44,7 +49,7 @@ namespace FlaxEditor.GUI.ContextMenu // Draw arrow if (ContextMenu.HasChildren) - Render2D.DrawSprite(style.ArrowRight, new Rectangle(Width - 15, (Height - 12) / 2, 12, 12), Enabled ? isCMopened ? style.BackgroundSelected : style.Foreground : style.ForegroundDisabled); + Render2D.DrawSprite(style.ArrowRight, new Rectangle(Width - 15 + AdjustArrowAmount, (Height - 12) / 2, 12, 12), Enabled ? isCMopened ? style.BackgroundSelected : style.Foreground : style.ForegroundDisabled); } /// diff --git a/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs b/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs index 8ba95a216..bce95566f 100644 --- a/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs +++ b/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs @@ -274,6 +274,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing { ScriptsBuilder.ScriptsReload += OnScriptsReload; ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd; + ScriptsBuilder.ScriptsLoaded += OnScriptsLoaded; Editor.Options.OptionsChanged += OnOptionsChanged; // Add default code editors (in-build) @@ -344,6 +345,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing ScriptsBuilder.ScriptsReload -= OnScriptsReload; ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd; + ScriptsBuilder.ScriptsLoaded -= OnScriptsLoaded; } /// @@ -379,6 +381,12 @@ namespace FlaxEditor.Modules.SourceCodeEditing OnTypesChanged(); } + private void OnScriptsLoaded() + { + // Clear any state with engine-only types + ClearTypes(); + } + private static bool IsTypeValidScriptingType(ScriptType t) { return !t.IsGenericType && !t.IsAbstract && !t.HasAttribute(typeof(HideInEditorAttribute), false); diff --git a/Source/Editor/Scripting/ScriptsBuilder.cpp b/Source/Editor/Scripting/ScriptsBuilder.cpp index 51aaef6d0..0ec002cc2 100644 --- a/Source/Editor/Scripting/ScriptsBuilder.cpp +++ b/Source/Editor/Scripting/ScriptsBuilder.cpp @@ -37,6 +37,7 @@ enum class EventType ReloadBegin = 5, Reload = 6, ReloadEnd = 7, + ScriptsLoaded = 8, }; struct EventData @@ -74,6 +75,7 @@ namespace ScriptsBuilderImpl void onScriptsReloadStart(); void onScriptsReload(); void onScriptsReloadEnd(); + void onScriptsLoaded(); void GetClassName(const MString& fullname, MString& className); @@ -203,6 +205,11 @@ void ScriptsBuilderImpl::onScriptsReloadEnd() CallEvent(EventType::ReloadEnd); } +void ScriptsBuilderImpl::onScriptsLoaded() +{ + CallEvent(EventType::ScriptsLoaded); +} + void ScriptsBuilder::Compile() { ScopeLock scopeLock(_locker); @@ -556,6 +563,7 @@ bool ScriptsBuilderService::Init() Level::ScriptsReloadStart.Bind(onScriptsReloadStart); Level::ScriptsReload.Bind(onScriptsReload); Level::ScriptsReloadEnd.Bind(onScriptsReloadEnd); + Scripting::ScriptsLoaded.Bind(onScriptsLoaded); // Listen to code editors manager events CodeEditingManager::AsyncOpenBegin.Bind(onCodeEditorAsyncOpenBegin); diff --git a/Source/Editor/Scripting/ScriptsBuilder.cs b/Source/Editor/Scripting/ScriptsBuilder.cs index 67a43388a..3dacc9a95 100644 --- a/Source/Editor/Scripting/ScriptsBuilder.cs +++ b/Source/Editor/Scripting/ScriptsBuilder.cs @@ -66,6 +66,11 @@ namespace FlaxEditor /// public static event Action ScriptsReloadEnd; + /// + /// Occurs when engine loads game scripts. + /// + public static event Action ScriptsLoaded; + /// /// Occurs when code editor starts asynchronous open a file or a solution. /// @@ -86,6 +91,7 @@ namespace FlaxEditor ReloadBegin = 5, Reload = 6, ReloadEnd = 7, + ScriptsLoaded = 8, } internal static void Internal_OnEvent(EventType type) @@ -118,6 +124,9 @@ namespace FlaxEditor case EventType.ReloadEnd: ScriptsReloadEnd?.Invoke(); break; + case EventType.ScriptsLoaded: + ScriptsLoaded?.Invoke(); + break; } } diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index dd214e8fc..b951f5f70 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -973,6 +973,19 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.TextBox(30, Surface.Constants.LayoutOffsetY, 140, TextBox.DefaultHeight, 0, false), } }, + new NodeArchetype + { + TypeID = 33, + Title = "Animation Instance Data", + Description = "Caches custom data per-instance and allow sampling it. Can be used to randomize animation play offset to offer randomization for crowds reusing the same graph.", + Flags = NodeFlags.AnimGraph, + Size = new Float2(240, 20), + Elements = new[] + { + NodeElementArchetype.Factory.Output(0, "Get", typeof(Float4), 0), + NodeElementArchetype.Factory.Input(0, "Init", true, typeof(Float4), 1), + } + }, }; } } diff --git a/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs b/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs index 537121001..252891d44 100644 --- a/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs +++ b/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs @@ -26,7 +26,9 @@ namespace FlaxEditor.Tools.Terrain _31 = 31, _63 = 63, _127 = 127, + _254 = 254, _255 = 255, + _511 = 511, } private class Options @@ -34,7 +36,7 @@ namespace FlaxEditor.Tools.Terrain [EditorOrder(100), EditorDisplay("Layout", "Number Of Patches"), DefaultValue(typeof(Int2), "1,1"), Limit(0, 512), Tooltip("Amount of terrain patches in each direction (X and Z). Each terrain patch contains a grid of 16 chunks. Patches can be later added or removed from terrain using a terrain editor tool.")] public Int2 NumberOfPatches = new Int2(1, 1); - [EditorOrder(110), EditorDisplay("Layout"), DefaultValue(ChunkSizes._127), Tooltip("The size of the chunk (amount of quads per edge for the highest LOD). Must be power of two minus one (eg. 63).")] + [EditorOrder(110), EditorDisplay("Layout"), DefaultValue(ChunkSizes._127), Tooltip("The size of the chunk (amount of quads per edge for the highest LOD).")] public ChunkSizes ChunkSize = ChunkSizes._127; [EditorOrder(120), EditorDisplay("Layout", "LOD Count"), DefaultValue(6), Limit(1, FlaxEngine.Terrain.MaxLODs), Tooltip("The maximum Level Of Details count. The actual amount of LODs may be lower due to provided chunk size (each LOD has 4 times less quads).")] diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index 2872b6821..659bab249 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -235,14 +235,14 @@ namespace FlaxEditor.Windows.Assets if (IsLayoutLocked) return; - var root = Graph.Root; - root.TreeNode.LockChildrenRecursive(); + _tree.LockChildrenRecursive(); // Update tree var query = _searchBox.Text; + var root = Graph.Root; root.TreeNode.UpdateFilter(query); - root.TreeNode.UnlockChildrenRecursive(); + _tree.UnlockChildrenRecursive(); PerformLayout(); PerformLayout(); } diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index a07bdd71f..532f400a3 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -123,14 +123,14 @@ namespace FlaxEditor.Windows if (IsLayoutLocked) return; - var root = Editor.Scene.Root; - root.TreeNode.LockChildrenRecursive(); + _tree.LockChildrenRecursive(); // Update tree var query = _searchBox.Text; + var root = Editor.Scene.Root; root.TreeNode.UpdateFilter(query); - root.TreeNode.UnlockChildrenRecursive(); + _tree.UnlockChildrenRecursive(); PerformLayout(); PerformLayout(); } @@ -251,7 +251,7 @@ namespace FlaxEditor.Windows if (nodes.Count == 1) { nodes[0].ExpandAllParents(true); - ScrollViewTo(nodes[0]); + _sceneTreePanel.ScrollViewTo(nodes[0]); } } diff --git a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp index 3205337ad..d7cdbeecd 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp @@ -115,6 +115,11 @@ void SlotBucketInit(AnimGraphInstanceData::Bucket& bucket) bucket.Slot.LoopsLeft = 0; } +void InstanceDataBucketInit(AnimGraphInstanceData::Bucket& bucket) +{ + bucket.InstanceData.Init = true; +} + bool SortMultiBlend1D(const byte& a, const byte& b, AnimGraphNode* n) { // Sort items by X location from the lowest to the highest @@ -432,10 +437,12 @@ bool AnimGraphBase::onNodeLoaded(Node* n) } // Animation Slot case 32: - { ADD_BUCKET(SlotBucketInit); break; - } + // Animation Instance Data + case 33: + ADD_BUCKET(InstanceDataBucketInit); + break; } break; // Custom diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h index 86ccdd10c..e40078f64 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.h +++ b/Source/Engine/Animations/Graph/AnimGraph.h @@ -315,6 +315,12 @@ public: int32 LoopsLeft; }; + struct InstanceDataBucket + { + bool Init; + float Data[4]; + }; + /// /// The single data storage bucket for the instanced animation graph node. Used to store the node state (playback position, state, transition data). /// @@ -327,6 +333,7 @@ public: BlendPoseBucket BlendPose; StateMachineBucket StateMachine; SlotBucket Slot; + InstanceDataBucket InstanceData; }; }; diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 9b74ff347..86aa42ff6 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -2035,10 +2035,22 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu } break; } + // Animation Instance Data + case 33: + { + auto& bucket = context.Data->State[node->BucketIndex].InstanceData; + if (bucket.Init) + { + bucket.Init = false; + *(Float4*)bucket.Data = (Float4)tryGetValue(node->GetBox(1), Value::Zero); + } + value = *(Float4*)bucket.Data; + break; + } default: break; } - context.ValueCache.Add(boxBase, value); + context.ValueCache[boxBase] = value; } void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& value) diff --git a/Source/Engine/Content/Loading/ContentLoadingManager.cpp b/Source/Engine/Content/Loading/ContentLoadingManager.cpp index c9680441b..7fd63a217 100644 --- a/Source/Engine/Content/Loading/ContentLoadingManager.cpp +++ b/Source/Engine/Content/Loading/ContentLoadingManager.cpp @@ -55,8 +55,7 @@ LoadingThread::~LoadingThread() // Check if has thread attached if (_thread != nullptr) { - if (_thread->IsRunning()) - _thread->Kill(true); + _thread->Kill(true); Delete(_thread); } } diff --git a/Source/Engine/Content/SceneReference.h b/Source/Engine/Content/SceneReference.h index dae02606f..da25b2c73 100644 --- a/Source/Engine/Content/SceneReference.h +++ b/Source/Engine/Content/SceneReference.h @@ -4,6 +4,7 @@ #include "Engine/Core/Types/Guid.h" #include "Engine/Core/ISerializable.h" +#include "Engine/Scripting/ScriptingType.h" /// /// Represents the reference to the scene asset. Stores the unique ID of the scene to reference. Can be used to load the selected scene. diff --git a/Source/Engine/Core/Templates.h b/Source/Engine/Core/Templates.h index dbb6388fc..49da8b84a 100644 --- a/Source/Engine/Core/Templates.h +++ b/Source/Engine/Core/Templates.h @@ -21,7 +21,11 @@ namespace THelpers template struct TIsTriviallyDestructibleImpl { - enum { Value = __has_trivial_destructor(T) }; +#if defined(__clang__) && __clang_major__ >= 15 + enum { Value = __is_trivially_destructible(T) }; +#else + enum { Value = __has_trivial_destructor(T) }; +#endif }; } @@ -261,7 +265,11 @@ struct TIsCopyConstructible template struct TIsTriviallyCopyConstructible { - enum { Value = TOrValue<__has_trivial_copy(T), TIsPODType>::Value }; +#if defined(__clang__) && __clang_major__ >= 15 + enum { Value = TOrValue<__is_trivially_copyable(T), TIsPODType>::Value }; +#else + enum { Value = TOrValue<__has_trivial_copy(T), TIsPODType>::Value }; +#endif }; //////////////////////////////////////////////////////////////////////////////////// @@ -289,7 +297,11 @@ struct TIsTriviallyDestructible template struct TIsTriviallyCopyAssignable { - enum { Value = TOrValue<__has_trivial_assign(T), TIsPODType>::Value }; +#if defined(__clang__) && __clang_major__ >= 15 + enum { Value = TOrValue<__is_trivially_assignable(T, const T), TIsPODType>::Value }; +#else + enum { Value = TOrValue<__has_trivial_assign(T), TIsPODType>::Value }; +#endif }; //////////////////////////////////////////////////////////////////////////////////// diff --git a/Source/Engine/Graphics/PostProcessSettings.cpp b/Source/Engine/Graphics/PostProcessSettings.cpp index 3b28107b2..a564bc85f 100644 --- a/Source/Engine/Graphics/PostProcessSettings.cpp +++ b/Source/Engine/Graphics/PostProcessSettings.cpp @@ -172,6 +172,7 @@ void ScreenSpaceReflectionsSettings::BlendWith(ScreenSpaceReflectionsSettings& o const bool isHalf = weight >= 0.5f; BLEND_FLOAT(Intensity); + BLEND_ENUM(TraceMode); BLEND_ENUM(DepthResolution); BLEND_ENUM(RayTracePassResolution); BLEND_FLOAT(BRDFBias); diff --git a/Source/Engine/Graphics/RenderTask.cpp b/Source/Engine/Graphics/RenderTask.cpp index e10f4b6f5..48e89f59e 100644 --- a/Source/Engine/Graphics/RenderTask.cpp +++ b/Source/Engine/Graphics/RenderTask.cpp @@ -134,9 +134,15 @@ bool RenderTask::Resize(int32 width, int32 height) SceneRenderTask::SceneRenderTask(const SpawnParams& params) : RenderTask(params) { + Buffers = New(); + + // Initialize view View.Position = Float3::Zero; View.Direction = Float3::Forward; - Buffers = New(); + Matrix::PerspectiveFov(PI_OVER_2, 1.0f, View.Near, View.Far, View.Projection); + View.NonJitteredProjection = View.Projection; + Matrix::Invert(View.Projection, View.IP); + View.SetFace(4); } SceneRenderTask::~SceneRenderTask() diff --git a/Source/Engine/Level/Actors/PostFxVolume.h b/Source/Engine/Level/Actors/PostFxVolume.h index 21ad1a010..577f05e57 100644 --- a/Source/Engine/Level/Actors/PostFxVolume.h +++ b/Source/Engine/Level/Actors/PostFxVolume.h @@ -100,10 +100,8 @@ public: public: /// - /// Gets the order in which multiple volumes are blended together. - /// The volume with the highest priority takes precedence over all other overlapping volumes. + /// Gets the order in which multiple volumes are blended together. The volume with the highest priority takes precedence over all other overlapping volumes. /// - /// The result. API_PROPERTY(Attributes="EditorDisplay(\"PostFx Volume\"), EditorOrder(60)") FORCE_INLINE int32 GetPriority() const { @@ -111,10 +109,8 @@ public: } /// - /// Sets the order in which multiple volumes are blended together. - /// The volume with the highest priority takes precedence over all other overlapping volumes. + /// Sets the order in which multiple volumes are blended together. The volume with the highest priority takes precedence over all other overlapping volumes. /// - /// The value. API_PROPERTY() FORCE_INLINE void SetPriority(int32 value) { _priority = value; @@ -123,7 +119,6 @@ public: /// /// Gets the distance inside the volume at which blending with the volume's settings occurs. /// - /// The result. API_PROPERTY(Attributes="EditorDisplay(\"PostFx Volume\"), EditorOrder(70)") FORCE_INLINE float GetBlendRadius() const { @@ -133,7 +128,6 @@ public: /// /// Sets the distance inside the volume at which blending with the volume's settings occurs. /// - /// The value. API_PROPERTY() void SetBlendRadius(float value) { _blendRadius = Math::Clamp(value, 0.0f, 1000.0f); @@ -142,7 +136,6 @@ public: /// /// Gets the amount of influence the volume's properties have. 0 is no effect; 1 is full effect. /// - /// The result. API_PROPERTY(Attributes="EditorDisplay(\"PostFx Volume\"), EditorOrder(80)") FORCE_INLINE float GetBlendWeight() const { @@ -152,18 +145,14 @@ public: /// /// Sets the amount of influence the volume's properties have. 0 is no effect; 1 is full effect. /// - /// The value. API_PROPERTY() void SetBlendWeight(float value) { _blendWeight = Math::Saturate(value); } /// - /// Gets the value indicating whether the bounds of the volume are taken into account. - /// If false, the volume affects the entire world, regardless of its bounds. - /// If true, the volume only has an effect within its bounds. + /// Gets the value indicating whether the bounds of the volume are taken into account. If false, the volume affects the entire world, regardless of its bounds. If true, the volume only has an effect within its bounds. /// - /// The result. API_PROPERTY(Attributes="EditorDisplay(\"PostFx Volume\"), EditorOrder(90)") FORCE_INLINE bool GetIsBounded() const { @@ -171,11 +160,8 @@ public: } /// - /// Sets the value indicating whether the bounds of the volume are taken into account. - /// If false, the volume affects the entire world, regardless of its bounds. - /// If true, the volume only has an effect within its bounds. + /// Sets the value indicating whether the bounds of the volume are taken into account. If false, the volume affects the entire world, regardless of its bounds. If true, the volume only has an effect within its bounds. /// - /// The value. API_PROPERTY() FORCE_INLINE void SetIsBounded(bool value) { _isBounded = value; diff --git a/Source/Engine/Navigation/NavMeshRuntime.cpp b/Source/Engine/Navigation/NavMeshRuntime.cpp index 5b229a424..b09f8346a 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.cpp +++ b/Source/Engine/Navigation/NavMeshRuntime.cpp @@ -352,7 +352,7 @@ void NavMeshRuntime::EnsureCapacity(int32 tilesToAddCount) // Navmesh tiles capacity growing rule int32 newCapacity = capacity ? capacity : 32; while (newCapacity < newTilesCount) - newCapacity = Math::RoundUpToPowerOf2(newCapacity); + newCapacity = Math::RoundUpToPowerOf2(newCapacity + 1); LOG(Info, "Resizing navmesh {2} from {0} to {1} tiles capacity", capacity, newCapacity, Properties.Name); diff --git a/Source/Engine/Physics/Colliders/BoxCollider.h b/Source/Engine/Physics/Colliders/BoxCollider.h index c4ee6c2b5..0cc0e7be9 100644 --- a/Source/Engine/Physics/Colliders/BoxCollider.h +++ b/Source/Engine/Physics/Colliders/BoxCollider.h @@ -22,7 +22,7 @@ public: /// Gets the size of the box, measured in the object's local space. /// /// The box size will be scaled by the actor's world scale. - API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(typeof(Vector3), \"100,100,100\"), EditorDisplay(\"Collider\")") + API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(typeof(Float3), \"100,100,100\"), EditorDisplay(\"Collider\")") FORCE_INLINE Float3 GetSize() const { return _size; diff --git a/Source/Engine/Platform/Base/FileSystemBase.h b/Source/Engine/Platform/Base/FileSystemBase.h index d0284042b..fc3d75bb1 100644 --- a/Source/Engine/Platform/Base/FileSystemBase.h +++ b/Source/Engine/Platform/Base/FileSystemBase.h @@ -50,11 +50,16 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(FileSystemBase); /// /// The parent window or null. /// The initial directory. - /// The custom filter. + /// The file filter string as null-terminated pairs of name and list of extensions. Multiple file extensions must be separated with semicolon. /// True if allow multiple files to be selected, otherwise use single-file mode. /// The dialog title. /// The output names of the files picked by the user. /// True if failed, otherwise false. + /// + /// Example file filters: + /// "All Files\0*.*" + /// "All Files\0*.*\0Image Files\0*.png;*.jpg" + /// API_FUNCTION() static bool ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, API_PARAM(Out) Array& filenames); /// @@ -62,11 +67,16 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(FileSystemBase); /// /// The parent window. /// The initial directory. - /// The filter. + /// The file filter string as null-terminated pairs of name and list of extensions. Multiple file extensions must be separated with semicolon. /// True if allow multiple files to be selected, otherwise use single-file mode. /// The title. /// The output names of the files picked by the user. /// True if failed, otherwise false. + /// + /// Example file filters: + /// "All Files\0*.*" + /// "All Files\0*.*\0Image Files\0*.png;*.jpg" + /// API_FUNCTION() static bool ShowSaveFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, API_PARAM(Out) Array& filenames); /// diff --git a/Source/Engine/Platform/Base/StringUtilsBase.cpp b/Source/Engine/Platform/Base/StringUtilsBase.cpp index 6f1d741d5..d005f8a04 100644 --- a/Source/Engine/Platform/Base/StringUtilsBase.cpp +++ b/Source/Engine/Platform/Base/StringUtilsBase.cpp @@ -528,4 +528,17 @@ String StringUtils::ToString(double value) return String::Format(TEXT("{}"), value); } +String StringUtils::GetZZString(const Char* str) +{ + const Char* end = str; + while (*end != '\0') + { + end++; + if (*end == '\0') + end++; + } + const int len = end - str; + return String(str, len); +} + #undef STRING_UTILS_ITOSTR_BUFFER_SIZE diff --git a/Source/Engine/Platform/Base/ThreadBase.cpp b/Source/Engine/Platform/Base/ThreadBase.cpp index 38e99ceb7..5739dbf3e 100644 --- a/Source/Engine/Platform/Base/ThreadBase.cpp +++ b/Source/Engine/Platform/Base/ThreadBase.cpp @@ -43,7 +43,10 @@ void ThreadBase::SetPriority(ThreadPriority priority) void ThreadBase::Kill(bool waitForJoin) { if (!_isRunning) + { + ClearHandleInternal(); return; + } ASSERT(GetID()); const auto thread = static_cast(this); @@ -105,7 +108,6 @@ int32 ThreadBase::Run() _callAfterWork = false; _runnable->AfterWork(false); } - ClearHandleInternal(); _isRunning = false; ThreadExiting(thread, exitCode); ThreadRegistry::Remove(thread); diff --git a/Source/Engine/Platform/Base/ThreadBase.h b/Source/Engine/Platform/Base/ThreadBase.h index f9e78b555..dffae8005 100644 --- a/Source/Engine/Platform/Base/ThreadBase.h +++ b/Source/Engine/Platform/Base/ThreadBase.h @@ -12,6 +12,7 @@ class IRunnable; /// /// Base class for thread objects. /// +/// Ensure to call Kill or Join before deleting thread object. class FLAXENGINE_API ThreadBase : public Object, public NonCopyable { public: diff --git a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp index 7291dc8ac..f8ad0d476 100644 --- a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp +++ b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp @@ -4,7 +4,9 @@ #include "LinuxFileSystem.h" #include "Engine/Platform/File.h" +#include "Engine/Platform/StringUtils.h" #include "Engine/Core/Types/String.h" +#include "Engine/Core/Types/StringBuilder.h" #include "Engine/Core/Types/StringView.h" #include "Engine/Core/Types/TimeSpan.h" #include "Engine/Core/Math/Math.h" @@ -27,20 +29,38 @@ bool LinuxFileSystem::ShowOpenFileDialog(Window* parentWindow, const StringView& { const StringAsANSI<> initialDirectoryAnsi(*initialDirectory, initialDirectory.Length()); const StringAsANSI<> titleAnsi(*title, title.Length()); + const char* initDir = initialDirectory.HasChars() ? initialDirectoryAnsi.Get() : "."; char cmd[2048]; - if (FileSystem::FileExists(TEXT("/usr/bin/zenity"))) + String xdgCurrentDesktop; + StringBuilder fileFilter; + Array fileFilterEntries; + Platform::GetEnvironmentVariable(TEXT("XDG_CURRENT_DESKTOP"), xdgCurrentDesktop); + StringUtils::GetZZString(filter.Get()).Split('\0', fileFilterEntries); + + const bool zenitySupported = FileSystem::FileExists(TEXT("/usr/bin/zenity")); + const bool kdialogSupported = FileSystem::FileExists(TEXT("/usr/bin/kdialog")); + if (zenitySupported && (xdgCurrentDesktop != TEXT("KDE") || !kdialogSupported)) // Prefer kdialog when running on KDE { - // TODO: initialDirectory support - // TODO: multiSelect support - // TODO: filter support - sprintf(cmd, "/usr/bin/zenity --modal --file-selection --title=\"%s\" ", titleAnsi.Get()); + for (int32 i = 1; i < fileFilterEntries.Count(); i += 2) + { + String extensions(fileFilterEntries[i]); + fileFilterEntries[i].Replace(TEXT(";"), TEXT(" ")); + fileFilter.Append(String::Format(TEXT("{0}--file-filter=\"{1}|{2}\""), i > 1 ? TEXT(" ") : TEXT(""), fileFilterEntries[i-1].Get(), extensions.Get())); + } + + sprintf(cmd, "/usr/bin/zenity --modal --file-selection %s--filename=\"%s\" --title=\"%s\" %s ", multiSelect ? "--multiple --separator=$'\n' " : " ", initDir, titleAnsi.Get(), fileFilter.ToStringView().ToStringAnsi().GetText()); } - else if (FileSystem::FileExists(TEXT("/usr/bin/kdialog"))) + else if (kdialogSupported) { - // TODO: multiSelect support - // TODO: filter support - const char* initDir = initialDirectory.HasChars() ? initialDirectoryAnsi.Get() : "."; - sprintf(cmd, "/usr/bin/kdialog --getopenfilename \"%s\" --title \"%s\" ", initDir, titleAnsi.Get()); + for (int32 i = 1; i < fileFilterEntries.Count(); i += 2) + { + String extensions(fileFilterEntries[i]); + fileFilterEntries[i].Replace(TEXT(";"), TEXT(" ")); + fileFilter.Append(String::Format(TEXT("{0}\"{1}({2})\""), i > 1 ? TEXT(" ") : TEXT(""), fileFilterEntries[i-1].Get(), extensions.Get())); + } + fileFilter.Append(String::Format(TEXT("{0}\"{1}({2})\""), TEXT(" "), TEXT("many things"), TEXT("*.png *.jpg"))); + + sprintf(cmd, "/usr/bin/kdialog --getopenfilename %s--title \"%s\" \"%s\" %s ", multiSelect ? "--multiple --separate-output " : " ", titleAnsi.Get(), initDir, fileFilter.ToStringView().ToStringAnsi().GetText()); } else { diff --git a/Source/Engine/Platform/Linux/LinuxFileSystemWatcher.cpp b/Source/Engine/Platform/Linux/LinuxFileSystemWatcher.cpp index 93457623c..2f1f1219c 100644 --- a/Source/Engine/Platform/Linux/LinuxFileSystemWatcher.cpp +++ b/Source/Engine/Platform/Linux/LinuxFileSystemWatcher.cpp @@ -101,7 +101,7 @@ namespace FileSystemWatchers { } else - { + { GetRootWatcher(event->wd)->OnEvent(name, FileSystemAction::Modify); } } @@ -140,7 +140,7 @@ namespace FileSystemWatchers void AddDirWatcher(const int rootFileDesc, const String& path) { - auto watcher = New(path, false, rootFileDesc); + auto watcher = New(path, true, rootFileDesc); Pair subDir = Pair(path, watcher); } @@ -183,8 +183,16 @@ LinuxFileSystemWatcher::LinuxFileSystemWatcher(const String& directory, bool wit FileSystemWatchers::Watchers[WachedDirectory] = Pair>(rootWatcher, Pair(directory, this)); if (withSubDirs) { - FileSystemWatchers::RootWatchers[WachedDirectory] = this; - FileSystemWatchers::AddSubDirWatcher(WachedDirectory, directory); + if (rootWatcher == -1) + { + FileSystemWatchers::RootWatchers[WachedDirectory] = this; + FileSystemWatchers::AddSubDirWatcher(WachedDirectory, directory); + } + else + { + FileSystemWatchers::RootWatchers[WachedDirectory] = FileSystemWatchers::RootWatchers[rootWatcher]; + FileSystemWatchers::AddSubDirWatcher(rootWatcher, directory); + } } } FileSystemWatchers::Locker.Unlock(); diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp index fc265d7ea..2d04492e6 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp @@ -431,8 +431,11 @@ static int X11_MessageBoxCreateWindow(MessageBoxData* data) } else { - x = (X11_DisplayWidth(display, data->screen) - data->dialog_width) / 2; - y = (X11_DisplayHeight(display, data->screen) - data->dialog_height) / 3; + int screenCount; + X11::XineramaScreenInfo* xsi = X11::XineramaQueryScreens(xDisplay, &screenCount); + ASSERT(data->screen < screenCount); + x = (float)xsi[data->screen].x_org + ((float)xsi[data->screen].width - data->dialog_width) / 2; + y = (float)xsi[data->screen].y_org + ((float)xsi[data->screen].height - data->dialog_height) / 2; } X11::XMoveWindow(display, data->window, x, y); @@ -840,17 +843,17 @@ int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event) int32 CalculateDpi() { - // in X11 a screen is not necessarily identical to a desktop - // so we need to stick to one type for pixel and physical size query + int dpi = 96; + char* resourceString = X11::XResourceManagerString(xDisplay); + if (resourceString == NULL) + return dpi; - int screenIdx = 0; - int widthMM = X11_DisplayWidthMM(xDisplay, screenIdx); - int heightMM = X11_DisplayHeightMM(xDisplay, screenIdx); - double xdpi = (widthMM ? X11_DisplayWidth(xDisplay, screenIdx) / (double)widthMM * 25.4 : 0); - double ydpi = (heightMM ? X11_DisplayHeight(xDisplay, screenIdx) / (double)heightMM * 25.4 : 0); - if (xdpi || ydpi) - return (int32)Math::Ceil((xdpi + ydpi) / (xdpi && ydpi ? 2 : 1)); - return 96; + char* type = NULL; + X11::XrmValue value; + X11::XrmDatabase database = X11::XrmGetStringDatabase(resourceString); + if (X11::XrmGetResource(database, "Xft.dpi", "String", &type, &value) == 1 && value.addr != NULL) + dpi = (int)atof(value.addr); + return dpi; } // Maps Flax key codes to X11 names for physical key locations. @@ -2101,6 +2104,7 @@ bool LinuxPlatform::Init() xAtomWmName = X11::XInternAtom(xDisplay, "_NET_WM_NAME", 0); xAtomClipboard = X11::XInternAtom(xDisplay, "CLIPBOARD", 0); + X11::XrmInitialize(); SystemDpi = CalculateDpi(); int cursorSize = X11::XcursorGetDefaultSize(xDisplay); @@ -2806,6 +2810,7 @@ int32 LinuxProcess(const StringView& cmdLine, const StringView& workingDir, cons { close(fildes[0]); // close the reading end of the pipe dup2(fildes[1], STDOUT_FILENO); // redirect stdout to pipe + close(fildes[1]); dup2(STDOUT_FILENO, STDERR_FILENO); // redirect stderr to stdout } @@ -2814,6 +2819,8 @@ int32 LinuxProcess(const StringView& cmdLine, const StringView& workingDir, cons { LOG(Warning, " failed, errno={}", errno); } + fflush(stdout); + _exit(1); } else { @@ -2826,7 +2833,7 @@ int32 LinuxProcess(const StringView& cmdLine, const StringView& workingDir, cons { char lineBuffer[1024]; close(fildes[1]); // close the writing end of the pipe - FILE *stdPipe = fdopen(fildes[0], "r"); + FILE* stdPipe = fdopen(fildes[0], "r"); while (fgets(lineBuffer, sizeof(lineBuffer), stdPipe) != NULL) { char *p = lineBuffer + strlen(lineBuffer)-1; @@ -2861,6 +2868,7 @@ int32 LinuxProcess(const StringView& cmdLine, const StringView& workingDir, cons returnCode = EPIPE; } } + close(fildes[0]); } } diff --git a/Source/Engine/Platform/Linux/LinuxWindow.cpp b/Source/Engine/Platform/Linux/LinuxWindow.cpp index cbcfca0af..7694836cb 100644 --- a/Source/Engine/Platform/Linux/LinuxWindow.cpp +++ b/Source/Engine/Platform/Linux/LinuxWindow.cpp @@ -47,6 +47,11 @@ static X11::Time MouseLastButtonPressTime = 0; LinuxWindow::LinuxWindow(const CreateWindowSettings& settings) : WindowBase(settings) { + auto display = (X11::Display*)LinuxPlatform::GetXDisplay(); + if (!display) + return; + auto screen = XDefaultScreen(display); + // Cache data int32 width = Math::TruncToInt(settings.Size.X); int32 height = Math::TruncToInt(settings.Size.Y); @@ -64,9 +69,21 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings) break; case WindowStartPosition::CenterScreen: { - Float2 desktopSize = Platform::GetDesktopSize(); - x = Math::TruncToInt((desktopSize.X - _clientSize.X) * 0.5f); - y = Math::TruncToInt((desktopSize.Y - _clientSize.Y) * 0.5f); + Rectangle desktopBounds; + int event, err; + const bool ok = X11::XineramaQueryExtension(display, &event, &err); + if (X11::XineramaQueryExtension(display, &event, &err)) + { + int count; + X11::XineramaScreenInfo* xsi = X11::XineramaQueryScreens(display, &count); + ASSERT(screen < count); + desktopBounds = Rectangle(Float2((float)xsi[screen].x_org, (float)xsi[screen].y_org), Float2((float)xsi[screen].width, (float)xsi[screen].height)); + X11::XFree(xsi); + } + else + desktopBounds = Rectangle(Float2::Zero, Platform::GetDesktopSize()); + x = Math::TruncToInt(desktopBounds.Location.X + (desktopBounds.Size.X - _clientSize.X) * 0.5f); + y = Math::TruncToInt(desktopBounds.Location.Y + (desktopBounds.Size.Y - _clientSize.Y) * 0.5f); } break; case WindowStartPosition::Manual: @@ -76,11 +93,6 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings) } _resizeDisabled = !settings.HasSizingFrame; - auto display = (X11::Display*)LinuxPlatform::GetXDisplay(); - if (!display) - return; - - auto screen = XDefaultScreen(display); auto rootWindow = XRootWindow(display, screen); long visualMask = VisualScreenMask; diff --git a/Source/Engine/Platform/StringUtils.h b/Source/Engine/Platform/StringUtils.h index 6f90bbe92..ea982c2ee 100644 --- a/Source/Engine/Platform/StringUtils.h +++ b/Source/Engine/Platform/StringUtils.h @@ -438,6 +438,13 @@ public: static String ToString(uint64 value); static String ToString(float value); static String ToString(double value); + +public: + + // Returns the String to double null-terminated string + // @param str Double null-terminated string + // @return Double null-terminated String + static String GetZZString(const Char* str); }; inline uint32 GetHash(const char* key) diff --git a/Source/Engine/Platform/Unix/UnixThread.cpp b/Source/Engine/Platform/Unix/UnixThread.cpp index 4fa532a4b..f662bdba8 100644 --- a/Source/Engine/Platform/Unix/UnixThread.cpp +++ b/Source/Engine/Platform/Unix/UnixThread.cpp @@ -53,6 +53,7 @@ UnixThread* UnixThread::Setup(UnixThread* thread, uint32 stackSize) void UnixThread::Join() { pthread_join(_thread, nullptr); + ClearHandleInternal(); } void UnixThread::ClearHandleInternal() diff --git a/Source/Engine/Platform/Win32/Win32Thread.cpp b/Source/Engine/Platform/Win32/Win32Thread.cpp index 5ede5cfc6..0cd8faaf2 100644 --- a/Source/Engine/Platform/Win32/Win32Thread.cpp +++ b/Source/Engine/Platform/Win32/Win32Thread.cpp @@ -113,6 +113,7 @@ unsigned long Win32Thread::ThreadProc(void* pThis) void Win32Thread::Join() { WaitForSingleObject((HANDLE)_thread, INFINITE); + ClearHandleInternal(); } void Win32Thread::ClearHandleInternal() diff --git a/Source/Engine/Terrain/TerrainManager.cpp b/Source/Engine/Terrain/TerrainManager.cpp index 0980bcc7a..df9b6d706 100644 --- a/Source/Engine/Terrain/TerrainManager.cpp +++ b/Source/Engine/Terrain/TerrainManager.cpp @@ -98,10 +98,12 @@ bool TerrainManager::GetChunkGeometry(DrawCall& drawCall, int32 chunkSize, int32 // Prepare const int32 vertexCount = (chunkSize + 1) >> lodIndex; chunkSize = vertexCount - 1; + const bool indexUse16bits = chunkSize * chunkSize * vertexCount < MAX_uint16; + const int32 indexSize = indexUse16bits ? sizeof(uint16) : sizeof(uint32); const int32 indexCount = chunkSize * chunkSize * 2 * 3; const int32 vertexCount2 = vertexCount * vertexCount; const int32 vbSize = sizeof(TerrainVertex) * vertexCount2; - const int32 ibSize = sizeof(uint16) * indexCount; + const int32 ibSize = indexSize * indexCount; TempData.Clear(); TempData.EnsureCapacity(Math::Max(vbSize, ibSize)); @@ -140,26 +142,51 @@ bool TerrainManager::GetChunkGeometry(DrawCall& drawCall, int32 chunkSize, int32 // Create index buffer auto ib = GPUDevice::Instance->CreateBuffer(TEXT("TerrainChunk.IB")); - auto index = (uint16*)TempData.Get(); - for (int32 z = 0; z < chunkSize; z++) + if (indexUse16bits) { - for (int32 x = 0; x < chunkSize; x++) + auto index = (uint16*)TempData.Get(); + for (int32 z = 0; z < chunkSize; z++) { - const uint16 i00 = (x + 0) + (z + 0) * vertexCount; - const uint16 i10 = (x + 1) + (z + 0) * vertexCount; - const uint16 i11 = (x + 1) + (z + 1) * vertexCount; - const uint16 i01 = (x + 0) + (z + 1) * vertexCount; + for (int32 x = 0; x < chunkSize; x++) + { + const uint16 i00 = (x + 0) + (z + 0) * vertexCount; + const uint16 i10 = (x + 1) + (z + 0) * vertexCount; + const uint16 i11 = (x + 1) + (z + 1) * vertexCount; + const uint16 i01 = (x + 0) + (z + 1) * vertexCount; - *index++ = i00; - *index++ = i11; - *index++ = i10; + *index++ = i00; + *index++ = i11; + *index++ = i10; - *index++ = i00; - *index++ = i01; - *index++ = i11; + *index++ = i00; + *index++ = i01; + *index++ = i11; + } } } - desc = GPUBufferDescription::Index(sizeof(uint16), indexCount, TempData.Get()); + else + { + auto index = (uint32*)TempData.Get(); + for (int32 z = 0; z < chunkSize; z++) + { + for (int32 x = 0; x < chunkSize; x++) + { + const uint32 i00 = (x + 0) + (z + 0) * vertexCount; + const uint32 i10 = (x + 1) + (z + 0) * vertexCount; + const uint32 i11 = (x + 1) + (z + 1) * vertexCount; + const uint32 i01 = (x + 0) + (z + 1) * vertexCount; + + *index++ = i00; + *index++ = i11; + *index++ = i10; + + *index++ = i00; + *index++ = i01; + *index++ = i11; + } + } + } + desc = GPUBufferDescription::Index(indexSize, indexCount, TempData.Get()); if (ib->Init(desc)) { vb->ReleaseGPU(); diff --git a/Source/Engine/Threading/JobSystem.cpp b/Source/Engine/Threading/JobSystem.cpp index 1d4e9762a..396294e56 100644 --- a/Source/Engine/Threading/JobSystem.cpp +++ b/Source/Engine/Threading/JobSystem.cpp @@ -149,8 +149,7 @@ void JobSystemService::Dispose() { if (Threads[i]) { - if (Threads[i]->IsRunning()) - Threads[i]->Kill(true); + Threads[i]->Kill(true); Delete(Threads[i]); Threads[i] = nullptr; } diff --git a/Source/Engine/Threading/ThreadPool.cpp b/Source/Engine/Threading/ThreadPool.cpp index 2dd6176d0..b7db81ffa 100644 --- a/Source/Engine/Threading/ThreadPool.cpp +++ b/Source/Engine/Threading/ThreadPool.cpp @@ -98,10 +98,7 @@ void ThreadPoolService::Dispose() // Delete threads for (int32 i = 0; i < ThreadPoolImpl::Threads.Count(); i++) { - if (ThreadPoolImpl::Threads[i]->IsRunning()) - { - ThreadPoolImpl::Threads[i]->Kill(true); - } + ThreadPoolImpl::Threads[i]->Kill(true); } ThreadPoolImpl::Threads.ClearDelete(); } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 981c1ee14..d51d7e7f1 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -1155,6 +1155,8 @@ namespace Flax.Build.Bindings contents.Append("out "); else if (parameterInfo.IsRef) contents.Append("ref "); + else if (parameterInfo.IsThis) + contents.Append("this "); contents.Append(managedType); contents.Append(' '); contents.Append(parameterInfo.Name); @@ -1214,6 +1216,8 @@ namespace Flax.Build.Bindings contents.Append("out "); else if (parameterInfo.IsRef) contents.Append("ref "); + else if (parameterInfo.IsThis) + contents.Append("this "); contents.Append(managedType); contents.Append(' '); contents.Append(parameterInfo.Name); @@ -1913,6 +1917,8 @@ namespace Flax.Build.Bindings contents.Append("out "); else if (parameterInfo.IsRef) contents.Append("ref "); + else if (parameterInfo.IsThis) + contents.Append("this "); contents.Append(managedType); contents.Append(' '); contents.Append(parameterInfo.Name); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs index 0f0b011a0..d1aba9162 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs @@ -19,7 +19,7 @@ namespace Flax.Build.Bindings partial class BindingsGenerator { private static readonly Dictionary TypeCache = new Dictionary(); - private const int CacheVersion = 17; + private const int CacheVersion = 18; internal static void Write(BinaryWriter writer, string e) { diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 6dd0d59e4..b0cbf3be1 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -2487,7 +2487,10 @@ namespace Flax.Build.Bindings separator = true; contents.Append(parameterInfo.Type).Append(' ').Append(parameterInfo.Name); } - contents.Append(") override").AppendLine(); + contents.Append(')'); + if (functionInfo.IsConst) + contents.Append(" const"); + contents.Append(" override").AppendLine(); contents.AppendLine(" {"); // TODO: try to use ScriptVTable for interfaces implementation in scripting to call proper function instead of manually check at runtime if (functionInfo.Parameters.Count != 0) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index bca9b2d9d..b09b0c137 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -334,6 +334,9 @@ namespace Flax.Build.Bindings case "out": currentParam.IsOut = true; break; + case "this": + currentParam.IsThis = true; + break; case "attributes": currentParam.Attributes = tag.Value; break; diff --git a/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs b/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs index b16b1313e..3676e8e76 100644 --- a/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs @@ -18,6 +18,7 @@ namespace Flax.Build.Bindings public string Attributes; public bool IsRef; public bool IsOut; + public bool IsThis; public bool HasDefaultValue => !string.IsNullOrEmpty(DefaultValue); @@ -35,6 +36,7 @@ namespace Flax.Build.Bindings // TODO: convert into flags writer.Write(IsRef); writer.Write(IsOut); + writer.Write(IsThis); } public void Read(BinaryReader reader) @@ -46,6 +48,7 @@ namespace Flax.Build.Bindings // TODO: convert into flags IsRef = reader.ReadBoolean(); IsOut = reader.ReadBoolean(); + IsThis = reader.ReadBoolean(); } public override string ToString() diff --git a/Source/Tools/Flax.Build/Platforms/Android/AndroidSdk.cs b/Source/Tools/Flax.Build/Platforms/Android/AndroidSdk.cs index 5fa8eae30..a023d33b2 100644 --- a/Source/Tools/Flax.Build/Platforms/Android/AndroidSdk.cs +++ b/Source/Tools/Flax.Build/Platforms/Android/AndroidSdk.cs @@ -33,7 +33,9 @@ namespace Flax.Build.Platforms return; // Find Android SDK folder path - var sdkPath = Environment.GetEnvironmentVariable("ANDROID_SDK"); + var sdkPath = Environment.GetEnvironmentVariable("ANDROID_HOME"); + if (string.IsNullOrEmpty(sdkPath)) + sdkPath = Environment.GetEnvironmentVariable("ANDROID_SDK"); if (string.IsNullOrEmpty(sdkPath)) { // Look for adb in Android folders of some common locations @@ -53,6 +55,8 @@ namespace Flax.Build.Platforms foreach (string searchDir in searchDirs) { string androidDir = Path.Combine(searchDir, "Android"); + if (!Directory.Exists(androidDir)) + androidDir = Path.Combine(searchDir, "sdk"); if (Directory.Exists(androidDir)) { string[] subDirs = Directory.GetDirectories(androidDir, "*sdk*", SearchOption.TopDirectoryOnly); @@ -72,7 +76,7 @@ namespace Flax.Build.Platforms } else if (!Directory.Exists(sdkPath)) { - Log.Warning(string.Format("Specified Android SDK folder in ANDROID_SDK env variable doesn't exist ({0})", sdkPath)); + Log.Warning(string.Format("Specified Android SDK folder in ANDROID_HOME env variable doesn't exist ({0})", sdkPath)); } if (string.IsNullOrEmpty(sdkPath)) { diff --git a/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs b/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs index cb4dbf48d..c82add0ad 100644 --- a/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs @@ -24,14 +24,24 @@ namespace Flax.Build.Platforms { // Setup system paths var includePath = Path.Combine(ToolsetRoot, "usr", "include"); - SystemIncludePaths.Add(includePath); + if (Directory.Exists(includePath)) + SystemIncludePaths.Add(includePath); + else + Log.Error($"Missing toolset header files location {includePath}"); var cppIncludePath = Path.Combine(includePath, "c++", ClangVersion.ToString()); if (Directory.Exists(cppIncludePath)) SystemIncludePaths.Add(cppIncludePath); + else + Log.Verbose($"Missing Clang {ClangVersion} C++ header files location {cppIncludePath}"); var clangLibPath = Path.Combine(ToolsetRoot, "usr", "lib", "clang"); var clangIncludePath = Path.Combine(clangLibPath, ClangVersion.Major.ToString(), "include"); if (!Directory.Exists(clangIncludePath)) + { + var error = $"Missing Clang {ClangVersion} header files location {clangIncludePath}"; clangIncludePath = Path.Combine(clangLibPath, ClangVersion.ToString(), "include"); + if (!Directory.Exists(clangIncludePath)) + Log.Error(error); + } SystemIncludePaths.Add(clangIncludePath); } @@ -75,7 +85,8 @@ namespace Flax.Build.Platforms args.Add("-Wl,-rpath,\"\\$ORIGIN\""); //args.Add("-Wl,--as-needed"); - args.Add("-Wl,--copy-dt-needed-entries"); + if (LdKind == "bfd") + args.Add("-Wl,--copy-dt-needed-entries"); args.Add("-Wl,--hash-style=gnu"); //args.Add("-Wl,--build-id"); diff --git a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs index eb4c8f183..6bbb8707f 100644 --- a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Text.RegularExpressions; using Flax.Build.Graph; using Flax.Build.NativeCpp; @@ -61,6 +62,11 @@ namespace Flax.Build.Platforms /// protected string LdPath; + /// + /// The type of the linker. + /// + protected string LdKind; + /// /// The clang tool version. /// @@ -91,7 +97,19 @@ namespace Flax.Build.Platforms RanlibPath = UnixPlatform.Which("ranlib"); StripPath = UnixPlatform.Which("strip"); ObjcopyPath = UnixPlatform.Which("objcopy"); - LdPath = UnixPlatform.Which("ld"); + + LdPath = UnixPlatform.Which("ld.lld"); + LdKind = "lld"; + if (LdPath == null) + { + LdPath = UnixPlatform.Which("ld.gold"); + LdKind = "gold"; + } + if (LdPath == null) + { + LdPath = UnixPlatform.Which("ld"); // ld.bfd + LdKind = "bfd"; + } } else { @@ -478,6 +496,9 @@ namespace Flax.Build.Platforms // TODO: linkEnvironment.LinkTimeCodeGeneration // TODO: linkEnvironment.Optimization // TODO: linkEnvironment.UseIncrementalLinking + + if (!string.IsNullOrEmpty(LdKind)) + args.Add(string.Format("-fuse-ld={0}", LdKind)); } SetupLinkFilesArgs(graph, options, args, outputFilePath); @@ -536,9 +557,10 @@ namespace Flax.Build.Platforms } } - // Input files + // Input files (link static libraries last) task.PrerequisiteFiles.AddRange(linkEnvironment.InputFiles); - foreach (var file in linkEnvironment.InputFiles) + foreach (var file in linkEnvironment.InputFiles.Where(x => !x.EndsWith(".a")) + .Concat(linkEnvironment.InputFiles.Where(x => x.EndsWith(".a")))) { args.Add(string.Format("\"{0}\"", file.Replace('\\', '/'))); }