Merge remote-tracking branch 'origin/master' into gi

# Conflicts:
#	Flax.flaxproj
#	Source/Engine/Core/Math/Vector3.h
#	Source/Engine/Graphics/Textures/GPUTexture.cpp
#	Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp
#	Source/Engine/Terrain/Terrain.cpp
#	Source/Engine/Tools/ModelTool/ModelTool.Build.cs
#	Source/FlaxEngine.Gen.cs
#	Source/FlaxEngine.Gen.h
This commit is contained in:
Wojciech Figat
2022-03-22 13:00:21 +01:00
49 changed files with 647 additions and 163 deletions

View File

@@ -6,7 +6,7 @@ jobs:
# Editor
editor-windows:
name: Editor (Windows, Development x64)
runs-on: "windows-latest"
runs-on: "windows-2019"
steps:
- name: Checkout repo
uses: actions/checkout@v2
@@ -23,7 +23,7 @@ jobs:
# Game
game-windows:
name: Game (Windows, Release x64)
runs-on: "windows-latest"
runs-on: "windows-2019"
steps:
- name: Checkout repo
uses: actions/checkout@v2

View File

@@ -9,7 +9,7 @@ jobs:
# Windows
package-windows-editor:
name: Editor (Windows)
runs-on: "windows-latest"
runs-on: "windows-2019"
steps:
- name: Checkout repo
uses: actions/checkout@v2
@@ -34,7 +34,7 @@ jobs:
path: Output/EditorDebugSymbols.zip
package-windows-game:
name: Game (Windows)
runs-on: "windows-latest"
runs-on: "windows-2019"
steps:
- name: Checkout repo
uses: actions/checkout@v2

View File

@@ -163,6 +163,12 @@ namespace FlaxEditor.Content.Import
[EditorOrder(90), DefaultValue(ModelLightmapUVsSource.Disable), EditorDisplay("Geometry", "Lightmap UVs Source"), Tooltip("Model lightmap UVs source")]
public ModelLightmapUVsSource LightmapUVsSource { get; set; } = ModelLightmapUVsSource.Disable;
/// <summary>
/// If specified, all meshes which name starts with this prefix will be imported as a separate collision data (excluded used for rendering).
/// </summary>
[EditorOrder(100), DefaultValue(""), EditorDisplay("Geometry")]
public string CollisionMeshesPrefix { get; set; }
/// <summary>
/// Custom uniform import scale.
/// </summary>
@@ -284,7 +290,7 @@ namespace FlaxEditor.Content.Import
/// </summary>
[EditorOrder(420), DefaultValue(true), EditorDisplay("Materials", "Restore Materials On Reimport"), Tooltip("If checked, the importer will try to restore the assigned materials to the model slots.")]
public bool RestoreMaterialsOnReimport { get; set; } = true;
/// <summary>
/// If checked, the imported mesh/animations are splitted into separate assets. Used if ObjectIndex is set to -1.
/// </summary>
@@ -314,6 +320,7 @@ namespace FlaxEditor.Content.Import
public byte ImportVertexColors;
public byte ImportBlendShapes;
public ModelLightmapUVsSource LightmapUVsSource;
public string CollisionMeshesPrefix;
// Transform
public float Scale;
@@ -364,6 +371,7 @@ namespace FlaxEditor.Content.Import
ImportVertexColors = (byte)(ImportVertexColors ? 1 : 0),
ImportBlendShapes = (byte)(ImportBlendShapes ? 1 : 0),
LightmapUVsSource = LightmapUVsSource,
CollisionMeshesPrefix = CollisionMeshesPrefix,
Scale = Scale,
Rotation = Rotation,
Translation = Translation,
@@ -403,6 +411,7 @@ namespace FlaxEditor.Content.Import
ImportVertexColors = options.ImportVertexColors != 0;
ImportBlendShapes = options.ImportBlendShapes != 0;
LightmapUVsSource = options.LightmapUVsSource;
CollisionMeshesPrefix = options.CollisionMeshesPrefix;
Scale = options.Scale;
Rotation = options.Rotation;
Translation = options.Translation;

View File

@@ -86,6 +86,7 @@ namespace FlaxEditor.Content
{
foreach (var child in modelItem.ParentFolder.Children)
{
// Check if there is collision that was made with this model
if (child is BinaryAssetItem b && b.IsOfType<CollisionData>())
{
var collisionData = FlaxEngine.Content.Load<CollisionData>(b.ID);
@@ -97,6 +98,25 @@ namespace FlaxEditor.Content
return;
}
}
// Check if there is a auto-imported collision
if (child is ContentFolder childFolder && childFolder.ShortName == modelItem.ShortName)
{
foreach (var childFolderChild in childFolder.Children)
{
if (childFolderChild is BinaryAssetItem c && c.IsOfType<CollisionData>())
{
var collisionData = FlaxEngine.Content.Load<CollisionData>(c.ID);
if (collisionData && (collisionData.Options.Model == model.ID || collisionData.Options.Model == Guid.Empty))
{
Editor.Instance.Windows.ContentWin.Select(c);
if (created != null)
FlaxEngine.Scripting.InvokeOnUpdate(() => created(collisionData));
return;
}
}
}
}
}
}

View File

@@ -229,7 +229,13 @@ namespace FlaxEditor.CustomEditors
for (int i = 0; i < Count; i++)
{
if (!Equals(this[i], _defaultValue))
{
// Special case for String (null string is kind of equal to empty string from the user perspective)
if (this[i] == null && _defaultValue is string defaultValueStr && defaultValueStr.Length == 0)
continue;
return true;
}
}
}
return false;

View File

@@ -227,8 +227,8 @@ namespace FlaxEditor.GUI.Tabs
/// </summary>
public Tab SelectedTab
{
get => _selectedIndex == -1 ? null : Children[_selectedIndex + 1] as Tab;
set => SelectedTabIndex = Children.IndexOf(value) - 1;
get => _selectedIndex == -1 && Children.Count > _selectedIndex + 1 ? null : Children[_selectedIndex + 1] as Tab;
set => SelectedTabIndex = value != null ? Children.IndexOf(value) - 1 : -1;
}
/// <summary>

View File

@@ -166,6 +166,7 @@ struct InternalModelOptions
byte ImportVertexColors;
byte ImportBlendShapes;
ModelLightmapUVsSource LightmapUVsSource;
MonoString* CollisionMeshesPrefix;
// Transform
float Scale;
@@ -213,6 +214,7 @@ struct InternalModelOptions
to->ImportVertexColors = from->ImportVertexColors;
to->ImportBlendShapes = from->ImportBlendShapes;
to->LightmapUVsSource = from->LightmapUVsSource;
to->CollisionMeshesPrefix = MUtils::ToString(from->CollisionMeshesPrefix);
to->Scale = from->Scale;
to->Rotation = from->Rotation;
to->Translation = from->Translation;
@@ -251,6 +253,7 @@ struct InternalModelOptions
to->ImportVertexColors = from->ImportVertexColors;
to->ImportBlendShapes = from->ImportBlendShapes;
to->LightmapUVsSource = from->LightmapUVsSource;
to->CollisionMeshesPrefix = MUtils::ToString(from->CollisionMeshesPrefix);
to->Scale = from->Scale;
to->Rotation = from->Rotation;
to->Translation = from->Translation;

