Merge branch 'FlaxEngine:master' into visject_grid_snap
This commit is contained in:
@@ -139,7 +139,7 @@ namespace FlaxEditor.Content.Import
|
||||
var menu = new ContextMenu();
|
||||
menu.AddButton("Rename", OnRenameClicked);
|
||||
menu.AddButton("Don't import", OnDontImportClicked);
|
||||
menu.AddButton("Show in Explorer", OnShowInExplorerClicked);
|
||||
menu.AddButton(Utilities.Constants.ShowInExplorer, OnShowInExplorerClicked);
|
||||
menu.Tag = node;
|
||||
menu.Show(node, location);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace FlaxEditor.Content
|
||||
//_watcher.Changed += OnEvent;
|
||||
_watcher.Created += OnEvent;
|
||||
_watcher.Deleted += OnEvent;
|
||||
//_watcher.Renamed += OnEvent;
|
||||
_watcher.Renamed += OnEvent;
|
||||
}
|
||||
|
||||
private void OnEvent(object sender, FileSystemEventArgs e)
|
||||
|
||||
26
Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs
Normal file
26
Source/Editor/CustomEditors/Dedicated/MissingScriptEditor.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated;
|
||||
|
||||
/// <summary>
|
||||
/// The missing script editor.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(MissingScript)), DefaultEditor]
|
||||
public class MissingScriptEditor : GenericEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
if (layout.ContainerControl is not DropPanel dropPanel)
|
||||
{
|
||||
base.Initialize(layout);
|
||||
return;
|
||||
}
|
||||
|
||||
dropPanel.HeaderTextColor = Color.OrangeRed;
|
||||
|
||||
base.Initialize(layout);
|
||||
}
|
||||
}
|
||||
@@ -931,6 +931,11 @@ namespace FlaxEditor.Modules
|
||||
// Check if node already has that element (skip during init when we want to walk project dir very fast)
|
||||
if (_isDuringFastSetup || !parent.Folder.ContainsChild(path))
|
||||
{
|
||||
#if PLATFORM_MAC
|
||||
if (path.EndsWith(".DS_Store", StringComparison.Ordinal))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
// Create file item
|
||||
ContentItem item;
|
||||
if (path.EndsWith(".cs"))
|
||||
@@ -972,6 +977,11 @@ namespace FlaxEditor.Modules
|
||||
// Check if node already has that element (skip during init when we want to walk project dir very fast)
|
||||
if (_isDuringFastSetup || !parent.Folder.ContainsChild(path))
|
||||
{
|
||||
#if PLATFORM_MAC
|
||||
if (path.EndsWith(".DS_Store", StringComparison.Ordinal))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
// Create file item
|
||||
ContentItem item = null;
|
||||
if (FlaxEngine.Content.GetAssetInfo(path, out var assetInfo))
|
||||
@@ -1197,6 +1207,7 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
case WatcherChangeTypes.Created:
|
||||
case WatcherChangeTypes.Deleted:
|
||||
case WatcherChangeTypes.Renamed:
|
||||
{
|
||||
lock (_dirtyNodes)
|
||||
{
|
||||
|
||||
@@ -14,5 +14,11 @@ namespace FlaxEditor.Utilities
|
||||
public const string FacebookUrl = "https://facebook.com/FlaxEngine";
|
||||
public const string YoutubeUrl = "https://youtube.com/c/FlaxEngine";
|
||||
public const string TwitterUrl = "https://twitter.com/FlaxEngine";
|
||||
|
||||
#if PLATFORM_MAC
|
||||
public const string ShowInExplorer = "Show in Finder";
|
||||
#else
|
||||
public const string ShowInExplorer = "Show in explorer";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace FlaxEditor.Windows
|
||||
|
||||
if (item is ContentFolder contentFolder && contentFolder.Node is ProjectTreeNode)
|
||||
{
|
||||
cm.AddButton("Show in explorer", () => FileSystem.ShowFileExplorer(CurrentViewFolder.Path));
|
||||
cm.AddButton(Utilities.Constants.ShowInExplorer, () => FileSystem.ShowFileExplorer(CurrentViewFolder.Path));
|
||||
}
|
||||
else if (isValidElement)
|
||||
{
|
||||
@@ -72,7 +72,7 @@ namespace FlaxEditor.Windows
|
||||
Open(e);
|
||||
});
|
||||
|
||||
cm.AddButton("Show in explorer", () => FileSystem.ShowFileExplorer(System.IO.Path.GetDirectoryName(item.Path)));
|
||||
cm.AddButton(Utilities.Constants.ShowInExplorer, () => FileSystem.ShowFileExplorer(System.IO.Path.GetDirectoryName(item.Path)));
|
||||
|
||||
if (item.HasDefaultThumbnail == false)
|
||||
{
|
||||
@@ -135,7 +135,7 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
else
|
||||
{
|
||||
cm.AddButton("Show in explorer", () => FileSystem.ShowFileExplorer(CurrentViewFolder.Path));
|
||||
cm.AddButton(Utilities.Constants.ShowInExplorer, () => FileSystem.ShowFileExplorer(CurrentViewFolder.Path));
|
||||
|
||||
b = cm.AddButton("Paste", _view.Paste);
|
||||
b.Enabled = _view.CanPaste();
|
||||
|
||||
@@ -195,7 +195,7 @@ namespace FlaxEditor.Windows
|
||||
_contextMenu.AddButton("Clear log", Clear);
|
||||
_contextMenu.AddButton("Copy selection", _output.Copy);
|
||||
_contextMenu.AddButton("Select All", _output.SelectAll);
|
||||
_contextMenu.AddButton("Show in explorer", () => FileSystem.ShowFileExplorer(Path.Combine(Globals.ProjectFolder, "Logs")));
|
||||
_contextMenu.AddButton(Utilities.Constants.ShowInExplorer, () => FileSystem.ShowFileExplorer(Path.Combine(Globals.ProjectFolder, "Logs")));
|
||||
_contextMenu.AddButton("Scroll to bottom", () => { _vScroll.TargetValue = _vScroll.Maximum; }).Icon = Editor.Icons.ArrowDown12;
|
||||
|
||||
// Setup editor options
|
||||
|
||||
@@ -274,7 +274,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
ContextMenuButton b;
|
||||
b = cm.AddButton("Open", () => Editor.Instance.ContentEditing.Open(assetItem));
|
||||
cm.AddButton("Show in content window", () => Editor.Instance.Windows.ContentWin.Select(assetItem));
|
||||
cm.AddButton("Show in explorer", () => FileSystem.ShowFileExplorer(System.IO.Path.GetDirectoryName(assetItem.Path)));
|
||||
cm.AddButton(Utilities.Constants.ShowInExplorer, () => FileSystem.ShowFileExplorer(System.IO.Path.GetDirectoryName(assetItem.Path)));
|
||||
cm.AddButton("Select actors using this asset", () => Editor.Instance.SceneEditing.SelectActorsUsingAsset(assetItem.ID));
|
||||
cm.AddButton("Show asset references graph", () => Editor.Instance.Windows.Open(new AssetReferencesGraphWindow(Editor.Instance, assetItem)));
|
||||
cm.AddButton("Copy name", () => Clipboard.Text = assetItem.NamePath);
|
||||
|
||||
@@ -157,7 +157,7 @@ namespace FlaxEditor.Windows.Search
|
||||
var cm = new FlaxEditor.GUI.ContextMenu.ContextMenu { Tag = assetItem };
|
||||
b = cm.AddButton("Open", () => Editor.Instance.ContentFinding.Open(Item));
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Show in explorer", () => FileSystem.ShowFileExplorer(System.IO.Path.GetDirectoryName(assetItem.Path)));
|
||||
cm.AddButton(Utilities.Constants.ShowInExplorer, () => FileSystem.ShowFileExplorer(System.IO.Path.GetDirectoryName(assetItem.Path)));
|
||||
cm.AddButton("Show in Content window", () => Editor.Instance.Windows.ContentWin.Select(assetItem, true));
|
||||
b.Enabled = proxy != null && proxy.CanReimport(assetItem);
|
||||
if (assetItem is BinaryAssetItem binaryAsset)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "../Collections/Array.h"
|
||||
#include "../Collections/Dictionary.h"
|
||||
#include <functional>
|
||||
#include "../Delegate.h"
|
||||
|
||||
class ArrayExtensions;
|
||||
|
||||
@@ -23,7 +23,6 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the common key.
|
||||
/// </summary>
|
||||
/// <returns>The key.</returns>
|
||||
FORCE_INLINE const TKey& GetKey() const
|
||||
{
|
||||
return _key;
|
||||
@@ -32,7 +31,6 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the common key.
|
||||
/// </summary>
|
||||
/// <returns>The key.</returns>
|
||||
FORCE_INLINE TKey GetKey()
|
||||
{
|
||||
return _key;
|
||||
@@ -52,7 +50,7 @@ public:
|
||||
/// <param name="predicate">The prediction function. Should return true for the target element to find.</param>
|
||||
/// <returns>The index of the element or -1 if nothing found.</returns>
|
||||
template<typename T, typename AllocationType>
|
||||
static int32 IndexOf(const Array<T, AllocationType>& obj, const std::function<bool(const T&)>& predicate)
|
||||
static int32 IndexOf(const Array<T, AllocationType>& obj, const Function<bool(const T&)>& predicate)
|
||||
{
|
||||
for (int32 i = 0; i < obj.Count(); i++)
|
||||
{
|
||||
@@ -71,7 +69,7 @@ public:
|
||||
/// <param name="predicate">The prediction function.</param>
|
||||
/// <returns>True if any element in the collection matches the prediction, otherwise false.</returns>
|
||||
template<typename T, typename AllocationType>
|
||||
static bool Any(const Array<T, AllocationType>& obj, const std::function<bool(const T&)>& predicate)
|
||||
static bool Any(const Array<T, AllocationType>& obj, const Function<bool(const T&)>& predicate)
|
||||
{
|
||||
for (int32 i = 0; i < obj.Count(); i++)
|
||||
{
|
||||
@@ -90,7 +88,7 @@ public:
|
||||
/// <param name="predicate">The prediction function.</param>
|
||||
/// <returns>True if all elements in the collection matches the prediction, otherwise false.</returns>
|
||||
template<typename T, typename AllocationType>
|
||||
static int32 All(const Array<T, AllocationType>& obj, const std::function<bool(const T&)>& predicate)
|
||||
static int32 All(const Array<T, AllocationType>& obj, const Function<bool(const T&)>& predicate)
|
||||
{
|
||||
for (int32 i = 0; i < obj.Count(); i++)
|
||||
{
|
||||
@@ -109,7 +107,7 @@ public:
|
||||
/// <param name="keySelector">A function to extract the key for each element.</param>
|
||||
/// <param name="result">The result collection with groups.</param>
|
||||
template<typename TSource, typename TKey, typename AllocationType>
|
||||
static void GroupBy(const Array<TSource, AllocationType>& obj, const std::function<TKey(TSource const&)>& keySelector, Array<IGrouping<TKey, TSource>, AllocationType>& result)
|
||||
static void GroupBy(const Array<TSource, AllocationType>& obj, const Function<TKey(TSource const&)>& keySelector, Array<IGrouping<TKey, TSource>, AllocationType>& result)
|
||||
{
|
||||
Dictionary<TKey, IGrouping<TKey, TSource>> data(static_cast<int32>(obj.Count() * 3.0f));
|
||||
for (int32 i = 0; i < obj.Count(); i++)
|
||||
|
||||
@@ -105,6 +105,14 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
Globals::StartupFolder = Globals::BinariesFolder = Platform::GetMainDirectory();
|
||||
#if USE_EDITOR
|
||||
Globals::StartupFolder /= TEXT("../../../..");
|
||||
#if PLATFORM_MAC
|
||||
if (Globals::BinariesFolder.EndsWith(TEXT(".app/Contents")))
|
||||
{
|
||||
// If running editor from application package on macOS
|
||||
Globals::StartupFolder = Globals::BinariesFolder;
|
||||
Globals::BinariesFolder /= TEXT("MacOS");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
StringUtils::PathRemoveRelativeParts(Globals::StartupFolder);
|
||||
FileSystem::NormalizePath(Globals::BinariesFolder);
|
||||
@@ -122,7 +130,6 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
}
|
||||
|
||||
EngineImpl::InitPaths();
|
||||
|
||||
EngineImpl::InitLog();
|
||||
|
||||
#if USE_EDITOR
|
||||
@@ -542,7 +549,8 @@ void EngineImpl::InitLog()
|
||||
LOG(Info, "Product: {0}, Company: {1}", Globals::ProductName, Globals::CompanyName);
|
||||
LOG(Info, "Current culture: {0}", Platform::GetUserLocaleName());
|
||||
LOG(Info, "Command line: {0}", CommandLine);
|
||||
LOG(Info, "Base directory: {0}", Globals::StartupFolder);
|
||||
LOG(Info, "Base folder: {0}", Globals::StartupFolder);
|
||||
LOG(Info, "Binaries folder: {0}", Globals::BinariesFolder);
|
||||
LOG(Info, "Temporary folder: {0}", Globals::TemporaryFolder);
|
||||
LOG(Info, "Project folder: {0}", Globals::ProjectFolder);
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
/// <summary>
|
||||
@@ -23,11 +25,17 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
public float ValueRaw => Input.GetAxisRaw(Name);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when axis is changed. Called before scripts update.
|
||||
/// </summary>
|
||||
public event Action ValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InputAxis"/> class.
|
||||
/// </summary>
|
||||
public InputAxis()
|
||||
{
|
||||
Input.AxisValueChanged += Handler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -36,7 +44,31 @@ namespace FlaxEngine
|
||||
/// <param name="name">The axis name.</param>
|
||||
public InputAxis(string name)
|
||||
{
|
||||
Input.AxisValueChanged += Handler;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
private void Handler(string name)
|
||||
{
|
||||
if (string.Equals(Name, name, StringComparison.OrdinalIgnoreCase))
|
||||
ValueChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="InputAxis"/> class.
|
||||
/// </summary>
|
||||
~InputAxis()
|
||||
{
|
||||
Input.AxisValueChanged -= Handler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases this object.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Input.AxisValueChanged -= Handler;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,36 @@ namespace FlaxEngine
|
||||
public string Name;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the event has been triggered during the current frame (e.g. user pressed a key). Use <see cref="Triggered"/> to catch events without active waiting.
|
||||
/// Returns true if the event has been triggered during the current frame (e.g. user pressed a key). Use <see cref="Pressed"/> to catch events without active waiting.
|
||||
/// </summary>
|
||||
public bool Active => Input.GetAction(Name);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the event state. Use Use <see cref="Pressed"/>, <see cref="Pressing"/>, <see cref="Released"/> to catch events without active waiting.
|
||||
/// </summary>
|
||||
public InputActionState State => Input.GetActionState(Name);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when event is triggered (e.g. user pressed a key). Called before scripts update.
|
||||
/// </summary>
|
||||
[System.Obsolete("Depreciated in 1.7, use Pressed Action.")]
|
||||
public event Action Triggered;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when event is pressed (e.g. user pressed a key). Called before scripts update.
|
||||
/// </summary>
|
||||
public event Action Pressed;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when event is being pressing (e.g. user pressing a key). Called before scripts update.
|
||||
/// </summary>
|
||||
public event Action Pressing;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when event is released (e.g. user releases a key). Called before scripts update.
|
||||
/// </summary>
|
||||
public event Action Released;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InputEvent"/> class.
|
||||
/// </summary>
|
||||
@@ -51,10 +72,26 @@ namespace FlaxEngine
|
||||
Input.ActionTriggered -= Handler;
|
||||
}
|
||||
|
||||
private void Handler(string name)
|
||||
private void Handler(string name, InputActionState state)
|
||||
{
|
||||
if (string.Equals(name, Name, StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.Equals(name, Name, StringComparison.OrdinalIgnoreCase))
|
||||
return;
|
||||
switch (state)
|
||||
{
|
||||
case InputActionState.None: break;
|
||||
case InputActionState.Waiting: break;
|
||||
case InputActionState.Pressing:
|
||||
Pressing?.Invoke();
|
||||
break;
|
||||
case InputActionState.Press:
|
||||
Triggered?.Invoke();
|
||||
Pressed?.Invoke();
|
||||
break;
|
||||
case InputActionState.Release:
|
||||
Released?.Invoke();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -130,7 +130,7 @@ void FoliageType::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
|
||||
SERIALIZE(Model);
|
||||
|
||||
const std::function<bool(const ModelInstanceEntry&)> IsValidMaterial = [](const ModelInstanceEntry& e) -> bool
|
||||
const Function<bool(const ModelInstanceEntry&)> IsValidMaterial = [](const ModelInstanceEntry& e) -> bool
|
||||
{
|
||||
return e.Material;
|
||||
};
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
const auto parentModelIndex = node.ParentIndex;
|
||||
|
||||
// Find matching node in skeleton (or map to best parent)
|
||||
const std::function<bool(const T&)> f = [node](const T& x) -> bool
|
||||
const Function<bool(const T&)> f = [node](const T& x) -> bool
|
||||
{
|
||||
return x.Name == node.Name;
|
||||
};
|
||||
|
||||
@@ -459,9 +459,9 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des
|
||||
{
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
||||
{
|
||||
const VkSampler sampler = _samplerHandles[slot];
|
||||
ASSERT(sampler);
|
||||
needsWrite |= dsWriter.WriteSampler(descriptorIndex, sampler, index);
|
||||
const VkSampler handle = _samplerHandles[slot];
|
||||
ASSERT(handle);
|
||||
needsWrite |= dsWriter.WriteSampler(descriptorIndex, handle, index);
|
||||
break;
|
||||
}
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
@@ -547,12 +547,18 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des
|
||||
}
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
||||
{
|
||||
auto cb = handles[slot];
|
||||
ASSERT(cb);
|
||||
VkBuffer buffer;
|
||||
VkDeviceSize offset, range;
|
||||
uint32 dynamicOffset;
|
||||
cb->DescriptorAsDynamicUniformBuffer(this, buffer, offset, range, dynamicOffset);
|
||||
auto handle = handles[slot];
|
||||
VkBuffer buffer = VK_NULL_HANDLE;
|
||||
VkDeviceSize offset = 0, range = 0;
|
||||
uint32 dynamicOffset = 0;
|
||||
if (handle)
|
||||
handle->DescriptorAsDynamicUniformBuffer(this, buffer, offset, range, dynamicOffset);
|
||||
else
|
||||
{
|
||||
const auto dummy = _device->HelperResources.GetDummyBuffer();
|
||||
buffer = dummy->GetHandle();
|
||||
range = dummy->GetSize();
|
||||
}
|
||||
needsWrite |= dsWriter.WriteDynamicUniformBuffer(descriptorIndex, buffer, offset, range, dynamicOffset, index);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,10 @@ static const char* GValidationLayers[] =
|
||||
|
||||
static const char* GInstanceExtensions[] =
|
||||
{
|
||||
#if PLATFORM_APPLE_FAMILY && defined(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)
|
||||
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
|
||||
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
|
||||
#endif
|
||||
#if VK_EXT_validation_cache
|
||||
VK_EXT_VALIDATION_CACHE_EXTENSION_NAME,
|
||||
#endif
|
||||
@@ -46,6 +50,9 @@ static const char* GInstanceExtensions[] =
|
||||
|
||||
static const char* GDeviceExtensions[] =
|
||||
{
|
||||
#if PLATFORM_APPLE_FAMILY && defined(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)
|
||||
VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
|
||||
#endif
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||
#if VK_KHR_maintenance1
|
||||
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
|
||||
@@ -571,7 +578,7 @@ void GPUDeviceVulkan::ParseOptionalDeviceExtensions(const Array<const char*>& de
|
||||
|
||||
const auto HasExtension = [&deviceExtensions](const char* name) -> bool
|
||||
{
|
||||
const std::function<bool(const char* const&)> CheckCallback = [&name](const char* const& extension) -> bool
|
||||
const Function<bool(const char* const&)> CheckCallback = [&name](const char* const& extension) -> bool
|
||||
{
|
||||
return StringUtils::Compare(extension, name) == 0;
|
||||
};
|
||||
|
||||
@@ -431,7 +431,7 @@ void DeferredDeletionQueueVulkan::EnqueueGenericResource(Type type, uint64 handl
|
||||
ScopeLock lock(_locker);
|
||||
|
||||
#if BUILD_DEBUG
|
||||
const std::function<bool(const Entry&)> ContainsHandle = [handle](const Entry& e)
|
||||
const Function<bool(const Entry&)> ContainsHandle = [handle](const Entry& e)
|
||||
{
|
||||
return e.Handle == handle;
|
||||
};
|
||||
@@ -868,7 +868,7 @@ GPUBufferVulkan* HelperResourcesVulkan::GetDummyBuffer()
|
||||
if (!_dummyBuffer)
|
||||
{
|
||||
_dummyBuffer = (GPUBufferVulkan*)_device->CreateBuffer(TEXT("DummyBuffer"));
|
||||
_dummyBuffer->Init(GPUBufferDescription::Buffer(sizeof(int32), GPUBufferFlags::ShaderResource | GPUBufferFlags::UnorderedAccess, PixelFormat::R32_SInt));
|
||||
_dummyBuffer->Init(GPUBufferDescription::Buffer(sizeof(int32) * 256, GPUBufferFlags::ShaderResource | GPUBufferFlags::UnorderedAccess, PixelFormat::R32_SInt));
|
||||
}
|
||||
|
||||
return _dummyBuffer;
|
||||
@@ -1078,13 +1078,16 @@ GPUDevice* GPUDeviceVulkan::Create()
|
||||
|
||||
VkInstanceCreateInfo instInfo;
|
||||
RenderToolsVulkan::ZeroStruct(instInfo, VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO);
|
||||
#if PLATFORM_APPLE_FAMILY
|
||||
instInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||
#endif
|
||||
instInfo.pApplicationInfo = &appInfo;
|
||||
|
||||
GetInstanceLayersAndExtensions(InstanceExtensions, InstanceLayers, SupportsDebugUtilsExt);
|
||||
|
||||
const auto hasExtension = [](const Array<const char*>& extensions, const char* name) -> bool
|
||||
{
|
||||
const std::function<bool(const char* const&)> callback = [&name](const char* const& extension) -> bool
|
||||
const Function<bool(const char* const&)> callback = [&name](const char* const& extension) -> bool
|
||||
{
|
||||
return extension && StringUtils::Compare(extension, name) == 0;
|
||||
};
|
||||
|
||||
@@ -51,7 +51,8 @@ public sealed class VulkanSdk : Sdk
|
||||
var subDirs = Directory.GetDirectories(path);
|
||||
if (subDirs.Length != 0)
|
||||
{
|
||||
path = Path.Combine(subDirs[0], "macOS");
|
||||
Flax.Build.Utilities.SortVersionDirectories(subDirs);
|
||||
path = Path.Combine(subDirs.Last(), "macOS");
|
||||
if (Directory.Exists(path))
|
||||
vulkanSdk = path;
|
||||
}
|
||||
|
||||
@@ -41,4 +41,15 @@
|
||||
#define VMA_NOT_NULL
|
||||
#include <ThirdParty/VulkanMemoryAllocator/vk_mem_alloc.h>
|
||||
|
||||
#if PLATFORM_APPLE_FAMILY
|
||||
// Declare potentially missing extensions from newer SDKs
|
||||
#ifndef VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME
|
||||
#define VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME "VK_KHR_portability_enumeration"
|
||||
#define VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR 0x00000001
|
||||
#endif
|
||||
#ifndef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME
|
||||
#define VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME "VK_KHR_portability_subset"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -97,7 +97,8 @@ Action Input::MouseLeave;
|
||||
Delegate<const Float2&, int32> Input::TouchDown;
|
||||
Delegate<const Float2&, int32> Input::TouchMove;
|
||||
Delegate<const Float2&, int32> Input::TouchUp;
|
||||
Delegate<StringView> Input::ActionTriggered;
|
||||
Delegate<StringView, InputActionState> Input::ActionTriggered;
|
||||
Delegate<StringView> Input::AxisValueChanged;
|
||||
Array<ActionConfig> Input::ActionMappings;
|
||||
Array<AxisConfig> Input::AxisMappings;
|
||||
|
||||
@@ -1017,14 +1018,22 @@ void InputService::Update()
|
||||
Input::SetMousePosition(Screen::GetSize() * 0.5f);
|
||||
}
|
||||
|
||||
// Send events for the active actions (send events only in play mode)
|
||||
// Send events for the active actions and axes (send events only in play mode)
|
||||
if (!Time::GetGamePaused())
|
||||
{
|
||||
for (auto i = Axes.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (Math::NotNearEqual(i->Value.Value, i->Value.PrevKeyValue))
|
||||
{
|
||||
Input::AxisValueChanged(i->Key);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto i = Actions.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Value.Active)
|
||||
if (i->Value.State != InputActionState::Waiting)
|
||||
{
|
||||
Input::ActionTriggered(i->Key);
|
||||
Input::ActionTriggered(i->Key, i->Value.State);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,7 +293,13 @@ public:
|
||||
/// Event fired when virtual input action is triggered. Called before scripts update. See <see cref="ActionMappings"/> to edit configuration.
|
||||
/// </summary>
|
||||
/// <seealso cref="InputEvent"/>
|
||||
API_EVENT() static Delegate<StringView> ActionTriggered;
|
||||
API_EVENT() static Delegate<StringView, InputActionState> ActionTriggered;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when virtual input axis is changed. Called before scripts update. See <see cref="AxisMappings"/> to edit configuration.
|
||||
/// </summary>
|
||||
/// <seealso cref="InputAxis"/>
|
||||
API_EVENT() static Delegate<StringView> AxisValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the virtual action identified by name. Use <see cref="ActionMappings"/> to get the current config.
|
||||
|
||||
65
Source/Engine/Level/Components/MissingScript.h
Normal file
65
Source/Engine/Level/Components/MissingScript.h
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
#include "Engine/Core/Cache.h"
|
||||
#include "Engine/Scripting/Script.h"
|
||||
#include "Engine/Scripting/ScriptingObjectReference.h"
|
||||
#include "Engine/Serialization/JsonWriters.h"
|
||||
|
||||
/// <summary>
|
||||
/// Actor script component that represents missing script.
|
||||
/// </summary>
|
||||
API_CLASS(Attributes="HideInEditor") class FLAXENGINE_API MissingScript : public Script
|
||||
{
|
||||
API_AUTO_SERIALIZATION();
|
||||
DECLARE_SCRIPTING_TYPE(MissingScript);
|
||||
|
||||
private:
|
||||
ScriptingObjectReference<Script> _referenceScript;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Namespace and type name of missing script.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="ReadOnly") String MissingTypeName;
|
||||
|
||||
/// <summary>
|
||||
/// Missing script serialized data.
|
||||
/// </summary>
|
||||
API_FIELD(Hidden, Attributes="HideInEditor") String Data;
|
||||
|
||||
/// <summary>
|
||||
/// Field for assigning new script to transfer data to.
|
||||
/// </summary>
|
||||
API_PROPERTY() ScriptingObjectReference<Script> GetReferenceScript() const
|
||||
{
|
||||
return _referenceScript;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Field for assigning new script to transfer data to.
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetReferenceScript(const ScriptingObjectReference<Script>& value)
|
||||
{
|
||||
_referenceScript = value;
|
||||
if (Data.IsEmpty())
|
||||
return;
|
||||
rapidjson_flax::Document document;
|
||||
document.Parse(Data.ToStringAnsi().GetText());
|
||||
|
||||
auto modifier = Cache::ISerializeModifier.Get();
|
||||
_referenceScript->Deserialize(document, modifier.Value);
|
||||
|
||||
DeleteObject();
|
||||
}
|
||||
};
|
||||
|
||||
inline MissingScript::MissingScript(const SpawnParams& params)
|
||||
: Script(params)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "SceneObjectsFactory.h"
|
||||
#include "Components/MissingScript.h"
|
||||
#include "Engine/Level/Actor.h"
|
||||
#include "Engine/Level/Prefabs/Prefab.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
@@ -230,7 +231,8 @@ void SceneObjectsFactory::HandleObjectDeserializationError(const ISerializable::
|
||||
rapidjson_flax::StringBuffer buffer;
|
||||
PrettyJsonWriter writer(buffer);
|
||||
value.Accept(writer.GetWriter());
|
||||
LOG(Warning, "Failed to deserialize scene object from data: {0}", String(buffer.GetString()));
|
||||
String bufferStr(buffer.GetString());
|
||||
LOG(Warning, "Failed to deserialize scene object from data: {0}", bufferStr);
|
||||
|
||||
// Try to log some useful info about missing object (eg. it's parent name for faster fixing)
|
||||
const auto parentIdMember = value.FindMember("ParentID");
|
||||
@@ -240,6 +242,14 @@ void SceneObjectsFactory::HandleObjectDeserializationError(const ISerializable::
|
||||
Actor* parent = Scripting::FindObject<Actor>(parentId);
|
||||
if (parent)
|
||||
{
|
||||
#if USE_EDITOR
|
||||
// Add dummy script
|
||||
auto* dummyScript = parent->AddScript<MissingScript>();
|
||||
const auto parentIdMember = value.FindMember("TypeName");
|
||||
if (parentIdMember != value.MemberEnd() && parentIdMember->value.IsString())
|
||||
dummyScript->MissingTypeName = parentIdMember->value.GetString();
|
||||
dummyScript->Data = MoveTemp(bufferStr);
|
||||
#endif
|
||||
LOG(Warning, "Parent actor of the missing object: {0}", parent->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <uuid/uuid.h>
|
||||
@@ -316,6 +317,14 @@ bool ApplePlatform::Init()
|
||||
OnPlatformUserAdd(New<User>(username));
|
||||
}
|
||||
|
||||
// Increase the maximum number of simultaneously open files
|
||||
{
|
||||
struct rlimit limit;
|
||||
limit.rlim_cur = OPEN_MAX;
|
||||
limit.rlim_max = RLIM_INFINITY;
|
||||
setrlimit(RLIMIT_NOFILE, &limit);
|
||||
}
|
||||
|
||||
AutoreleasePool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
return false;
|
||||
|
||||
@@ -351,6 +351,34 @@ namespace FlaxEngine.Json
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize <see cref="Tag"/> as inlined text.
|
||||
/// </summary>
|
||||
/// <seealso cref="Newtonsoft.Json.JsonConverter" />
|
||||
internal class TagConverter : JsonConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
|
||||
{
|
||||
var tag = (Tag)value;
|
||||
writer.WriteValue(tag.ToString());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.String)
|
||||
return Tags.Get((string)reader.Value);
|
||||
return Tag.Default;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(Tag);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Serialize Guid values using `N` format
|
||||
|
||||
@@ -127,6 +127,7 @@ namespace FlaxEngine.Json
|
||||
settings.Converters.Add(new MarginConverter());
|
||||
settings.Converters.Add(new VersionConverter());
|
||||
settings.Converters.Add(new LocalizedStringConverter());
|
||||
settings.Converters.Add(new TagConverter());
|
||||
//settings.Converters.Add(new GuidConverter());
|
||||
return settings;
|
||||
}
|
||||
|
||||
@@ -705,8 +705,15 @@ bool ModelTool::ImportDataAssimp(const char* path, ImportedModelData& data, Opti
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create root node
|
||||
AssimpNode& rootNode = context->Nodes.AddOne();
|
||||
rootNode.ParentIndex = -1;
|
||||
rootNode.LodIndex = 0;
|
||||
rootNode.Name = TEXT("Root");
|
||||
rootNode.LocalTransform = Transform::Identity;
|
||||
|
||||
// Process imported scene nodes
|
||||
ProcessNodes(*context, context->Scene->mRootNode, -1);
|
||||
ProcessNodes(*context, context->Scene->mRootNode, 0);
|
||||
}
|
||||
DeleteMe<AssimpImporterData> contextCleanup(options.SplitContext ? nullptr : context);
|
||||
|
||||
@@ -822,7 +829,13 @@ bool ModelTool::ImportDataAssimp(const char* path, ImportedModelData& data, Opti
|
||||
const auto animations = context->Scene->mAnimations[animIndex];
|
||||
data.Animation.Channels.Resize(animations->mNumChannels, false);
|
||||
data.Animation.Duration = animations->mDuration;
|
||||
data.Animation.FramesPerSecond = animations->mTicksPerSecond != 0.0 ? animations->mTicksPerSecond : 25.0;
|
||||
data.Animation.FramesPerSecond = animations->mTicksPerSecond;
|
||||
if (data.Animation.FramesPerSecond <= 0)
|
||||
{
|
||||
data.Animation.FramesPerSecond = context->Options.DefaultFrameRate;
|
||||
if (data.Animation.FramesPerSecond <= 0)
|
||||
data.Animation.FramesPerSecond = 30.0f;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < animations->mNumChannels; i++)
|
||||
{
|
||||
|
||||
@@ -1474,7 +1474,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
|
||||
|
||||
// Group meshes that can be merged together
|
||||
typedef Pair<int32, int32> MeshGroupKey;
|
||||
const std::function<MeshGroupKey(MeshData* const&)> f = [](MeshData* const& x) -> MeshGroupKey
|
||||
const Function<MeshGroupKey(MeshData* const&)> f = [](MeshData* const& x) -> MeshGroupKey
|
||||
{
|
||||
return MeshGroupKey(x->NodeIndex, x->MaterialSlotIndex);
|
||||
};
|
||||
|
||||
16
Source/Platforms/Mac/Default.entitlements
Normal file
16
Source/Platforms/Mac/Default.entitlements
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-executable-page-protection</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
Source/Platforms/Mac/Default.icns
Normal file
BIN
Source/Platforms/Mac/Default.icns
Normal file
Binary file not shown.
43
Source/Platforms/Mac/Default.plist
Normal file
43
Source/Platforms/Mac/Default.plist
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>icon.icns</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>{Executable}</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>FlaxEditor</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.flaxengine.editor</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>{Version}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>{Version}</string>
|
||||
<key>NSRequiresAquaSystemAppearance</key>
|
||||
<false/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>{Copyright}</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.developer-tools</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.15</string>
|
||||
<key>LSMinimumSystemVersionByArchitecture</key>
|
||||
<dict>
|
||||
<key>{Arch}</key>
|
||||
<string>10.15</string>
|
||||
</dict>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
46
Source/ThirdParty/OpenFBX/ofbx.cpp
vendored
46
Source/ThirdParty/OpenFBX/ofbx.cpp
vendored
@@ -365,6 +365,7 @@ float DataView::toFloat() const
|
||||
|
||||
bool DataView::operator==(const char* rhs) const
|
||||
{
|
||||
if (!begin) return !rhs[0];
|
||||
const char* c = rhs;
|
||||
const char* c2 = (const char*)begin;
|
||||
while (*c && c2 != (const char*)end)
|
||||
@@ -802,6 +803,14 @@ static OptionalError<Property*> readTextProperty(Cursor* cursor, Allocator& allo
|
||||
|
||||
prop->value.end = cursor->current;
|
||||
}
|
||||
else if (cursor->current < cursor->end && (*cursor->current == 'e' || *cursor->current == 'E')) {
|
||||
prop->type = 'D';
|
||||
// 10e-013
|
||||
++cursor->current;
|
||||
if (cursor->current < cursor->end && *cursor->current == '-') ++cursor->current;
|
||||
while (cursor->current < cursor->end && isdigit(*cursor->current)) ++cursor->current;
|
||||
prop->value.end = cursor->current;
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
@@ -1184,7 +1193,6 @@ struct GeometryImpl : Geometry
|
||||
const BlendShape* blendShape = nullptr;
|
||||
|
||||
std::vector<int> indices;
|
||||
std::vector<int> to_old_vertices;
|
||||
std::vector<NewVertex> to_new_vertices;
|
||||
|
||||
GeometryImpl(const Scene& _scene, const IElement& _element)
|
||||
@@ -1765,7 +1773,7 @@ struct AnimationLayerImpl : AnimationLayer
|
||||
{
|
||||
for (const AnimationCurveNodeImpl* node : curve_nodes)
|
||||
{
|
||||
if (node->bone_link_property == prop && node->bone == &bone) return node;
|
||||
if (node->bone_link_property.begin && node->bone_link_property == prop && node->bone == &bone) return node;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -2474,6 +2482,15 @@ static void triangulate(
|
||||
++in_polygon_idx;
|
||||
if (old_indices[i] < 0)
|
||||
{
|
||||
if (in_polygon_idx <= 2) {
|
||||
// invalid polygon, let's pop it
|
||||
to_old_vertices->pop_back();
|
||||
to_old_indices->pop_back();
|
||||
if (in_polygon_idx == 2) {
|
||||
to_old_vertices->pop_back();
|
||||
to_old_indices->pop_back();
|
||||
}
|
||||
}
|
||||
in_polygon_idx = 0;
|
||||
}
|
||||
}
|
||||
@@ -2487,20 +2504,22 @@ static void buildGeometryVertexData(
|
||||
std::vector<int>& to_old_indices,
|
||||
bool triangulationEnabled)
|
||||
{
|
||||
std::vector<int> to_old_vertices;
|
||||
|
||||
if (triangulationEnabled) {
|
||||
triangulate(original_indices, &geom->to_old_vertices, &to_old_indices);
|
||||
geom->vertices.resize(geom->to_old_vertices.size());
|
||||
triangulate(original_indices, &to_old_vertices, &to_old_indices);
|
||||
geom->vertices.resize(to_old_vertices.size());
|
||||
geom->indices.resize(geom->vertices.size());
|
||||
for (int i = 0, c = (int)geom->to_old_vertices.size(); i < c; ++i)
|
||||
for (int i = 0, c = (int)to_old_vertices.size(); i < c; ++i)
|
||||
{
|
||||
geom->vertices[i] = vertices[geom->to_old_vertices[i]];
|
||||
geom->vertices[i] = vertices[to_old_vertices[i]];
|
||||
geom->indices[i] = codeIndex(i, i % 3 == 2);
|
||||
}
|
||||
} else {
|
||||
geom->vertices = vertices;
|
||||
geom->to_old_vertices.resize(original_indices.size());
|
||||
to_old_vertices.resize(original_indices.size());
|
||||
for (size_t i = 0; i < original_indices.size(); ++i) {
|
||||
geom->to_old_vertices[i] = decodeIndex(original_indices[i]);
|
||||
to_old_vertices[i] = decodeIndex(original_indices[i]);
|
||||
}
|
||||
geom->indices = original_indices;
|
||||
to_old_indices.resize(original_indices.size());
|
||||
@@ -2508,8 +2527,7 @@ static void buildGeometryVertexData(
|
||||
}
|
||||
|
||||
geom->to_new_vertices.resize(vertices.size()); // some vertices can be unused, so this isn't necessarily the same size as to_old_vertices.
|
||||
const int* to_old_vertices = geom->to_old_vertices.empty() ? nullptr : &geom->to_old_vertices[0];
|
||||
for (int i = 0, c = (int)geom->to_old_vertices.size(); i < c; ++i)
|
||||
for (int i = 0, c = (int)to_old_vertices.size(); i < c; ++i)
|
||||
{
|
||||
int old = to_old_vertices[i];
|
||||
add(geom->to_new_vertices[old], i);
|
||||
@@ -2736,7 +2754,7 @@ bool ShapeImpl::postprocess(GeometryImpl* geom, Allocator& allocator)
|
||||
allocator.vec3_tmp2.clear(); // old normals
|
||||
allocator.int_tmp.clear(); // old indices
|
||||
if (!parseDoubleVecData(*vertices_element->first_property, &allocator.vec3_tmp, &allocator.tmp)) return true;
|
||||
if (!parseDoubleVecData(*normals_element->first_property, &allocator.vec3_tmp2, &allocator.tmp)) return true;
|
||||
if (normals_element && !parseDoubleVecData(*normals_element->first_property, &allocator.vec3_tmp2, &allocator.tmp)) return true;
|
||||
if (!parseBinaryArray(*indexes_element->first_property, &allocator.int_tmp)) return true;
|
||||
|
||||
if (allocator.vec3_tmp.size() != allocator.int_tmp.size() || allocator.vec3_tmp2.size() != allocator.int_tmp.size()) return false;
|
||||
@@ -2745,7 +2763,7 @@ bool ShapeImpl::postprocess(GeometryImpl* geom, Allocator& allocator)
|
||||
normals = geom->normals;
|
||||
|
||||
Vec3* vr = &allocator.vec3_tmp[0];
|
||||
Vec3* nr = &allocator.vec3_tmp2[0];
|
||||
Vec3* nr = normals_element ? &allocator.vec3_tmp2[0] : nullptr;
|
||||
int* ir = &allocator.int_tmp[0];
|
||||
for (int i = 0, c = (int)allocator.int_tmp.size(); i < c; ++i)
|
||||
{
|
||||
@@ -2755,7 +2773,7 @@ bool ShapeImpl::postprocess(GeometryImpl* geom, Allocator& allocator)
|
||||
while (n)
|
||||
{
|
||||
vertices[n->index] = vertices[n->index] + vr[i];
|
||||
normals[n->index] = normals[n->index] + nr[i];
|
||||
if (normals_element) normals[n->index] = normals[n->index] + nr[i];
|
||||
n = n->next;
|
||||
}
|
||||
}
|
||||
@@ -3561,7 +3579,7 @@ Object* Object::getParent() const
|
||||
if (connection.from == id)
|
||||
{
|
||||
Object* obj = scene.m_object_map.find(connection.to)->second.object;
|
||||
if (obj && obj->is_node)
|
||||
if (obj && obj->is_node && obj != this && connection.type == Scene::Connection::OBJECT_OBJECT)
|
||||
{
|
||||
assert(parent == nullptr);
|
||||
parent = obj;
|
||||
|
||||
@@ -244,7 +244,19 @@ namespace Flax.Build
|
||||
/// <returns>The toolchain.</returns>
|
||||
public Toolchain TryGetToolchain(TargetArchitecture targetArchitecture)
|
||||
{
|
||||
return HasRequiredSDKsInstalled ? GetToolchain(targetArchitecture) : null;
|
||||
Toolchain result = null;
|
||||
if (HasRequiredSDKsInstalled)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = GetToolchain(targetArchitecture);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Exception(ex);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -260,16 +272,12 @@ namespace Flax.Build
|
||||
if (_toolchains == null)
|
||||
_toolchains = new Dictionary<TargetArchitecture, Toolchain>();
|
||||
|
||||
var key = targetArchitecture;
|
||||
|
||||
Toolchain toolchain;
|
||||
if (_toolchains.TryGetValue(key, out toolchain))
|
||||
{
|
||||
if (_toolchains.TryGetValue(targetArchitecture, out toolchain))
|
||||
return toolchain;
|
||||
}
|
||||
|
||||
toolchain = CreateToolchain(targetArchitecture);
|
||||
_toolchains.Add(key, toolchain);
|
||||
_toolchains.Add(targetArchitecture, toolchain);
|
||||
|
||||
return toolchain;
|
||||
}
|
||||
|
||||
@@ -39,12 +39,6 @@ namespace Flax.Build
|
||||
[CommandLine("deploy", "Runs the deploy tool.")]
|
||||
public static bool Deploy = false;
|
||||
|
||||
/// <summary>
|
||||
/// Compresses deployed files.
|
||||
/// </summary>
|
||||
[CommandLine("deployDontCompress", "Skips compressing deployed files, and keeps files.")]
|
||||
public static bool DontCompress = false;
|
||||
|
||||
/// <summary>
|
||||
/// Builds the targets. Builds all the targets, use <see cref="BuildTargets"/> to select a custom set of targets for the build.
|
||||
/// </summary>
|
||||
|
||||
@@ -9,6 +9,12 @@ namespace Flax.Build
|
||||
{
|
||||
public static partial class Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Compresses deployed files.
|
||||
/// </summary>
|
||||
[CommandLine("deployDontCompress", "Skips compressing deployed files, and keeps files.")]
|
||||
public static bool DontCompress = false;
|
||||
|
||||
/// <summary>
|
||||
/// Package deployment output path.
|
||||
/// </summary>
|
||||
@@ -28,9 +34,9 @@ namespace Flax.Build
|
||||
public static bool DeployPlatforms;
|
||||
|
||||
/// <summary>
|
||||
/// Certificate file path for binaries signing.
|
||||
/// Certificate file path for binaries signing. Or sign identity for Apple platforms.
|
||||
/// </summary>
|
||||
[CommandLine("deployCert", "Certificate file path for binaries signing.")]
|
||||
[CommandLine("deployCert", "Certificate file path for binaries signing. Or sign identity for Apple platforms.")]
|
||||
public static string DeployCert;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -6,6 +6,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Flax.Build;
|
||||
using Flax.Build.Platforms;
|
||||
|
||||
@@ -17,11 +18,15 @@ namespace Flax.Deploy
|
||||
{
|
||||
if (string.IsNullOrEmpty(Configuration.DeployCert))
|
||||
return;
|
||||
Log.Info("Code signing file: " + file);
|
||||
switch (Platform.BuildTargetPlatform)
|
||||
{
|
||||
case TargetPlatform.Windows:
|
||||
VCEnvironment.CodeSign(file, Configuration.DeployCert, Configuration.DeployCertPass);
|
||||
break;
|
||||
case TargetPlatform.Mac:
|
||||
MacPlatform.CodeSign(file, Configuration.DeployCert);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +127,45 @@ namespace Flax.Deploy
|
||||
// Deploy project
|
||||
DeployFile(RootPath, OutputPath, "Flax.flaxproj");
|
||||
|
||||
// Package Editor into macOS app
|
||||
if (Platform.BuildTargetPlatform == TargetPlatform.Mac)
|
||||
{
|
||||
var arch = Platform.BuildTargetArchitecture;
|
||||
Log.Info(string.Empty);
|
||||
Log.Info("Creating macOS app...");
|
||||
var appPath = Path.Combine(Deployer.PackageOutputPath, "FlaxEditor.app");
|
||||
var appContentsPath = Path.Combine(appPath, "Contents");
|
||||
var appBinariesPath = Path.Combine(appContentsPath, "MacOS");
|
||||
Utilities.DirectoryDelete(appPath);
|
||||
Directory.CreateDirectory(appPath);
|
||||
Directory.CreateDirectory(appContentsPath);
|
||||
Directory.CreateDirectory(appBinariesPath);
|
||||
|
||||
// Copy icons set
|
||||
Directory.CreateDirectory(Path.Combine(appContentsPath, "Resources"));
|
||||
Utilities.FileCopy(Path.Combine(Globals.EngineRoot, "Source/Platforms/Mac/Default.icns"), Path.Combine(appContentsPath, "Resources/icon.icns"));
|
||||
|
||||
// Create PkgInfo file
|
||||
File.WriteAllText(Path.Combine(appContentsPath, "PkgInfo"), "APPL???", Encoding.ASCII);
|
||||
|
||||
// Create Info.plist
|
||||
var plist = File.ReadAllText(Path.Combine(Globals.EngineRoot, "Source/Platforms/Mac/Default.plist"));
|
||||
var flaxProject = EngineTarget.EngineProject;
|
||||
plist = plist.Replace("{Version}", flaxProject.Version.ToString());
|
||||
plist = plist.Replace("{Copyright}", flaxProject.Copyright);
|
||||
plist = plist.Replace("{Executable}", "FlaxEditor");
|
||||
plist = plist.Replace("{Arch}", arch == TargetArchitecture.ARM64 ? "arm64" : "x86_64");
|
||||
File.WriteAllText(Path.Combine(appContentsPath, "Info.plist"), plist, Encoding.ASCII);
|
||||
|
||||
// Copy output editor files
|
||||
Utilities.DirectoryCopy(OutputPath, appContentsPath);
|
||||
|
||||
// Copy native binaries for app execution
|
||||
var defaultEditorConfig = "Development";
|
||||
var ediotrBinariesPath = Path.Combine(appContentsPath, "Binaries/Editor/Mac", defaultEditorConfig);
|
||||
Utilities.DirectoryCopy(ediotrBinariesPath, appBinariesPath, true, true);
|
||||
}
|
||||
|
||||
// Compress
|
||||
if (Configuration.DontCompress)
|
||||
return;
|
||||
@@ -254,6 +298,10 @@ namespace Flax.Deploy
|
||||
Utilities.Run("strip", "FlaxEditor", null, dst, Utilities.RunOptions.None);
|
||||
Utilities.Run("strip", "FlaxEditor.dylib", null, dst, Utilities.RunOptions.None);
|
||||
Utilities.Run("strip", "libMoltenVK.dylib", null, dst, Utilities.RunOptions.None);
|
||||
|
||||
CodeSign(Path.Combine(dst, "FlaxEditor"));
|
||||
CodeSign(Path.Combine(dst, "FlaxEditor.dylib"));
|
||||
CodeSign(Path.Combine(dst, "libMoltenVK.dylib"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,19 +42,7 @@ namespace Flax.Build.Platforms
|
||||
var subdirs = Directory.GetDirectories(Path.Combine(AndroidSdk.Instance.RootPath, "ndk"));
|
||||
if (subdirs.Length != 0)
|
||||
{
|
||||
Array.Sort(subdirs, (a, b) =>
|
||||
{
|
||||
Version va, vb;
|
||||
if (Version.TryParse(a, out va))
|
||||
{
|
||||
if (Version.TryParse(b, out vb))
|
||||
return va.CompareTo(vb);
|
||||
return 1;
|
||||
}
|
||||
if (Version.TryParse(b, out vb))
|
||||
return -1;
|
||||
return 0;
|
||||
});
|
||||
Utilities.SortVersionDirectories(subdirs);
|
||||
sdkPath = subdirs.Last();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Flax.Build.Platforms
|
||||
public LinuxPlatform()
|
||||
{
|
||||
// Try to use system compiler
|
||||
if (Platform.BuildTargetPlatform == TargetPlatform.Linux)
|
||||
if (BuildTargetPlatform == TargetPlatform.Linux)
|
||||
{
|
||||
// Pick the newest compiler (overriden by specified in command line)
|
||||
if (Which(Configuration.Compiler) != null)
|
||||
@@ -67,7 +67,7 @@ namespace Flax.Build.Platforms
|
||||
Log.Verbose($"Using native Linux toolchain (compiler {Compiler})");
|
||||
HasRequiredSDKsInstalled = true;
|
||||
}
|
||||
else if (Platform.BuildTargetPlatform != TargetPlatform.Mac)
|
||||
else if (BuildTargetPlatform != TargetPlatform.Mac)
|
||||
{
|
||||
// Check if Linux toolchain is installed
|
||||
string toolchainName = "v13_clang-7.0.1-centos7";
|
||||
@@ -76,9 +76,11 @@ namespace Flax.Build.Platforms
|
||||
{
|
||||
if (string.IsNullOrEmpty(toolchainsRoot))
|
||||
{
|
||||
Log.Warning("Missing Linux Toolchain. Cannot build for Linux platform.");
|
||||
if (BuildTargetPlatform == TargetPlatform.Linux)
|
||||
Log.Warning("Missing Linux Toolchain. Cannot build for Linux platform.");
|
||||
else
|
||||
Log.Verbose("Missing Linux Toolchain. Cannot build for Linux platform.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Flax.Build.Platforms
|
||||
@@ -44,6 +46,24 @@ namespace Flax.Build.Platforms
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs codesign tool on macOS to sign the code with a given identity from local keychain.
|
||||
/// </summary>
|
||||
/// <param name="file">Path to file to codesign.</param>
|
||||
/// <param name="signIdenity">App code signing idenity name (from local Mac keychain). Use 'security find-identity -v -p codesigning' to list possible options.</param>
|
||||
public static void CodeSign(string file, string signIdenity)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
throw new FileNotFoundException("Missing file to sign.", file);
|
||||
string cmdLine = string.Format("--force --timestamp -s \"{0}\" \"{1}\"", signIdenity, file);
|
||||
if (string.IsNullOrEmpty(Path.GetExtension(file)))
|
||||
{
|
||||
// Add entitlements file with some settings for the app execution
|
||||
cmdLine += string.Format(" --entitlements \"{0}\"", Path.Combine(Globals.EngineRoot, "Source/Platforms/Mac/Default.entitlements"));
|
||||
}
|
||||
Utilities.Run("codesign", cmdLine, null, null, Utilities.RunOptions.Default | Utilities.RunOptions.ThrowExceptionOnError);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if running an x64 binary an arm64 host machine.
|
||||
/// </summary>
|
||||
|
||||
@@ -24,6 +24,13 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
/// <inheritdoc />
|
||||
public override void Generate(string solutionPath)
|
||||
{
|
||||
// Try to reuse the existing project guid from existing files
|
||||
ProjectGuid = GetProjectGuid(Path, Name);
|
||||
if (ProjectGuid == Guid.Empty)
|
||||
ProjectGuid = GetProjectGuid(solutionPath, Name);
|
||||
if (ProjectGuid == Guid.Empty)
|
||||
ProjectGuid = Guid.NewGuid();
|
||||
|
||||
var gen = (VisualStudioProjectGenerator)Generator;
|
||||
var projectFileToolVersion = gen.ProjectFileToolVersion;
|
||||
var vcProjectFileContent = new StringBuilder();
|
||||
@@ -500,7 +507,15 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
firstEditorMatch = i;
|
||||
}
|
||||
}
|
||||
if (firstFullMatch != -1)
|
||||
if (project is AndroidProject)
|
||||
{
|
||||
// Utility Android deploy project only for exact match
|
||||
if (firstFullMatch != -1)
|
||||
projectConfiguration = configuration;
|
||||
else
|
||||
projectConfiguration = new SolutionConfiguration(project.Configurations[0]);
|
||||
}
|
||||
else if (firstFullMatch != -1)
|
||||
{
|
||||
projectConfiguration = configuration;
|
||||
build = solution.MainProject == project || (solution.MainProject == null && project.Name == solution.Name);
|
||||
|
||||
@@ -758,8 +758,30 @@ namespace Flax.Build
|
||||
{
|
||||
extEnding = "";
|
||||
}
|
||||
|
||||
return extEnding;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sorts the directories by name assuming they contain version text. Sorted from lowest to the highest version.
|
||||
/// </summary>
|
||||
/// <param name="paths">The paths array to sort.</param>
|
||||
public static void SortVersionDirectories(string[] paths)
|
||||
{
|
||||
if (paths == null || paths.Length == 0)
|
||||
return;
|
||||
Array.Sort(paths, (a, b) =>
|
||||
{
|
||||
Version va, vb;
|
||||
if (Version.TryParse(a, out va))
|
||||
{
|
||||
if (Version.TryParse(b, out vb))
|
||||
return va.CompareTo(vb);
|
||||
return 1;
|
||||
}
|
||||
if (Version.TryParse(b, out vb))
|
||||
return -1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user