View File

@@ -93,6 +93,11 @@ namespace FlaxEditor.Options
public void Load()
{
Editor.Log("Loading editor options");
if (!File.Exists(_optionsFilePath))
{
Editor.LogWarning("Missing editor settings");
return;
}
try
{
@@ -100,12 +105,12 @@ namespace FlaxEditor.Options
var asset = FlaxEngine.Content.LoadAsync<JsonAsset>(_optionsFilePath);
if (asset == null)
{
Editor.LogWarning("Missing or invalid editor settings");
Editor.LogWarning("Invalid editor settings");
return;
}
if (asset.WaitForLoaded())
{
Editor.LogWarning("Failed to load editor settings");
Editor.LogError("Failed to load editor settings");
return;
}

View File

@@ -180,7 +180,22 @@ void CodeEditingManager::OpenSolution(CodeEditorTypes editorType)
const auto editor = GetCodeEditor(editorType);
if (editor)
{
OpenSolution(editor);
// Ensure that no async task is running
if (IsAsyncOpenRunning())
{
// TODO: enqueue action and handle many actions in the queue
LOG(Warning, "Cannot use code editor during async open action.");
return;
}
if (editor->UseAsyncForOpen())
{
AsyncOpenTask::OpenSolution(editor);
}
else
{
editor->OpenSolution();
}
}
else
{
@@ -201,26 +216,6 @@ void CodeEditingManager::OnFileAdded(CodeEditorTypes editorType, const String& p
}
}
void CodeEditingManager::OpenSolution(CodeEditor* editor)
{
// Ensure that no async task is running
if (IsAsyncOpenRunning())
{
// TODO: enqueue action and handle many actions in the queue
LOG(Warning, "Cannot use code editor during async open action.");
return;
}
if (editor->UseAsyncForOpen())
{
AsyncOpenTask::OpenSolution(editor);
}
else
{
editor->OpenSolution();
}
}
void OnAsyncBegin(Thread* thread)
{
ASSERT(AsyncOpenThread == nullptr);

View File

@@ -193,12 +193,6 @@ public:
/// <param name="path">The path.</param>
API_FUNCTION() static void OnFileAdded(CodeEditorTypes editorType, const String& path);
/// <summary>
/// Opens the solution project. Handles async opening.
/// </summary>
/// <param name="editor">The code editor.</param>
static void OpenSolution(CodeEditor* editor);
/// <summary>
/// The asynchronous open begins.
/// </summary>

View File

@@ -156,7 +156,7 @@ bool ScriptsBuilder::IsSourceWorkspaceDirty()
return _wasProjectStructureChanged;
}
bool ScriptsBuilder::IsSourceDirty(const TimeSpan& timeout)
bool ScriptsBuilder::IsSourceDirtyFor(const TimeSpan& timeout)
{
ScopeLock scopeLock(_locker);
return _lastSourceCodeEdited > (_lastCompileAction + timeout);
@@ -626,7 +626,7 @@ void ScriptsBuilderService::Update()
// Check if compile code (if has been edited)
const TimeSpan timeToCallCompileIfDirty = TimeSpan::FromMilliseconds(50);
auto mainWindow = Engine::MainWindow;
if (ScriptsBuilder::IsSourceDirty(timeToCallCompileIfDirty) && mainWindow && mainWindow->IsFocused())
if (ScriptsBuilder::IsSourceDirtyFor(timeToCallCompileIfDirty) && mainWindow && mainWindow->IsFocused())
{
// Check if auto reload is enabled
if (Editor::Managed->CanAutoReloadScripts())

View File

@@ -63,7 +63,7 @@ public:
/// </summary>
/// <param name="timeout">Time to use for checking.</param>
/// <returns>True if source code is dirty, otherwise false.</returns>
static bool IsSourceDirty(const TimeSpan& timeout);
static bool IsSourceDirtyFor(const TimeSpan& timeout);
/// <summary>
/// Returns true if scripts are being now compiled/reloaded.

View File

@@ -829,7 +829,13 @@ namespace FlaxEditor.Utilities
var attr = field.GetAttribute<System.ComponentModel.DefaultValueAttribute>();
if (attr != null)
{
field.SetValue(obj, attr.Value);
// Prevent value type conflicts
var value = attr.Value;
var fieldType = field.ValueType.Type;
if (value != null && value.GetType() != fieldType)
value = Convert.ChangeType(value, fieldType);
field.SetValue(obj, value);
}
else if (isStructure)
{
@@ -845,7 +851,13 @@ namespace FlaxEditor.Utilities
var attr = property.GetAttribute<System.ComponentModel.DefaultValueAttribute>();
if (attr != null)
{
property.SetValue(obj, attr.Value);
// Prevent value type conflicts
var value = attr.Value;
var propertyType = property.ValueType.Type;
if (value != null && value.GetType() != propertyType)
value = Convert.ChangeType(value, propertyType);
property.SetValue(obj, value);
}
}
}

View File

@@ -2,6 +2,7 @@
#include "Asset.h"
#include "Content.h"
#include "SoftAssetReference.h"
#include "Cache/AssetsCache.h"
#include "Loading/ContentLoadingManager.h"
#include "Loading/Tasks/LoadAssetTask.h"
@@ -102,6 +103,65 @@ void WeakAssetReferenceBase::OnUnloaded(Asset* asset)
_asset = nullptr;
}
String SoftAssetReferenceBase::ToString() const
{
return _asset ? _asset->ToString() : (_id.IsValid() ? _id.ToString() : TEXT("<null>"));
}
void SoftAssetReferenceBase::OnSet(Asset* asset)
{
if (_asset == asset)
return;
if (_asset)
{
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->RemoveReference();
}
_asset = asset;
_id = asset ? asset->GetID() : Guid::Empty;
if (asset)
{
asset->AddReference();
asset->OnUnloaded.Bind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
}
Changed();
}
void SoftAssetReferenceBase::OnSet(const Guid& id)
{
if (_id == id)
return;
if (_asset)
{
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->RemoveReference();
}
_asset = nullptr;
_id = id;
Changed();
}
void SoftAssetReferenceBase::OnResolve(const ScriptingTypeHandle& type)
{
ASSERT(!_asset);
_asset = ::LoadAsset(_id, type);
if (_asset)
{
_asset->OnUnloaded.Bind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset->AddReference();
}
}
void SoftAssetReferenceBase::OnUnloaded(Asset* asset)
{
ASSERT(_asset == asset);
_asset->RemoveReference();
_asset->OnUnloaded.Unbind<SoftAssetReferenceBase, &SoftAssetReferenceBase::OnUnloaded>(this);
_asset = nullptr;
_id = Guid::Empty;
Changed();
}
Asset::Asset(const SpawnParams& params, const AssetInfo* info)
: ManagedScriptingObject(params)
, _refCount(0)

View File

@@ -0,0 +1,234 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Content/Asset.h"
/// <summary>
/// The asset soft reference. Asset gets referenced (loaded) on actual use (ID reference is resolving it).
/// </summary>
class FLAXENGINE_API SoftAssetReferenceBase
{
protected:
Asset* _asset = nullptr;
Guid _id = Guid::Empty;
public:
/// <summary>
/// Action fired when field gets changed (link a new asset or change to the another value).
/// </summary>
Delegate<> Changed;
public:
NON_COPYABLE(SoftAssetReferenceBase);
/// <summary>
/// Initializes a new instance of the <see cref="SoftAssetReferenceBase"/> class.
/// </summary>
SoftAssetReferenceBase() = default;
/// <summary>
/// Finalizes an instance of the <see cref="SoftAssetReferenceBase"/> class.
/// </summary>
~SoftAssetReferenceBase()
{
}
public:
/// <summary>
/// Gets the asset ID or Guid::Empty if not set.
/// </summary>
FORCE_INLINE Guid GetID() const
{
return _id;
}
/// <summary>
/// Gets the asset property value as string.
/// </summary>
String ToString() const;
protected:
void OnSet(Asset* asset);
void OnSet(const Guid& id);
void OnResolve(const ScriptingTypeHandle& type);
void OnUnloaded(Asset* asset);
};
/// <summary>
/// The asset soft reference. Asset gets referenced (loaded) on actual use (ID reference is resolving it).
/// </summary>
template<typename T>
API_CLASS(InBuild) class SoftAssetReference : public SoftAssetReferenceBase
{
public:
typedef T AssetType;
typedef SoftAssetReference<T> Type;
public:
/// <summary>
/// Initializes a new instance of the <see cref="SoftAssetReference"/> class.
/// </summary>
SoftAssetReference()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SoftAssetReference"/> class.
/// </summary>
/// <param name="asset">The asset to set.</param>
SoftAssetReference(T* asset)
{
OnSet(asset);
}
/// <summary>
/// Initializes a new instance of the <see cref="SoftAssetReference"/> class.
/// </summary>
/// <param name="other">The other.</param>
SoftAssetReference(const SoftAssetReference& other)
{
OnSet(other.Get());
}
SoftAssetReference(SoftAssetReference&& other)
{
OnSet(other.Get());
other.OnSet(nullptr);
}
/// <summary>
/// Finalizes an instance of the <see cref="SoftAssetReference"/> class.
/// </summary>
~SoftAssetReference()
{
}
public:
FORCE_INLINE bool operator==(T* other)
{
return Get() == other;
}
FORCE_INLINE bool operator==(const SoftAssetReference& other)
{
return GetID() == other.GetID();
}
FORCE_INLINE bool operator!=(T* other)
{
return Get() != other;
}
FORCE_INLINE bool operator!=(const SoftAssetReference& other)
{
return GetID() != other.GetID();
}
SoftAssetReference& operator=(const SoftAssetReference& other)
{
if (this != &other)
OnSet(other.GetID());
return *this;
}
SoftAssetReference& operator=(SoftAssetReference&& other)
{
if (this != &other)
{
OnSet(other.GetID());
other.OnSet(nullptr);
}
return *this;
}
FORCE_INLINE SoftAssetReference& operator=(const T& other)
{
OnSet(&other);
return *this;
}
FORCE_INLINE SoftAssetReference& operator=(T* other)
{
OnSet(other);
return *this;
}
FORCE_INLINE SoftAssetReference& operator=(const Guid& id)
{
OnSet(id);
return *this;
}
FORCE_INLINE operator T*() const
{
return (T*)Get();
}
FORCE_INLINE operator bool() const
{
return Get() != nullptr;
}
FORCE_INLINE T* operator->() const
{
return (T*)Get();
}
template<typename U>
FORCE_INLINE U* As() const
{
return static_cast<U*>(Get());
}
public:
/// <summary>
/// Gets the asset (or null if unassigned).
/// </summary>
T* Get() const
{
if (!_asset)
const_cast<SoftAssetReference*>(this)->OnResolve(T::TypeInitializer);
return (T*)_asset;
}
/// <summary>
/// Gets managed instance object (or null if no asset linked).
/// </summary>
MObject* GetManagedInstance() const
{
auto asset = Get();
return asset ? asset->GetOrCreateManagedInstance() : nullptr;
}
/// <summary>
/// Determines whether asset is assigned and managed instance of the asset is alive.
/// </summary>
bool HasManagedInstance() const
{
auto asset = Get();
return asset && asset->HasManagedInstance();
}
/// <summary>
/// Gets the managed instance object or creates it if missing or null if not assigned.
/// </summary>
MObject* GetOrCreateManagedInstance() const
{
auto asset = Get();
return asset ? asset->GetOrCreateManagedInstance() : nullptr;
}
/// <summary>
/// Sets the asset.
/// </summary>
/// <param name="id">The object ID. Uses Scripting to find the registered asset of the given ID.</param>
FORCE_INLINE void Set(const Guid& id)
{
OnSet(id);
}
/// <summary>
/// Sets the asset.
/// </summary>
/// <param name="asset">The asset.</param>
FORCE_INLINE void Set(T* asset)
{
OnSet(asset);
}
};
template<typename T>
uint32 GetHash(const SoftAssetReference<T>& key)
{
return GetHash(key.GetID());
}

View File

@@ -75,8 +75,8 @@ ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context)
for (uint32 i = 0; i < vertices; i++)
{
auto v = vb1[i].TexCoord;
output->WriteText(StringAnsi::Format("vt {0} {1}\n", Float16Compressor::Decompress(v.X), Float16Compressor::Decompress(v.Y)));
auto v = vb1[i].TexCoord.ToVector2();
output->WriteText(StringAnsi::Format("vt {0} {1}\n", v.X, v.Y));
}
output->WriteChar('\n');
@@ -180,8 +180,8 @@ ExportAssetResult AssetExporters::ExportSkinnedModel(ExportAssetContext& context
for (uint32 i = 0; i < vertices; i++)
{
auto v = vb0[i].TexCoord;
output->WriteText(StringAnsi::Format("vt {0} {1}\n", Float16Compressor::Decompress(v.X), Float16Compressor::Decompress(v.Y)));
auto v = vb0[i].TexCoord.ToVector2();
output->WriteText(StringAnsi::Format("vt {0} {1}\n", v.X, v.Y));
}
output->WriteChar('\n');

View File

@@ -6,7 +6,9 @@
#if COMPILE_WITH_ASSETS_IMPORTER
#if COMPILE_WITH_PHYSICS_COOKING
#include "Engine/Physics/CollisionCooking.h"
#endif
/// <summary>
/// Creating collision data asset utility
@@ -23,7 +25,6 @@ public:
static CreateAssetResult Create(CreateAssetContext& context);
#if COMPILE_WITH_PHYSICS_COOKING
/// <summary>
/// Cooks the mesh collision data and saves it to the asset using <see cref="CollisionData"/> format.
/// </summary>
@@ -31,7 +32,6 @@ public:
/// <param name="arg">The input argument data.</param>
/// <returns>True if failed, otherwise false. See log file to track errors better.</returns>
static bool CookMeshCollision(const String& outputPath, CollisionCooking::Argument& arg);
#endif
};

View File

@@ -639,7 +639,7 @@ public:
{
Vector3 output = input;
const float length = input.Length();
if (!Math::IsZero(length))
if (Math::Abs(length) >= ZeroTolerance)
{
const float inv = 1.0f / length;
output.X *= inv;

View File

@@ -11,6 +11,7 @@
#include "Engine/Content/Assets/Shader.h"
#include "Engine/Content/Assets/Material.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/SoftAssetReference.h"
#include "Engine/Platform/Windows/WindowsWindow.h"
#include "Engine/Render2D/Render2D.h"
#include "Engine/Engine/CommandLine.h"
@@ -19,7 +20,6 @@
#include "Engine/Profiler/Profiler.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Core/Utilities.h"
#include "Engine/Scripting/SoftObjectReference.h"
GPUPipelineState* GPUPipelineState::Spawn(const SpawnParams& params)
{
@@ -253,7 +253,7 @@ struct GPUDevice::PrivateData
GPUPipelineState* PS_Clear = nullptr;
GPUBuffer* FullscreenTriangleVB = nullptr;
AssetReference<Material> DefaultMaterial;
SoftObjectReference<Material> DefaultDeformableMaterial;
SoftAssetReference<Material> DefaultDeformableMaterial;
AssetReference<Texture> DefaultNormalMap;
AssetReference<Texture> DefaultWhiteTexture;
AssetReference<Texture> DefaultBlackTexture;

View File

@@ -9,7 +9,7 @@ BlendShapesInstance::MeshInstance::MeshInstance()
: IsUsed(false)
, IsDirty(false)
, DirtyMinVertexIndex(0)
, DirtyMaxVertexIndex(MAX_uint32)
, DirtyMaxVertexIndex(MAX_uint32 - 1)
, VertexBuffer(0, sizeof(VB0SkinnedElementType), TEXT("Skinned Mesh Blend Shape"))
{
}
@@ -112,7 +112,7 @@ void BlendShapesInstance::Update(SkinnedModel* skinnedModel)
// Initialize the dynamic vertex buffer data (use the dirty range from the previous update to be cleared with initial data)
instance.VertexBuffer.Data.Resize(vertexBuffer.Length());
const uint32 dirtyVertexDataStart = instance.DirtyMinVertexIndex * sizeof(VB0SkinnedElementType);
const uint32 dirtyVertexDataLength = Math::Min<uint32>(instance.DirtyMaxVertexIndex - instance.DirtyMinVertexIndex, vertexCount) * sizeof(VB0SkinnedElementType);
const uint32 dirtyVertexDataLength = Math::Min<uint32>(instance.DirtyMaxVertexIndex - instance.DirtyMinVertexIndex + 1, vertexCount) * sizeof(VB0SkinnedElementType);
Platform::MemoryCopy(instance.VertexBuffer.Data.Get() + dirtyVertexDataStart, vertexBuffer.Get() + dirtyVertexDataStart, dirtyVertexDataLength);
// Blend all blend shapes

View File

@@ -589,6 +589,17 @@ void MeshData::Merge(MeshData& other)
}
}
bool MaterialSlotEntry::UsesProperties() const
{
return Diffuse.Color != Color::White ||
Diffuse.TextureIndex != -1 ||
Emissive.Color != Color::Transparent ||
Emissive.TextureIndex != -1 ||
!Math::IsOne(Opacity.Value) ||
Opacity.TextureIndex != -1 ||
Normals.TextureIndex != -1;
}
void ModelData::CalculateLODsScreenSizes()
{
const float autoComputeLodPowerBase = 0.5f;

View File

@@ -373,16 +373,7 @@ struct FLAXENGINE_API MaterialSlotEntry
bool TwoSided = false;
bool UsesProperties() const
{
return Diffuse.Color != Color::White ||
Diffuse.TextureIndex != -1 ||
Emissive.Color != Color::Transparent ||
Emissive.TextureIndex != -1 ||
!Math::IsOne(Opacity.Value) ||
Opacity.TextureIndex != -1 ||
Normals.TextureIndex != -1;
}
bool UsesProperties() const;
};
/// <summary>

View File

@@ -1,7 +1,6 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "PixelFormatExtensions.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Core/Math/Math.h"
// ReSharper disable CppClangTidyClangDiagnosticSwitchEnum

View File

@@ -1047,7 +1047,7 @@ void GPUContextVulkan::BindIB(GPUBuffer* indexBuffer)
void GPUContextVulkan::BindSampler(int32 slot, GPUSampler* sampler)
{
ASSERT(slot >= GPU_STATIC_SAMPLERS_COUNT && slot < GPU_MAX_SAMPLER_BINDED);
const auto handle = sampler ? ((GPUSamplerVulkan*)sampler)->Sampler : nullptr;
const auto handle = sampler ? ((GPUSamplerVulkan*)sampler)->Sampler : VK_NULL_HANDLE;
_samplerHandles[slot] = handle;
}

View File

@@ -366,7 +366,7 @@ void GPUDeviceVulkan::GetInstanceLayersAndExtensions(Array<const char*>& outInst
}
#if VK_EXT_debug_utils
if (!vkTrace && outDebugUtils && FindLayerExtension(globalLayerExtensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
if (!vkTrace && FindLayerExtension(globalLayerExtensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
{
outInstanceExtensions.Add(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}

View File

@@ -185,6 +185,8 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height)
// Flush removed resources
_device->DeferredDeletionQueue.ReleaseResources(true);
}
ASSERT(_surface == VK_NULL_HANDLE);
ASSERT_LOW_LAYER(_backBuffers.Count() == 0);
// Create platform-dependent surface
VulkanPlatform::CreateSurface(windowHandle, GPUDeviceVulkan::Instance, &_surface);
@@ -205,7 +207,7 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height)
VALIDATE_VULKAN_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, _surface, &surfaceFormatsCount, nullptr));
ASSERT(surfaceFormatsCount > 0);
Array<VkSurfaceFormatKHR> surfaceFormats;
Array<VkSurfaceFormatKHR, InlinedAllocation<16>> surfaceFormats;
surfaceFormats.AddZeroed(surfaceFormatsCount);
VALIDATE_VULKAN_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, _surface, &surfaceFormatsCount, surfaceFormats.Get()));
@@ -414,8 +416,8 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height)
}
}
// Calculate memory usage
_memoryUsage = RenderTools::CalculateTextureMemoryUsage(_format, _width, _height, 1) * _backBuffers.Count();
// Estimate memory usage
_memoryUsage = 1024 + RenderTools::CalculateTextureMemoryUsage(_format, _width, _height, 1) * _backBuffers.Count();
return false;
}

View File

@@ -270,6 +270,11 @@ bool GPUTextureVulkan::OnInit()
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
if (useUAV)
imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
#if PLATFORM_MAC
// MoltenVK: VK_ERROR_FEATURE_NOT_PRESENT: vkCreateImageView(): 2D views on 3D images can only be used as color attachments.
if (IsVolume() && _desc.HasPerSliceViews())
imageInfo.usage &= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
#endif
imageInfo.tiling = optimalTiling ? VK_IMAGE_TILING_OPTIMAL : VK_IMAGE_TILING_LINEAR;
imageInfo.samples = (VkSampleCountFlagBits)MultiSampleLevel();
// TODO: set initialLayout to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL for IsRegularTexture() ???

View File

@@ -8,6 +8,7 @@
void MacVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
{
extensions.Add(VK_KHR_SURFACE_EXTENSION_NAME);
extensions.Add(VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
}

View File

@@ -1024,10 +1024,12 @@ void UpdateGPU(RenderTask* task, GPUContext* context)
const auto& track = particleSystem->Tracks[j];
if (track.Type != ParticleSystem::Track::Types::Emitter || track.Disabled)
continue;
const uint32 emitterIndex = track.AsEmitter.Index;
auto emitter = particleSystem->Emitters[emitterIndex].Get();
auto& data = instance.Emitters[emitterIndex];
if (!emitter || !emitter->IsLoaded() || !data.Buffer || emitter->SimulationMode != ParticlesSimulationMode::GPU)
const int32 emitterIndex = track.AsEmitter.Index;
ParticleEmitter* emitter = particleSystem->Emitters[emitterIndex].Get();
if (!emitter || !emitter->IsLoaded() || emitter->SimulationMode != ParticlesSimulationMode::GPU || instance.Emitters.Count() <= emitterIndex)
continue;
ParticleEmitterInstance& data = instance.Emitters[emitterIndex];
if (!data.Buffer)
continue;
ASSERT(emitter->Capacity != 0 && emitter->Graph.Layout.Size != 0);

View File

@@ -242,7 +242,7 @@ void CharacterController::CreateShape()
void CharacterController::UpdateBounds()
{
void* actor = PhysicsBackend::GetShapeActor(_shape);
void* actor = _shape ? PhysicsBackend::GetShapeActor(_shape) : nullptr;
if (actor)
PhysicsBackend::GetActorBounds(actor, _box);
else

View File

@@ -837,8 +837,8 @@ DragDropEffect MacWindow::DoDragDrop(const StringView& data)
void MacWindow::SetCursor(CursorType type)
{
WindowBase::SetCursor(type);
if (!_isMouseOver)
return;
//if (!_isMouseOver)
// return;
NSCursor* cursor = nullptr;
switch (type)
{
@@ -875,6 +875,7 @@ void MacWindow::SetCursor(CursorType type)
if (cursor)
{
[cursor set];
[NSCursor unhide];
}
}

View File

@@ -25,7 +25,11 @@ public:
static void MemoryBarrier();
static int64 InterlockedExchange(int64 volatile* dst, int64 exchange)
{
#if WIN64
return _InterlockedExchange64(dst, exchange);
#else
return _interlockedexchange64(dst, exchange);
#endif
}
static int32 InterlockedCompareExchange(int32 volatile* dst, int32 exchange, int32 comperand)
{
@@ -37,15 +41,27 @@ public:
}
static int64 InterlockedIncrement(int64 volatile* dst)
{
#if WIN64
return _InterlockedExchangeAdd64(dst, 1) + 1;
#else
return _interlockedexchange64(dst, 1) + 1;
#endif
}
static int64 InterlockedDecrement(int64 volatile* dst)
{
#if WIN64
return _InterlockedExchangeAdd64(dst, -1) - 1;
#else
return _interlockedexchangeadd64(dst, -1) - 1;
#endif
}
static int64 InterlockedAdd(int64 volatile* dst, int64 value)
{
#if WIN64
return _InterlockedExchangeAdd64(dst, value);
#else
return _interlockedexchangeadd64(dst, value);
#endif
}
static int32 AtomicRead(int32 volatile* dst)
{
@@ -61,7 +77,11 @@ public:
}
static void AtomicStore(int64 volatile* dst, int64 value)
{
#if WIN64
_InterlockedExchange64(dst, value);
#else
_interlockedexchange64(dst, value);
#endif
}
static void Prefetch(void const* ptr);
static void* Allocate(uint64 size, uint64 alignment);

View File

@@ -18,7 +18,7 @@ class FLAXENGINE_API ScriptingEvents
public:
/// <summary>
/// Global table for registered even binder methods (key is pair of type and event name, value is method that takes instance with event, object to bind and flag to bind or unbind).
/// Global table for registered event binder methods (key is pair of type and event name, value is method that takes instance with event, object to bind and flag to bind or unbind).
/// </summary>
/// <remarks>
/// Key: pair of event type, event name.

View File

@@ -24,6 +24,7 @@ public:
Delegate<> Changed;
public:
NON_COPYABLE(SoftObjectReferenceBase);
/// <summary>
/// Initializes a new instance of the <see cref="SoftObjectReferenceBase"/> class.

View File

@@ -23,6 +23,8 @@
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Scripting/ManagedSerialization.h"
#include "Engine/Scripting/ManagedCLR/MUtils.h"
#include "Engine/Scripting/ScriptingObjectReference.h"
#include "Engine/Content/Asset.h"
#include "Engine/Utilities/Encryption.h"
#if USE_MONO
#include <ThirdParty/mono-2.0/mono/metadata/object.h>

View File

@@ -5,14 +5,21 @@
#include "SerializationFwd.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Scripting/ScriptingObjectReference.h"
#include "Engine/Scripting/SoftObjectReference.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Content/WeakAssetReference.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "Engine/Utilities/Encryption.h"
struct Version;
struct VariantType;
template<typename T>
class ScriptingObjectReference;
template<typename T>
class SoftObjectReference;
template<typename T>
class AssetReference;
template<typename T>
class WeakAssetReference;
template<typename T>
class SoftAssetReference;
// @formatter:off
@@ -513,6 +520,26 @@ namespace Serialization
v = id;
}
// Soft Asset Reference
template<typename T>
inline bool ShouldSerialize(const SoftAssetReference<T>& v, const void* otherObj)
{
return !otherObj || v.Get() != ((SoftAssetReference<T>*)otherObj)->Get();
}
template<typename T>
inline void Serialize(ISerializable::SerializeStream& stream, const SoftAssetReference<T>& v, const void* otherObj)
{
stream.Guid(v.GetID());
}
template<typename T>
inline void Deserialize(ISerializable::DeserializeStream& stream, SoftAssetReference<T>& v, ISerializeModifier* modifier)
{
Guid id;
Deserialize(stream, id, modifier);
v = id;
}
// Array
template<typename T, typename AllocationType = HeapAllocation>

View File

@@ -519,14 +519,12 @@ void ReadStream::ReadJson(ISerializable* obj)
void WriteStream::WriteText(const StringView& text)
{
for (int32 i = 0; i < text.Length(); i++)
WriteChar(text[i]);
WriteBytes(text.Get(), sizeof(Char) * text.Length());
}
void WriteStream::WriteText(const StringAnsiView& text)
{
for (int32 i = 0; i < text.Length(); i++)
WriteChar(text[i]);
WriteBytes(text.Get(), sizeof(char) * text.Length());
}
void WriteStream::WriteString(const StringView& data)

View File

@@ -423,7 +423,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context)
#endif
// Report progress
float hemispheresProgress = static_cast<float>(_workerStagePosition1) / lightmapEntry.Hemispheres.Count();
float hemispheresProgress = static_cast<float>(_workerStagePosition1) / Math::Max(lightmapEntry.Hemispheres.Count(), 1);
float lightmapsProgress = static_cast<float>(_workerStagePosition0 + hemispheresProgress) / scene->Lightmaps.Count();
float bouncesProgress = static_cast<float>(_giBounceRunningIndex) / _bounceCount;
reportProgress(BuildProgressStep::RenderHemispheres, lightmapsProgress / _bounceCount + bouncesProgress);

View File

@@ -42,6 +42,7 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj
SERIALIZE(ImportVertexColors);
SERIALIZE(ImportBlendShapes);
SERIALIZE(LightmapUVsSource);
SERIALIZE(CollisionMeshesPrefix);
SERIALIZE(Scale);
SERIALIZE(Rotation);
SERIALIZE(Translation);
@@ -79,6 +80,7 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi
DESERIALIZE(ImportVertexColors);
DESERIALIZE(ImportBlendShapes);
DESERIALIZE(LightmapUVsSource);
DESERIALIZE(CollisionMeshesPrefix);
DESERIALIZE(Scale);
DESERIALIZE(Rotation);
DESERIALIZE(Translation);

View File

@@ -16,6 +16,7 @@
#include "Engine/Tools/TextureTool/TextureTool.h"
#include "Engine/ContentImporters/AssetsImportingManager.h"
#include "Engine/ContentImporters/CreateMaterial.h"
#include "Engine/ContentImporters/CreateCollisionData.h"
#include "Editor/Utilities/EditorUtilities.h"
#include <ThirdParty/meshoptimizer/meshoptimizer.h>
@@ -562,7 +563,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
materialOptions.Opacity.Texture = data.Textures[material.Opacity.TextureIndex].AssetID;
if (material.Normals.TextureIndex != -1)
materialOptions.Normals.Texture = data.Textures[material.Normals.TextureIndex].AssetID;
if (material.TwoSided | material.Diffuse.HasAlphaMask)
if (material.TwoSided || material.Diffuse.HasAlphaMask)
materialOptions.Info.CullMode = CullMode::TwoSided;
if (!Math::IsOne(material.Opacity.Value) || material.Opacity.TextureIndex != -1)
materialOptions.Info.BlendMode = MaterialBlendMode::Transparent;
@@ -624,6 +625,41 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
}
}
// Collision mesh output
if (options.CollisionMeshesPrefix.HasChars())
{
// Extract collision meshes
ModelData collisionModel;
for (auto& lod : data.LODs)
{
for (int32 i = lod.Meshes.Count() - 1; i >= 0; i--)
{
auto mesh = lod.Meshes[i];
if (mesh->Name.StartsWith(options.CollisionMeshesPrefix, StringSearchCase::IgnoreCase))
{
if (collisionModel.LODs.Count() == 0)
collisionModel.LODs.AddOne();
collisionModel.LODs[0].Meshes.Add(mesh);
lod.Meshes.RemoveAtKeepOrder(i);
if (lod.Meshes.IsEmpty())
break;
}
}
}
if (collisionModel.LODs.HasItems())
{
// Create collision
CollisionCooking::Argument arg;
arg.Type = CollisionDataType::TriangleMesh;
arg.OverrideModelData = &collisionModel;
auto assetPath = autoImportOutput / StringUtils::GetFileNameWithoutExtension(path) + TEXT("Collision") ASSET_FILES_EXTENSION_WITH_DOT;
if (CreateCollisionData::CookMeshCollision(assetPath, arg))
{
LOG(Error, "Failed to create collision mesh.");
}
}
}
// For generated lightmap UVs coordinates needs to be moved so all meshes are in unique locations in [0-1]x[0-1] coordinates space
if (options.LightmapUVsSource == ModelLightmapUVsSource::Generate && data.LODs.HasItems() && data.LODs[0].Meshes.Count() > 1)
{

View File

@@ -177,6 +177,7 @@ public:
bool ImportVertexColors = true;
bool ImportBlendShapes = false;
ModelLightmapUVsSource LightmapUVsSource = ModelLightmapUVsSource::Disable;
String CollisionMeshesPrefix;
// Transform
float Scale = 1.0f;

View File

@@ -18,6 +18,7 @@
#define FMT_USE_WINDOWS_H 0
//#define FMT_USE_STRING_VIEW 1
#define FMT_USE_STRING 0
#define FMT_USE_LONG_DOUBLE 0
#define FMT_USE_ITERATOR 0
#define FMT_USE_LOCALE_GROUPING 0
#define FMT_EXCEPTIONS 0

View File

@@ -19,6 +19,7 @@ namespace Flax.Build.Bindings
internal static readonly Dictionary<string, string> CSharpNativeToManagedBasicTypes = new Dictionary<string, string>()
{
// Language types
{ "bool", "bool" },
{ "int8", "sbyte" },
{ "int16", "short" },
{ "int32", "int" },
@@ -233,8 +234,12 @@ namespace Flax.Build.Bindings
return result;
}
// ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
// Object reference property
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
return GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller);
// Array or Span
@@ -310,8 +315,12 @@ namespace Flax.Build.Bindings
return "IntPtr";
}
// ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
// Object reference property
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
return "IntPtr";
// Function
@@ -364,8 +373,12 @@ namespace Flax.Build.Bindings
return string.Format("FlaxEngine.Object.GetUnmanagedInterface({{0}}, typeof({0}))", apiType.FullNameManaged);
}
// ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
// Object reference property
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
return "FlaxEngine.Object.GetUnmanagedPtr({0})";
// Default

View File

@@ -19,7 +19,7 @@ namespace Flax.Build.Bindings
partial class BindingsGenerator
{
private static readonly Dictionary<string, Type> TypeCache = new Dictionary<string, Type>();
private const int CacheVersion = 11;
private const int CacheVersion = 12;
internal static void Write(BinaryWriter writer, string e)
{

View File

@@ -16,6 +16,7 @@ namespace Flax.Build.Bindings
private static readonly string[] CppParamsThatNeedConversionWrappers = new string[64];
private static readonly string[] CppParamsThatNeedConversionTypes = new string[64];
private static readonly string[] CppParamsWrappersCache = new string[64];
public static readonly List<KeyValuePair<string, string>> CppInternalCalls = new List<KeyValuePair<string, string>>();
public static readonly List<ApiTypeInfo> CppUsedNonPodTypes = new List<ApiTypeInfo>();
private static readonly List<ApiTypeInfo> CppUsedNonPodTypesList = new List<ApiTypeInfo>();
public static readonly HashSet<FileInfo> CppReferencesFiles = new HashSet<FileInfo>();
@@ -130,7 +131,11 @@ namespace Flax.Build.Bindings
return value;
if (typeInfo.Type == "String")
return $"Variant(StringView({value}))";
if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "SoftObjectReference")
if (typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "SoftObjectReference")
return $"Variant({value}.Get())";
if (typeInfo.IsArray)
{
@@ -177,7 +182,7 @@ namespace Flax.Build.Bindings
return $"(StringView){value}";
if (typeInfo.IsPtr && typeInfo.IsConst && typeInfo.Type == "Char")
return $"((StringView){value}).GetNonTerminatedText()"; // (StringView)Variant, if not empty, is guaranteed to point to a null-terminated buffer.
if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference")
if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftAssetReference")
return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((Asset*){value})";
if (typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "SoftObjectReference")
return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((ScriptingObject*){value})";
@@ -400,8 +405,12 @@ namespace Flax.Build.Bindings
type = "void*";
return "MUtils::ToManaged({0})";
default:
// ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
// Object reference property
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
{
type = "MonoObject*";
return "{0}.GetManagedInstance()";
@@ -568,8 +577,12 @@ namespace Flax.Build.Bindings
type = "void*";
return "MUtils::ToNative({0})";
default:
// ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
// Object reference property
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
{
// For non-pod types converting only, other API converts managed to unmanaged object in C# wrapper code)
if (CppNonPodTypesConvertingGeneration)
@@ -771,8 +784,53 @@ namespace Flax.Build.Bindings
return $"MUtils::Box<{nativeType}>({value}, {GenerateCppGetNativeClass(buildData, typeInfo, caller, null)})";
}
private static bool GenerateCppWrapperFunctionImplicitBinding(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller)
{
if (typeInfo.IsVoid)
return true;
if (typeInfo.IsPtr || typeInfo.IsRef || typeInfo.IsArray || typeInfo.IsBitField || (typeInfo.GenericArgs != null && typeInfo.GenericArgs.Count != 0))
return false;
if (CSharpNativeToManagedBasicTypes.ContainsKey(typeInfo.Type) || CSharpNativeToManagedBasicTypes.ContainsValue(typeInfo.Type))
return true;
var apiType = FindApiTypeInfo(buildData, typeInfo, caller);
if (apiType != null)
{
if (apiType.IsEnum)
return true;
}
return false;
}
private static bool GenerateCppWrapperFunctionImplicitBinding(BuildData buildData, FunctionInfo functionInfo, ApiTypeInfo caller)
{
if (!functionInfo.IsStatic || functionInfo.Access != AccessLevel.Public || (functionInfo.Glue.CustomParameters != null && functionInfo.Glue.CustomParameters.Count != 0))
return false;
if (!GenerateCppWrapperFunctionImplicitBinding(buildData, functionInfo.ReturnType, caller))
return false;
for (int i = 0; i < functionInfo.Parameters.Count; i++)
{
var parameterInfo = functionInfo.Parameters[i];
if (parameterInfo.IsOut || parameterInfo.IsRef || !GenerateCppWrapperFunctionImplicitBinding(buildData, parameterInfo.Type, caller))
return false;
}
return true;
}
private static void GenerateCppWrapperFunction(BuildData buildData, StringBuilder contents, ApiTypeInfo caller, FunctionInfo functionInfo, string callFormat = "{0}({1})")
{
// Optimize static function wrappers that match C# internal call ABI exactly
// Use it for Engine-internally only because in games this makes it problematic to use the same function name but with different signature that is not visible to scripting
if (CurrentModule.Module is EngineModule && callFormat == "{0}({1})" && GenerateCppWrapperFunctionImplicitBinding(buildData, functionInfo, caller))
{
// Ensure the function name is unique within a class/structure
if (caller is ClassStructInfo classStructInfo && classStructInfo.Functions.All(f => f.Name != functionInfo.Name || f == functionInfo))
{
// Use native method binding directly (no generated wrapper)
CppInternalCalls.Add(new KeyValuePair<string, string>(functionInfo.UniqueName, classStructInfo.Name + "::" + functionInfo.Name));
return;
}
}
// Setup function binding glue to ensure that wrapper method signature matches for C++ and C#
functionInfo.Glue = new FunctionInfo.GlueInfo
{
@@ -797,6 +855,7 @@ namespace Flax.Build.Bindings
});
}
CppInternalCalls.Add(new KeyValuePair<string, string>(functionInfo.UniqueName, functionInfo.UniqueName));
contents.AppendFormat(" static {0} {1}(", returnValueType, functionInfo.UniqueName);
var separator = false;
@@ -1453,6 +1512,7 @@ namespace Flax.Build.Bindings
var useScripting = classInfo.IsStatic || classInfo.IsScriptingObject;
var useCSharp = EngineConfiguration.WithCSharp(buildData.TargetOptions);
var hasInterface = classInfo.Interfaces != null && classInfo.Interfaces.Any(x => x.Access == AccessLevel.Public);
CppInternalCalls.Clear();
if (classInfo.IsAutoSerialization)
GenerateCppAutoSerialization(buildData, contents, moduleInfo, classInfo, classTypeNameNative);
@@ -1520,7 +1580,7 @@ namespace Flax.Build.Bindings
{
// Convert value back from managed to native (could be modified there)
paramType.IsRef = false;
var managedToNative = GenerateCppWrapperManagedToNative(buildData, paramType, classInfo, out var managedType, null, out var _);
var managedToNative = GenerateCppWrapperManagedToNative(buildData, paramType, classInfo, out var managedType, null, out _);
var passAsParamPtr = managedType.EndsWith("*");
var paramValue = $"({managedType}{(passAsParamPtr ? "" : "*")})params[{i}]";
if (!string.IsNullOrEmpty(managedToNative))
@@ -1538,6 +1598,7 @@ namespace Flax.Build.Bindings
contents.Append(" }").AppendLine().AppendLine();
// C# event wrapper binding method (binds/unbinds C# wrapper to C++ delegate)
CppInternalCalls.Add(new KeyValuePair<string, string>(eventInfo.Name + "_Bind", eventInfo.Name + "_ManagedBind"));
contents.AppendFormat(" static void {0}_ManagedBind(", eventInfo.Name);
if (!eventInfo.IsStatic)
contents.AppendFormat("{0}* obj, ", classTypeNameNative);
@@ -1685,47 +1746,9 @@ namespace Flax.Build.Bindings
}
if (useScripting && useCSharp)
{
foreach (var eventInfo in classInfo.Events)
foreach (var e in CppInternalCalls)
{
if (eventInfo.IsHidden)
continue;
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{eventInfo.Name}_Bind\", &{eventInfo.Name}_ManagedBind);");
}
foreach (var fieldInfo in classInfo.Fields)
{
if (fieldInfo.IsHidden)
continue;
if (fieldInfo.Getter != null)
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{fieldInfo.Getter.UniqueName}\", &{fieldInfo.Getter.UniqueName});");
if (fieldInfo.Setter != null)
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{fieldInfo.Setter.UniqueName}\", &{fieldInfo.Setter.UniqueName});");
}
foreach (var propertyInfo in classInfo.Properties)
{
if (propertyInfo.IsHidden)
continue;
if (propertyInfo.Getter != null)
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{propertyInfo.Getter.UniqueName}\", &{propertyInfo.Getter.UniqueName});");
if (propertyInfo.Setter != null)
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{propertyInfo.Setter.UniqueName}\", &{propertyInfo.Setter.UniqueName});");
}
foreach (var functionInfo in classInfo.Functions)
{
if (functionInfo.IsHidden)
continue;
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{functionInfo.UniqueName}\", &{functionInfo.UniqueName});");
}
if (hasInterface)
{
foreach (var interfaceInfo in classInfo.Interfaces)
{
if (interfaceInfo.Access != AccessLevel.Public)
continue;
foreach (var functionInfo in interfaceInfo.Functions)
{
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{functionInfo.UniqueName}\", &{functionInfo.UniqueName});");
}
}
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{e.Key}\", &{e.Value});");
}
}
GenerateCppClassInitRuntime?.Invoke(buildData, classInfo, contents);
@@ -1801,6 +1824,7 @@ namespace Flax.Build.Bindings
if (structureInfo.Parent != null && !(structureInfo.Parent is FileInfo))
structureTypeNameInternal = structureInfo.Parent.FullNameNative + '_' + structureTypeNameInternal;
var useCSharp = EngineConfiguration.WithCSharp(buildData.TargetOptions);
CppInternalCalls.Clear();
if (structureInfo.IsAutoSerialization)
GenerateCppAutoSerialization(buildData, contents, moduleInfo, structureInfo, structureTypeNameNative);
@@ -1869,12 +1893,9 @@ namespace Flax.Build.Bindings
if (useCSharp)
{
foreach (var fieldInfo in structureInfo.Fields)
foreach (var e in CppInternalCalls)
{
if (fieldInfo.Getter != null)
contents.AppendLine($" ADD_INTERNAL_CALL(\"{structureTypeNameManagedInternalCall}::Internal_{fieldInfo.Getter.UniqueName}\", &{fieldInfo.Getter.UniqueName});");
if (fieldInfo.Setter != null)
contents.AppendLine($" ADD_INTERNAL_CALL(\"{structureTypeNameManagedInternalCall}::Internal_{fieldInfo.Setter.UniqueName}\", &{fieldInfo.Setter.UniqueName});");
contents.AppendLine($" ADD_INTERNAL_CALL(\"{structureTypeNameManagedInternalCall}::Internal_{e.Key}\", &{e.Value});");
}
}
@@ -2189,6 +2210,7 @@ namespace Flax.Build.Bindings
CppIncludeFilesList.Clear();
CppVariantToTypes.Clear();
CppVariantFromTypes.Clear();
CurrentModule = moduleInfo;
// Disable C# scripting based on configuration
ScriptingLangInfos[0].Enabled = EngineConfiguration.WithCSharp(buildData.TargetOptions);

View File

@@ -48,6 +48,7 @@ namespace Flax.Build.Bindings
public static event GenerateModuleBindingsDelegate GenerateModuleBindings;
public static event GenerateBinaryModuleBindingsDelegate GenerateBinaryModuleBindings;
public static ModuleInfo CurrentModule;
public static ModuleInfo ParseModule(BuildData buildData, Module module, BuildOptions moduleOptions = null)
{

View File

@@ -16,6 +16,7 @@ namespace Flax.Build.Bindings
public ClassStructInfo BaseType;
public List<InterfaceInfo> Interfaces;
public List<TypeInfo> Inheritance; // Data from parsing, used to interfaces and base type construct in Init
public List<FunctionInfo> Functions = new List<FunctionInfo>();
public override void Init(Builder.BuildData buildData)
{
@@ -52,6 +53,7 @@ namespace Flax.Build.Bindings
writer.Write((byte)BaseTypeInheritance);
BindingsGenerator.Write(writer, BaseType);
BindingsGenerator.Write(writer, Inheritance);
BindingsGenerator.Write(writer, Functions);
base.Write(writer);
}
@@ -62,6 +64,7 @@ namespace Flax.Build.Bindings
BaseTypeInheritance = (AccessLevel)reader.ReadByte();
BaseType = BindingsGenerator.Read(reader, BaseType);
Inheritance = BindingsGenerator.Read(reader, Inheritance);
Functions = BindingsGenerator.Read(reader, Functions);
base.Read(reader);
}
@@ -72,8 +75,6 @@ namespace Flax.Build.Bindings
/// </summary>
public abstract class VirtualClassInfo : ClassStructInfo
{
public List<FunctionInfo> Functions = new List<FunctionInfo>();
internal HashSet<string> UniqueFunctionNames;
public override void Init(Builder.BuildData buildData)
@@ -100,20 +101,6 @@ namespace Flax.Build.Bindings
public abstract int GetScriptVTableOffset(VirtualClassInfo classInfo);
public override void Write(BinaryWriter writer)
{
BindingsGenerator.Write(writer, Functions);
base.Write(writer);
}
public override void Read(BinaryReader reader)
{
Functions = BindingsGenerator.Read(reader, Functions);
base.Read(reader);
}
public override void AddChild(ApiTypeInfo apiTypeInfo)
{
apiTypeInfo.Namespace = null;

View File

@@ -12,7 +12,6 @@ namespace Flax.Build.Bindings
public class StructureInfo : ClassStructInfo
{
public List<FieldInfo> Fields = new List<FieldInfo>();
public List<FunctionInfo> Functions = new List<FunctionInfo>();
public bool IsAutoSerialization;
public bool ForceNoPod;

View File

@@ -271,6 +271,7 @@ namespace Flax.Build.Platforms
// Input libraries
var libraryPaths = new HashSet<string>();
var dylibs = new HashSet<string>();
foreach (var library in linkEnvironment.InputLibraries)
{
var dir = Path.GetDirectoryName(library);
@@ -290,6 +291,7 @@ namespace Flax.Build.Platforms
else if (ext == ".dylib")
{
// Link against dynamic library
dylibs.Add(library);
task.PrerequisiteFiles.Add(library);
libraryPaths.Add(dir);
args.Add(string.Format("\"{0}\"", library));
@@ -319,6 +321,7 @@ namespace Flax.Build.Platforms
else if (ext == ".dylib")
{
// Link against dynamic library
dylibs.Add(library);
task.PrerequisiteFiles.Add(library);
libraryPaths.Add(dir);
args.Add(string.Format("\"{0}\"", library));
@@ -370,6 +373,27 @@ namespace Flax.Build.Platforms
task.Cost = task.PrerequisiteFiles.Count;
task.ProducedFiles.Add(outputFilePath);
Task lastTask = task;
if (options.LinkEnv.Output == LinkerOutput.Executable)
{
// Fix rpath for dynamic libraries
foreach (var library in dylibs)
{
var rpathTask = graph.Add<Task>();
rpathTask.ProducedFiles.Add(outputFilePath);
rpathTask.WorkingDirectory = options.WorkingDirectory;
rpathTask.CommandPath = "install_name_tool";
var filename = Path.GetFileName(library);
var outputFolder = Path.GetDirectoryName(outputFilePath);
rpathTask.CommandArguments = string.Format("-change \"{0}/{1}\" \"@loader_path/{1}\" {2}", outputFolder, filename, outputFilePath);
rpathTask.InfoMessage = "Fixing rpath to " + filename;
rpathTask.Cost = 1;
rpathTask.DisableCache = true;
rpathTask.DependentTasks = new HashSet<Task>();
rpathTask.DependentTasks.Add(lastTask);
lastTask = rpathTask;
}
}
if (!options.LinkEnv.DebugInformation)
{
// Strip debug symbols
@@ -382,7 +406,7 @@ namespace Flax.Build.Platforms
stripTask.Cost = 1;
stripTask.DisableCache = true;
stripTask.DependentTasks = new HashSet<Task>();
stripTask.DependentTasks.Add(task);
stripTask.DependentTasks.Add(lastTask);
}
}