Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -905,7 +905,7 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
|
||||
MException ex(exception);
|
||||
ex.Log(LogType::Error, TEXT("Property"));
|
||||
}
|
||||
else if (!MCore::Type::IsPointer(valueType))
|
||||
else if (!MCore::Type::IsPointer(valueType) && !MCore::Type::IsReference(valueType))
|
||||
{
|
||||
if (boxed)
|
||||
Platform::MemoryCopy(value, MCore::Object::Unbox(boxed), valueSize);
|
||||
|
||||
@@ -32,14 +32,21 @@ CreateAssetResult ImportShader::Import(CreateAssetContext& context)
|
||||
LOG(Warning, "Empty shader source file.");
|
||||
return CreateAssetResult::Error;
|
||||
}
|
||||
|
||||
// Ensure the source code has an empty line at the end (expected by glslang)
|
||||
auto sourceCodeChunkSize = sourceCodeSize + 1;
|
||||
if (sourceCodeText[sourceCodeSize - 1] != '\n')
|
||||
sourceCodeChunkSize++;
|
||||
|
||||
const auto& sourceCodeChunk = context.Data.Header.Chunks[SourceCodeChunk];
|
||||
sourceCodeChunk->Data.Allocate(sourceCodeSize + 1);
|
||||
sourceCodeChunk->Data.Allocate(sourceCodeChunkSize);
|
||||
const auto sourceCode = sourceCodeChunk->Get();
|
||||
Platform::MemoryCopy(sourceCode, sourceCodeText.Get(), sourceCodeSize);
|
||||
sourceCode[sourceCodeChunkSize - 2] = '\n';
|
||||
|
||||
// Encrypt source code
|
||||
Encryption::EncryptBytes(sourceCode, sourceCodeSize);
|
||||
sourceCode[sourceCodeSize] = 0;
|
||||
Encryption::EncryptBytes(sourceCode, sourceCodeChunkSize - 1);
|
||||
sourceCode[sourceCodeChunkSize - 1] = 0;
|
||||
|
||||
// Set Custom Data with Header
|
||||
ShaderStorage::Header20 shaderHeader;
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -251,19 +251,13 @@ void Log::Logger::Write(LogType type, const StringView& msg)
|
||||
OnError(type, msg);
|
||||
}
|
||||
|
||||
// Check if need to show message box with that log message
|
||||
if (type == LogType::Fatal)
|
||||
{
|
||||
// Ensure the error gets written to the disk
|
||||
if (type == LogType::Fatal || type == LogType::Error)
|
||||
Flush();
|
||||
|
||||
// Process message further
|
||||
if (type == LogType::Fatal)
|
||||
Platform::Fatal(msg);
|
||||
else if (type == LogType::Error)
|
||||
Platform::Error(msg);
|
||||
else
|
||||
Platform::Info(msg);
|
||||
}
|
||||
// Check if need to show message box with that log message
|
||||
if (type == LogType::Fatal)
|
||||
Platform::Fatal(msg);
|
||||
}
|
||||
|
||||
const Char* ToString(LogType e)
|
||||
|
||||
@@ -58,6 +58,7 @@ using Mathr = FlaxEngine.Mathf;
|
||||
*/
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@@ -953,6 +954,91 @@ namespace FlaxEngine
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a gradual change of a vector towards a specified target over time
|
||||
/// </summary>
|
||||
/// <param name="current">Current vector.</param>
|
||||
/// <param name="target">Target vector.</param>
|
||||
/// <param name="currentVelocity">Used to store the current velocity.</param>
|
||||
/// <param name="smoothTime">Determines the approximate time it should take to reach the target vector.</param>
|
||||
/// <param name="maxSpeed">Defines the upper limit on the speed of the Smooth Damp.</param>
|
||||
public static Vector2 SmoothDamp(Vector2 current, Vector2 target, ref Vector2 currentVelocity, float smoothTime, float maxSpeed)
|
||||
{
|
||||
return SmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed, Time.DeltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a gradual change of a vector towards a specified target over time
|
||||
/// </summary>
|
||||
/// <param name="current">Current vector.</param>
|
||||
/// <param name="target">Target vector.</param>
|
||||
/// <param name="currentVelocity">Used to store the current velocity.</param>
|
||||
/// <param name="smoothTime">Determines the approximate time it should take to reach the target vector.</param>
|
||||
public static Vector2 SmoothDamp(Vector2 current, Vector2 target, ref Vector2 currentVelocity, float smoothTime)
|
||||
{
|
||||
return SmoothDamp(current, target, ref currentVelocity, smoothTime, float.PositiveInfinity, Time.DeltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a gradual change of a vector towards a specified target over time
|
||||
/// </summary>
|
||||
/// <param name="current">Current vector.</param>
|
||||
/// <param name="target">Target vector.</param>
|
||||
/// <param name="currentVelocity">Used to store the current velocity.</param>
|
||||
/// <param name="smoothTime">Determines the approximate time it should take to reach the target vector.</param>
|
||||
/// <param name="maxSpeed">Defines the upper limit on the speed of the Smooth Damp.</param>
|
||||
/// <param name="deltaTime">Delta Time, represents the time elapsed since last frame.</param>
|
||||
public static Vector2 SmoothDamp(Vector2 current, Vector2 target, ref Vector2 currentVelocity, float smoothTime, [DefaultValue("float.PositiveInfinity")] float maxSpeed, [DefaultValue("Time.DeltaTime")] float deltaTime)
|
||||
{
|
||||
smoothTime = Mathf.Max(0.0001f, smoothTime);
|
||||
Real a = 2f / smoothTime;
|
||||
Real b = a * deltaTime;
|
||||
Real e = 1f / (1f + b + 0.48f * b * b + 0.235f * b * b * b);
|
||||
|
||||
Real change_x = current.X - target.X;
|
||||
Real change_y = current.Y - target.Y;
|
||||
Vector2 originalTo = target;
|
||||
|
||||
Real maxChangeSpeed = maxSpeed * smoothTime;
|
||||
Real changeSq = maxChangeSpeed * maxChangeSpeed;
|
||||
Real sqrDist = change_x * change_x + change_y * change_y;
|
||||
if (sqrDist > changeSq)
|
||||
{
|
||||
var dist = (Real)Math.Sqrt(sqrDist);
|
||||
change_x = change_x / dist * maxChangeSpeed;
|
||||
change_y = change_y / dist * maxChangeSpeed;
|
||||
}
|
||||
|
||||
target.X = current.X - change_x;
|
||||
target.Y = current.Y - change_y;
|
||||
|
||||
Real temp_x = (currentVelocity.X + a * change_x) * deltaTime;
|
||||
Real temp_y = (currentVelocity.Y + a * change_y) * deltaTime;
|
||||
|
||||
currentVelocity.X = (currentVelocity.X - a * temp_x) * e;
|
||||
currentVelocity.Y = (currentVelocity.Y - a * temp_y) * e;
|
||||
|
||||
Real output_x = target.X + (change_x + temp_x) * e;
|
||||
Real output_y = target.Y + (change_y + temp_y) * e;
|
||||
|
||||
Real x1 = originalTo.X - current.X;
|
||||
Real y1 = originalTo.Y - current.Y;
|
||||
|
||||
Real x2 = output_x - originalTo.X;
|
||||
Real y2 = output_y - originalTo.Y;
|
||||
|
||||
if (x1 * x2 + y1 * y2 > 0)
|
||||
{
|
||||
output_x = originalTo.X;
|
||||
output_y = originalTo.Y;
|
||||
|
||||
currentVelocity.X = (output_x - originalTo.X) / deltaTime;
|
||||
currentVelocity.Y = (output_y - originalTo.Y) / deltaTime;
|
||||
}
|
||||
|
||||
return new Vector2(output_x, output_y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a cubic interpolation between two vectors.
|
||||
/// </summary>
|
||||
|
||||
@@ -58,6 +58,7 @@ using Mathr = FlaxEngine.Mathf;
|
||||
*/
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@@ -1042,6 +1043,103 @@ namespace FlaxEngine
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a gradual change of a vector towards a specified target over time
|
||||
/// </summary>
|
||||
/// <param name="current">Current vector.</param>
|
||||
/// <param name="target">Target vector.</param>
|
||||
/// <param name="currentVelocity">Used to store the current velocity.</param>
|
||||
/// <param name="smoothTime">Determines the approximate time it should take to reach the target vector.</param>
|
||||
/// <param name="maxSpeed">Defines the upper limit on the speed of the Smooth Damp.</param>
|
||||
public static Vector3 SmoothDamp(Vector3 current, Vector3 target, ref Vector3 currentVelocity, float smoothTime, float maxSpeed)
|
||||
{
|
||||
return SmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed, Time.DeltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a gradual change of a vector towards a specified target over time
|
||||
/// </summary>
|
||||
/// <param name="current">Current vector.</param>
|
||||
/// <param name="target">Target vector.</param>
|
||||
/// <param name="currentVelocity">Used to store the current velocity.</param>
|
||||
/// <param name="smoothTime">Determines the approximate time it should take to reach the target vector.</param>
|
||||
public static Vector3 SmoothDamp(Vector3 current, Vector3 target, ref Vector3 currentVelocity, float smoothTime)
|
||||
{
|
||||
return SmoothDamp(current, target, ref currentVelocity, smoothTime, float.PositiveInfinity, Time.DeltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a gradual change of a vector towards a specified target over time
|
||||
/// </summary>
|
||||
/// <param name="current">Current vector.</param>
|
||||
/// <param name="target">Target vector.</param>
|
||||
/// <param name="currentVelocity">Used to store the current velocity.</param>
|
||||
/// <param name="smoothTime">Determines the approximate time it should take to reach the target vector.</param>
|
||||
/// <param name="maxSpeed">Defines the upper limit on the speed of the Smooth Damp.</param>
|
||||
/// <param name="deltaTime">Delta Time, represents the time elapsed since last frame.</param>
|
||||
public static Vector3 SmoothDamp(Vector3 current, Vector3 target, ref Vector3 currentVelocity, float smoothTime, [DefaultValue("float.PositiveInfinity")] float maxSpeed, [DefaultValue("Time.DeltaTime")] float deltaTime)
|
||||
{
|
||||
smoothTime = Mathf.Max(0.0001f, smoothTime);
|
||||
Real a = 2f / smoothTime;
|
||||
Real b = a * deltaTime;
|
||||
Real e = 1f / (1f + b + 0.48f * b * b + 0.235f * b * b * b);
|
||||
|
||||
Real change_x = current.X - target.X;
|
||||
Real change_y = current.Y - target.Y;
|
||||
Real change_z = current.Z - target.Z;
|
||||
|
||||
Vector3 originalTo = target;
|
||||
|
||||
Real maxChangeSpeed = maxSpeed * smoothTime;
|
||||
Real changeSq = maxChangeSpeed * maxChangeSpeed;
|
||||
Real sqrLen = change_x * change_x + change_y * change_y + change_z * change_z;
|
||||
if (sqrLen > changeSq)
|
||||
{
|
||||
var len = (Real)Math.Sqrt(sqrLen);
|
||||
change_x = change_x / len * maxChangeSpeed;
|
||||
change_y = change_y / len * maxChangeSpeed;
|
||||
change_z = change_z / len * maxChangeSpeed;
|
||||
}
|
||||
|
||||
target.X = current.X - change_x;
|
||||
target.Y = current.Y - change_y;
|
||||
target.Z = current.Z - change_z;
|
||||
|
||||
Real temp_x = (currentVelocity.X + a * change_x) * deltaTime;
|
||||
Real temp_y = (currentVelocity.Y + a * change_y) * deltaTime;
|
||||
Real temp_z = (currentVelocity.Z + a * change_z) * deltaTime;
|
||||
|
||||
currentVelocity.X = (currentVelocity.X - a * temp_x) * e;
|
||||
currentVelocity.Y = (currentVelocity.Y - a * temp_y) * e;
|
||||
currentVelocity.Z = (currentVelocity.Z - a * temp_z) * e;
|
||||
|
||||
Real output_x = target.X + (change_x + temp_x) * e;
|
||||
Real output_y = target.Y + (change_y + temp_y) * e;
|
||||
Real output_z = target.Z + (change_z + temp_z) * e;
|
||||
|
||||
Real x1 = originalTo.X - current.X;
|
||||
Real y1 = originalTo.Y - current.Y;
|
||||
Real z1 = originalTo.Z - current.Z;
|
||||
|
||||
Real x2 = output_x - originalTo.X;
|
||||
Real y2 = output_y - originalTo.Y;
|
||||
Real z2 = output_z - originalTo.Z;
|
||||
|
||||
if (x1 * x2 + y1 * y2 + z1 * z2 > 0)
|
||||
{
|
||||
output_x = originalTo.X;
|
||||
output_y = originalTo.Y;
|
||||
output_z = originalTo.Z;
|
||||
|
||||
currentVelocity.X = (output_x - originalTo.X) / deltaTime;
|
||||
currentVelocity.Y = (output_y - originalTo.Y) / deltaTime;
|
||||
currentVelocity.Z = (output_z - originalTo.Z) / deltaTime;
|
||||
}
|
||||
|
||||
return new Vector3(output_x, output_y, output_z);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Performs a cubic interpolation between two vectors.
|
||||
/// </summary>
|
||||
|
||||
@@ -102,9 +102,6 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
|
||||
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
|
||||
Time::StartupTime = DateTime::Now();
|
||||
#if COMPILE_WITH_PROFILER
|
||||
ProfilerCPU::Enabled = true;
|
||||
#endif
|
||||
Globals::StartupFolder = Globals::BinariesFolder = Platform::GetMainDirectory();
|
||||
#if USE_EDITOR
|
||||
Globals::StartupFolder /= TEXT("../../../..");
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace FlaxEngine.Interop
|
||||
|
||||
internal static IntPtr MarshalReturnValueType(ref Type returnValue)
|
||||
{
|
||||
return returnValue != null ? ManagedHandle.ToIntPtr(GetTypeGCHandle(returnValue)) : IntPtr.Zero;
|
||||
return returnValue != null ? ManagedHandle.ToIntPtr(GetTypeManagedHandle(returnValue)) : IntPtr.Zero;
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueArray<TRet>(ref TRet returnValue)
|
||||
@@ -162,8 +162,8 @@ namespace FlaxEngine.Interop
|
||||
return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnObject);
|
||||
if (returnType == typeof(bool))
|
||||
return (bool)returnObject ? boolTruePtr : boolFalsePtr;
|
||||
if (returnType == typeof(Type))
|
||||
return ManagedHandle.ToIntPtr(GetTypeGCHandle(Unsafe.As<Type>(returnObject)));
|
||||
if (returnType == typeof(Type) || returnType == typeof(TypeHolder))
|
||||
return ManagedHandle.ToIntPtr(GetTypeManagedHandle(Unsafe.As<Type>(returnObject)));
|
||||
if (returnType.IsArray && ArrayFactory.GetMarshalledType(returnType.GetElementType()) == returnType.GetElementType())
|
||||
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As<Array>(returnObject)), GCHandleType.Weak);
|
||||
if (returnType.IsArray)
|
||||
@@ -186,8 +186,8 @@ namespace FlaxEngine.Interop
|
||||
return (IntPtr)(object)returnObject;
|
||||
if (returnType == typeof(ManagedHandle))
|
||||
return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnObject);
|
||||
if (returnType == typeof(Type))
|
||||
return returnObject != null ? ManagedHandle.ToIntPtr(GetTypeGCHandle(Unsafe.As<Type>(returnObject))) : IntPtr.Zero;
|
||||
if (returnType == typeof(Type) || returnType == typeof(TypeHolder))
|
||||
return returnObject != null ? ManagedHandle.ToIntPtr(GetTypeManagedHandle(Unsafe.As<Type>(returnObject))) : IntPtr.Zero;
|
||||
if (returnType.IsArray)
|
||||
{
|
||||
var elementType = returnType.GetElementType();
|
||||
|
||||
@@ -119,13 +119,13 @@ namespace FlaxEngine.Interop
|
||||
[CustomMarshaller(typeof(Type), MarshalMode.Default, typeof(SystemTypeMarshaller))]
|
||||
public static class SystemTypeMarshaller
|
||||
{
|
||||
public static Type ConvertToManaged(IntPtr unmanaged) => Unsafe.As<Type>(ManagedHandleMarshaller.ConvertToManaged(unmanaged));
|
||||
public static Type ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As<FlaxEngine.Interop.NativeInterop.TypeHolder>(ManagedHandleMarshaller.ConvertToManaged(unmanaged)).type : null;
|
||||
|
||||
public static IntPtr ConvertToUnmanaged(Type managed)
|
||||
{
|
||||
if (managed == null)
|
||||
return IntPtr.Zero;
|
||||
ManagedHandle handle = NativeInterop.GetTypeGCHandle(managed);
|
||||
ManagedHandle handle = NativeInterop.GetTypeManagedHandle(managed);
|
||||
return ManagedHandle.ToIntPtr(handle);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace FlaxEngine.Interop
|
||||
internal struct NativeClassDefinitions
|
||||
{
|
||||
internal ManagedHandle typeHandle;
|
||||
internal IntPtr nativePointer;
|
||||
internal IntPtr name;
|
||||
internal IntPtr fullname;
|
||||
internal IntPtr @namespace;
|
||||
@@ -40,6 +41,7 @@ namespace FlaxEngine.Interop
|
||||
internal IntPtr name;
|
||||
internal ManagedHandle fieldHandle;
|
||||
internal ManagedHandle fieldTypeHandle;
|
||||
internal int fieldOffset;
|
||||
internal uint fieldAttributes;
|
||||
}
|
||||
|
||||
@@ -139,6 +141,9 @@ namespace FlaxEngine.Interop
|
||||
|
||||
unsafe partial class NativeInterop
|
||||
{
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "NativeInterop_CreateClass", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
|
||||
internal static partial void NativeInterop_CreateClass(ref NativeClassDefinitions managedClass, ManagedHandle assemblyHandle);
|
||||
|
||||
internal enum MTypes : uint
|
||||
{
|
||||
End = 0x00,
|
||||
@@ -205,46 +210,8 @@ namespace FlaxEngine.Interop
|
||||
NativeMemory.AlignedFree(ptr);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void GetManagedClasses(ManagedHandle assemblyHandle, NativeClassDefinitions** managedClasses, int* managedClassCount)
|
||||
private static Assembly GetOwningAssembly(Type type)
|
||||
{
|
||||
Assembly assembly = Unsafe.As<Assembly>(assemblyHandle.Target);
|
||||
var assemblyTypes = GetAssemblyTypes(assembly);
|
||||
|
||||
NativeClassDefinitions* arr = (NativeClassDefinitions*)NativeAlloc(assemblyTypes.Length, Unsafe.SizeOf<NativeClassDefinitions>());
|
||||
|
||||
for (int i = 0; i < assemblyTypes.Length; i++)
|
||||
{
|
||||
var type = assemblyTypes[i];
|
||||
IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf<NativeClassDefinitions>() * i);
|
||||
var managedClass = new NativeClassDefinitions
|
||||
{
|
||||
typeHandle = GetTypeGCHandle(type),
|
||||
name = NativeAllocStringAnsi(type.Name),
|
||||
fullname = NativeAllocStringAnsi(type.GetTypeName()),
|
||||
@namespace = NativeAllocStringAnsi(type.Namespace ?? ""),
|
||||
typeAttributes = (uint)type.Attributes,
|
||||
};
|
||||
Unsafe.Write(ptr.ToPointer(), managedClass);
|
||||
}
|
||||
|
||||
*managedClasses = arr;
|
||||
*managedClassCount = assemblyTypes.Length;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void GetManagedClassFromType(ManagedHandle typeHandle, NativeClassDefinitions* managedClass, ManagedHandle* assemblyHandle)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
*managedClass = new NativeClassDefinitions
|
||||
{
|
||||
typeHandle = GetTypeGCHandle(type),
|
||||
name = NativeAllocStringAnsi(type.Name),
|
||||
fullname = NativeAllocStringAnsi(type.GetTypeName()),
|
||||
@namespace = NativeAllocStringAnsi(type.Namespace ?? ""),
|
||||
typeAttributes = (uint)type.Attributes,
|
||||
};
|
||||
|
||||
Assembly assembly = null;
|
||||
if (type.IsGenericType && !type.Assembly.IsCollectible)
|
||||
{
|
||||
@@ -261,14 +228,87 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
if (assembly == null)
|
||||
assembly = type.Assembly;
|
||||
return assembly;
|
||||
}
|
||||
|
||||
*assemblyHandle = GetAssemblyHandle(assembly);
|
||||
private static NativeClassDefinitions CreateNativeClassDefinitions(Type type, out ManagedHandle assemblyHandle)
|
||||
{
|
||||
assemblyHandle = GetAssemblyHandle(GetOwningAssembly(type));
|
||||
return CreateNativeClassDefinitions(type);
|
||||
}
|
||||
|
||||
private static NativeClassDefinitions CreateNativeClassDefinitions(Type type)
|
||||
{
|
||||
return new NativeClassDefinitions()
|
||||
{
|
||||
typeHandle = RegisterType(type).handle,
|
||||
name = NativeAllocStringAnsi(type.Name),
|
||||
fullname = NativeAllocStringAnsi(type.GetTypeName()),
|
||||
@namespace = NativeAllocStringAnsi(type.Namespace ?? ""),
|
||||
typeAttributes = (uint)type.Attributes,
|
||||
};
|
||||
}
|
||||
|
||||
private static NativeClassDefinitions CreateNativeClassDefinitions(Type type, ManagedHandle typeHandle, out ManagedHandle assemblyHandle)
|
||||
{
|
||||
assemblyHandle = GetAssemblyHandle(GetOwningAssembly(type));
|
||||
return new NativeClassDefinitions()
|
||||
{
|
||||
typeHandle = typeHandle,
|
||||
name = NativeAllocStringAnsi(type.Name),
|
||||
fullname = NativeAllocStringAnsi(type.GetTypeName()),
|
||||
@namespace = NativeAllocStringAnsi(type.Namespace ?? ""),
|
||||
typeAttributes = (uint)type.Attributes,
|
||||
};
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void GetManagedClasses(ManagedHandle assemblyHandle, NativeClassDefinitions** managedClasses, int* managedClassCount)
|
||||
{
|
||||
Assembly assembly = Unsafe.As<Assembly>(assemblyHandle.Target);
|
||||
Type[] assemblyTypes = GetAssemblyTypes(assembly);
|
||||
|
||||
*managedClasses = (NativeClassDefinitions*)NativeAlloc(assemblyTypes.Length, Unsafe.SizeOf<NativeClassDefinitions>());
|
||||
*managedClassCount = assemblyTypes.Length;
|
||||
Span<NativeClassDefinitions> span = new Span<NativeClassDefinitions>(*managedClasses, assemblyTypes.Length);
|
||||
for (int i = 0; i < assemblyTypes.Length; i++)
|
||||
{
|
||||
Type type = assemblyTypes[i];
|
||||
ref var managedClass = ref span[i];
|
||||
managedClass = CreateNativeClassDefinitions(type);
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void RegisterManagedClassNativePointers(NativeClassDefinitions** managedClasses, int managedClassCount)
|
||||
{
|
||||
Span<NativeClassDefinitions> span = new Span<NativeClassDefinitions>(Unsafe.Read<IntPtr>(managedClasses).ToPointer(), managedClassCount);
|
||||
foreach (ref NativeClassDefinitions managedClass in span)
|
||||
{
|
||||
TypeHolder typeHolder = Unsafe.As<TypeHolder>(managedClass.typeHandle.Target);
|
||||
typeHolder.managedClassPointer = managedClass.nativePointer;
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void GetManagedClassFromType(ManagedHandle typeHandle, NativeClassDefinitions* managedClass, ManagedHandle* assemblyHandle)
|
||||
{
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
*managedClass = CreateNativeClassDefinitions(type, out ManagedHandle handle);
|
||||
*assemblyHandle = handle;
|
||||
}
|
||||
|
||||
private static void RegisterNativeClassFromType(TypeHolder typeHolder, ManagedHandle typeHandle)
|
||||
{
|
||||
NativeClassDefinitions managedClass = CreateNativeClassDefinitions(typeHolder.type, typeHandle, out ManagedHandle assemblyHandle);
|
||||
NativeInterop_CreateClass(ref managedClass, assemblyHandle);
|
||||
typeHolder.managedClassPointer = managedClass.nativePointer;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void GetClassMethods(ManagedHandle typeHandle, NativeMethodDefinitions** classMethods, int* classMethodsCount)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
|
||||
var methods = new List<MethodInfo>();
|
||||
var staticMethods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
|
||||
@@ -296,7 +336,7 @@ namespace FlaxEngine.Interop
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void GetClassFields(ManagedHandle typeHandle, NativeFieldDefinitions** classFields, int* classFieldsCount)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
var fields = type.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
|
||||
NativeFieldDefinitions* arr = (NativeFieldDefinitions*)NativeAlloc(fields.Length, Unsafe.SizeOf<NativeFieldDefinitions>());
|
||||
@@ -318,7 +358,8 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
name = NativeAllocStringAnsi(fieldHolder.field.Name),
|
||||
fieldHandle = fieldHandle,
|
||||
fieldTypeHandle = GetTypeGCHandle(fieldHolder.field.FieldType),
|
||||
fieldTypeHandle = GetTypeManagedHandle(fieldHolder.field.FieldType),
|
||||
fieldOffset = fieldHolder.fieldOffset,
|
||||
fieldAttributes = (uint)fieldHolder.field.Attributes,
|
||||
};
|
||||
Unsafe.Write(IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf<NativeFieldDefinitions>() * i).ToPointer(), classField);
|
||||
@@ -330,7 +371,7 @@ namespace FlaxEngine.Interop
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void GetClassProperties(ManagedHandle typeHandle, NativePropertyDefinitions** classProperties, int* classPropertiesCount)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
var properties = type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
|
||||
var arr = (NativePropertyDefinitions*)NativeAlloc(properties.Length, Unsafe.SizeOf<NativePropertyDefinitions>());
|
||||
@@ -364,7 +405,7 @@ namespace FlaxEngine.Interop
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void GetClassAttributes(ManagedHandle typeHandle, ManagedHandle** classAttributes, int* classAttributesCount)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
object[] attributeValues = type.GetCustomAttributes(false);
|
||||
|
||||
ManagedHandle* arr = (ManagedHandle*)NativeAlloc(attributeValues.Length, Unsafe.SizeOf<ManagedHandle>());
|
||||
@@ -384,13 +425,13 @@ namespace FlaxEngine.Interop
|
||||
[UnmanagedCallersOnly]
|
||||
internal static ManagedHandle GetCustomAttribute(ManagedHandle typeHandle, ManagedHandle attributeHandle)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
var attributes = type.GetCustomAttributes(false);
|
||||
object attrib;
|
||||
if (attributeHandle.IsAllocated)
|
||||
{
|
||||
// Check for certain attribute type
|
||||
Type attributeType = Unsafe.As<Type>(attributeHandle.Target);
|
||||
Type attributeType = Unsafe.As<TypeHolder>(attributeHandle.Target);
|
||||
attrib = attributes.FirstOrDefault(x => x.GetType() == attributeType);
|
||||
}
|
||||
else
|
||||
@@ -413,7 +454,7 @@ namespace FlaxEngine.Interop
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void GetClassInterfaces(ManagedHandle typeHandle, IntPtr* classInterfaces, int* classInterfacesCount)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
Type[] interfaces = type.GetInterfaces();
|
||||
|
||||
// Match mono_class_get_interfaces which doesn't return interfaces from base class
|
||||
@@ -465,7 +506,7 @@ namespace FlaxEngine.Interop
|
||||
IntPtr arr = (IntPtr)NativeAlloc(interfaces.Length, IntPtr.Size);
|
||||
for (int i = 0; i < interfaces.Length; i++)
|
||||
{
|
||||
ManagedHandle handle = GetTypeGCHandle(interfaces[i]);
|
||||
ManagedHandle handle = GetTypeManagedHandle(interfaces[i]);
|
||||
Unsafe.Write<ManagedHandle>(IntPtr.Add(arr, IntPtr.Size * i).ToPointer(), handle);
|
||||
}
|
||||
*classInterfaces = arr;
|
||||
@@ -477,7 +518,7 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
MethodHolder methodHolder = Unsafe.As<MethodHolder>(methodHandle.Target);
|
||||
Type returnType = methodHolder.returnType;
|
||||
return GetTypeGCHandle(returnType);
|
||||
return GetTypeManagedHandle(returnType);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
@@ -488,7 +529,7 @@ namespace FlaxEngine.Interop
|
||||
IntPtr arr = (IntPtr)NativeAlloc(methodHolder.parameterTypes.Length, IntPtr.Size);
|
||||
for (int i = 0; i < methodHolder.parameterTypes.Length; i++)
|
||||
{
|
||||
ManagedHandle typeHandle = GetTypeGCHandle(methodHolder.parameterTypes[i]);
|
||||
ManagedHandle typeHandle = GetTypeManagedHandle(methodHolder.parameterTypes[i]);
|
||||
Unsafe.Write<ManagedHandle>(IntPtr.Add(new IntPtr(arr), IntPtr.Size * i).ToPointer(), typeHandle);
|
||||
}
|
||||
*typeHandles = arr;
|
||||
@@ -509,22 +550,15 @@ namespace FlaxEngine.Interop
|
||||
[UnmanagedCallersOnly]
|
||||
internal static ManagedHandle NewObject(ManagedHandle typeHandle)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
if (type.IsAbstract)
|
||||
{
|
||||
// Dotnet doesn't allow to instantiate abstract type thus allow to use generated mock class usage (eg. for Script or GPUResource) for generated abstract types
|
||||
var abstractWrapper = type.GetNestedType("AbstractWrapper", BindingFlags.NonPublic);
|
||||
if (abstractWrapper != null)
|
||||
type = abstractWrapper;
|
||||
}
|
||||
object value = RuntimeHelpers.GetUninitializedObject(type);
|
||||
TypeHolder typeHolder = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
object value = typeHolder.CreateObject();
|
||||
return ManagedHandle.Alloc(value);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static ManagedHandle NewArray(ManagedHandle typeHandle, long size)
|
||||
{
|
||||
Type elementType = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type elementType = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
Type marshalledType = ArrayFactory.GetMarshalledType(elementType);
|
||||
Type arrayType = ArrayFactory.GetArrayType(elementType);
|
||||
if (marshalledType.IsValueType)
|
||||
@@ -543,9 +577,9 @@ namespace FlaxEngine.Interop
|
||||
[UnmanagedCallersOnly]
|
||||
internal static ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle)
|
||||
{
|
||||
Type elementType = Unsafe.As<Type>(elementTypeHandle.Target);
|
||||
Type elementType = Unsafe.As<TypeHolder>(elementTypeHandle.Target);
|
||||
Type classType = ArrayFactory.GetArrayType(elementType);
|
||||
return GetTypeGCHandle(classType);
|
||||
return GetTypeManagedHandle(classType);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
@@ -606,7 +640,7 @@ namespace FlaxEngine.Interop
|
||||
Type classType = obj.GetType();
|
||||
if (classType == typeof(ManagedArray))
|
||||
classType = ((ManagedArray)obj).ArrayType;
|
||||
return GetTypeGCHandle(classType);
|
||||
return GetTypeManagedHandle(classType);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
@@ -647,7 +681,7 @@ namespace FlaxEngine.Interop
|
||||
[UnmanagedCallersOnly]
|
||||
internal static ManagedHandle BoxValue(ManagedHandle typeHandle, IntPtr valuePtr)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
object value = MarshalToManaged(valuePtr, type);
|
||||
return ManagedHandle.Alloc(value, GCHandleType.Weak);
|
||||
}
|
||||
@@ -690,6 +724,14 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static IntPtr GetObjectClass(ManagedHandle objectHandle)
|
||||
{
|
||||
object obj = objectHandle.Target;
|
||||
TypeHolder typeHolder = GetTypeHolder(obj.GetType());
|
||||
return typeHolder.managedClassPointer;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static IntPtr InvokeMethod(ManagedHandle instanceHandle, ManagedHandle methodHandle, IntPtr paramPtr, IntPtr exceptionPtr)
|
||||
{
|
||||
@@ -706,7 +748,7 @@ namespace FlaxEngine.Interop
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exceptionPtr != IntPtr.Zero)
|
||||
Marshal.WriteIntPtr(exceptionPtr, ManagedHandle.ToIntPtr(exception, GCHandleType.Weak));
|
||||
Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(exception, GCHandleType.Weak));
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
return returnValue;
|
||||
@@ -721,7 +763,7 @@ namespace FlaxEngine.Interop
|
||||
|
||||
for (int i = 0; i < numParams; i++)
|
||||
{
|
||||
IntPtr nativePtr = Marshal.ReadIntPtr(IntPtr.Add(paramPtr, sizeof(IntPtr) * i));
|
||||
IntPtr nativePtr = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * i)).ToPointer());
|
||||
methodParameters[i] = MarshalToManaged(nativePtr, methodHolder.parameterTypes[i]);
|
||||
}
|
||||
|
||||
@@ -737,7 +779,7 @@ namespace FlaxEngine.Interop
|
||||
realException = exception.InnerException;
|
||||
|
||||
if (exceptionPtr != IntPtr.Zero)
|
||||
Marshal.WriteIntPtr(exceptionPtr, ManagedHandle.ToIntPtr(realException, GCHandleType.Weak));
|
||||
Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(realException, GCHandleType.Weak));
|
||||
else
|
||||
throw realException;
|
||||
return IntPtr.Zero;
|
||||
@@ -749,7 +791,7 @@ namespace FlaxEngine.Interop
|
||||
Type parameterType = methodHolder.parameterTypes[i];
|
||||
if (parameterType.IsByRef)
|
||||
{
|
||||
IntPtr nativePtr = Marshal.ReadIntPtr(IntPtr.Add(paramPtr, sizeof(IntPtr) * i));
|
||||
IntPtr nativePtr = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * i)).ToPointer());
|
||||
MarshalToNative(methodParameters[i], nativePtr, parameterType.GetElementType());
|
||||
}
|
||||
}
|
||||
@@ -803,7 +845,7 @@ namespace FlaxEngine.Interop
|
||||
internal static int FieldGetOffset(ManagedHandle fieldHandle)
|
||||
{
|
||||
FieldHolder field = Unsafe.As<FieldHolder>(fieldHandle.Target);
|
||||
return (int)Marshal.OffsetOf(field.field.DeclaringType, field.field.Name);
|
||||
return field.fieldOffset;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
@@ -811,7 +853,40 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
object fieldOwner = fieldOwnerHandle.Target;
|
||||
FieldHolder field = Unsafe.As<FieldHolder>(fieldHandle.Target);
|
||||
field.toNativeMarshaller(field.field, fieldOwner, valuePtr, out int fieldOffset);
|
||||
field.toNativeMarshaller(field.field, field.fieldOffset, fieldOwner, valuePtr, out int fieldSize);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void FieldGetValueReference(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, IntPtr valuePtr)
|
||||
{
|
||||
object fieldOwner = fieldOwnerHandle.Target;
|
||||
FieldHolder field = Unsafe.As<FieldHolder>(fieldHandle.Target);
|
||||
if (fieldOwner.GetType().IsValueType)
|
||||
{
|
||||
ref IntPtr fieldRef = ref FieldHelper.GetValueTypeFieldReference<object, IntPtr>(field.fieldOffset, ref fieldOwner);
|
||||
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
|
||||
}
|
||||
else
|
||||
{
|
||||
ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<object, IntPtr>(field.fieldOffset, ref fieldOwner);
|
||||
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void FieldGetValueReferenceWithOffset(ManagedHandle fieldOwnerHandle, int fieldOffset, IntPtr valuePtr)
|
||||
{
|
||||
object fieldOwner = fieldOwnerHandle.Target;
|
||||
if (fieldOwner.GetType().IsValueType)
|
||||
{
|
||||
ref IntPtr fieldRef = ref FieldHelper.GetValueTypeFieldReference<object, IntPtr>(fieldOffset, ref fieldOwner);
|
||||
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
|
||||
}
|
||||
else
|
||||
{
|
||||
ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<object, IntPtr>(fieldOffset, ref fieldOwner);
|
||||
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
@@ -839,7 +914,15 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static ManagedHandle LoadAssemblyImage(IntPtr assemblyPathPtr, IntPtr* assemblyName, IntPtr* assemblyFullName)
|
||||
internal static void GetAssemblyName(ManagedHandle assemblyHandle, IntPtr* assemblyName, IntPtr* assemblyFullName)
|
||||
{
|
||||
Assembly assembly = Unsafe.As<Assembly>(assemblyHandle.Target);
|
||||
*assemblyName = NativeAllocStringAnsi(assembly.GetName().Name);
|
||||
*assemblyFullName = NativeAllocStringAnsi(assembly.FullName);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static ManagedHandle LoadAssemblyImage(IntPtr assemblyPathPtr)
|
||||
{
|
||||
if (!firstAssemblyLoaded)
|
||||
{
|
||||
@@ -847,55 +930,55 @@ namespace FlaxEngine.Interop
|
||||
firstAssemblyLoaded = true;
|
||||
|
||||
Assembly flaxEngineAssembly = AssemblyLoadContext.Default.Assemblies.First(x => x.GetName().Name == "FlaxEngine.CSharp");
|
||||
*assemblyName = NativeAllocStringAnsi(flaxEngineAssembly.GetName().Name);
|
||||
*assemblyFullName = NativeAllocStringAnsi(flaxEngineAssembly.FullName);
|
||||
return GetAssemblyHandle(flaxEngineAssembly);
|
||||
}
|
||||
try
|
||||
{
|
||||
string assemblyPath = Marshal.PtrToStringUni(assemblyPathPtr);
|
||||
|
||||
string assemblyPath = Marshal.PtrToStringAnsi(assemblyPathPtr);
|
||||
|
||||
Assembly assembly;
|
||||
Assembly assembly;
|
||||
#if FLAX_EDITOR
|
||||
// Load assembly from loaded bytes to prevent file locking in Editor
|
||||
var assemblyBytes = File.ReadAllBytes(assemblyPath);
|
||||
using MemoryStream stream = new MemoryStream(assemblyBytes);
|
||||
var pdbPath = Path.ChangeExtension(assemblyPath, "pdb");
|
||||
if (File.Exists(pdbPath))
|
||||
{
|
||||
// Load including debug symbols
|
||||
using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open);
|
||||
assembly = scriptingAssemblyLoadContext.LoadFromStream(stream, pdbStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
assembly = scriptingAssemblyLoadContext.LoadFromStream(stream);
|
||||
}
|
||||
// Load assembly from loaded bytes to prevent file locking in Editor
|
||||
var assemblyBytes = File.ReadAllBytes(assemblyPath);
|
||||
using MemoryStream stream = new MemoryStream(assemblyBytes);
|
||||
var pdbPath = Path.ChangeExtension(assemblyPath, "pdb");
|
||||
if (File.Exists(pdbPath))
|
||||
{
|
||||
// Load including debug symbols
|
||||
using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open);
|
||||
assembly = scriptingAssemblyLoadContext.LoadFromStream(stream, pdbStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
assembly = scriptingAssemblyLoadContext.LoadFromStream(stream);
|
||||
}
|
||||
#else
|
||||
// Load assembly from file
|
||||
assembly = scriptingAssemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
|
||||
// Load assembly from file
|
||||
assembly = scriptingAssemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
|
||||
#endif
|
||||
if (assembly == null)
|
||||
return new ManagedHandle();
|
||||
NativeLibrary.SetDllImportResolver(assembly, NativeLibraryImportResolver);
|
||||
if (assembly == null)
|
||||
return new ManagedHandle();
|
||||
NativeLibrary.SetDllImportResolver(assembly, NativeLibraryImportResolver);
|
||||
|
||||
// Assemblies loaded via streams have no Location: https://github.com/dotnet/runtime/issues/12822
|
||||
AssemblyLocations.Add(assembly.FullName, assemblyPath);
|
||||
// Assemblies loaded via streams have no Location: https://github.com/dotnet/runtime/issues/12822
|
||||
AssemblyLocations.Add(assembly.FullName, assemblyPath);
|
||||
|
||||
*assemblyName = NativeAllocStringAnsi(assembly.GetName().Name);
|
||||
*assemblyFullName = NativeAllocStringAnsi(assembly.FullName);
|
||||
return GetAssemblyHandle(assembly);
|
||||
return GetAssemblyHandle(assembly);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
return new ManagedHandle();
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static ManagedHandle GetAssemblyByName(IntPtr namePtr, IntPtr* assemblyName, IntPtr* assemblyFullName)
|
||||
internal static ManagedHandle GetAssemblyByName(IntPtr namePtr)
|
||||
{
|
||||
string name = Marshal.PtrToStringAnsi(namePtr);
|
||||
Assembly assembly = Utils.GetAssemblies().FirstOrDefault(x => x.GetName().Name == name);
|
||||
if (assembly == null)
|
||||
return new ManagedHandle();
|
||||
|
||||
*assemblyName = NativeAllocStringAnsi(assembly.GetName().Name);
|
||||
*assemblyFullName = NativeAllocStringAnsi(assembly.FullName);
|
||||
return GetAssemblyHandle(assembly);
|
||||
}
|
||||
|
||||
@@ -934,9 +1017,9 @@ namespace FlaxEngine.Interop
|
||||
|
||||
// Release all references in collectible ALC
|
||||
cachedDelegatesCollectible.Clear();
|
||||
foreach (var pair in typeHandleCacheCollectible)
|
||||
pair.Value.Free();
|
||||
typeHandleCacheCollectible.Clear();
|
||||
foreach (var pair in managedTypesCollectible)
|
||||
pair.Value.handle.Free();
|
||||
managedTypesCollectible.Clear();
|
||||
foreach (var handle in methodHandlesCollectible)
|
||||
handle.Free();
|
||||
methodHandlesCollectible.Clear();
|
||||
@@ -966,7 +1049,7 @@ namespace FlaxEngine.Interop
|
||||
[UnmanagedCallersOnly]
|
||||
internal static int NativeSizeOf(ManagedHandle typeHandle)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
Type nativeType = GetInternalType(type) ?? type;
|
||||
if (nativeType == typeof(Version))
|
||||
nativeType = typeof(NativeVersion);
|
||||
@@ -984,8 +1067,8 @@ namespace FlaxEngine.Interop
|
||||
if (typeHandle == otherTypeHandle)
|
||||
return 1;
|
||||
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type otherType = Unsafe.As<Type>(otherTypeHandle.Target);
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
Type otherType = Unsafe.As<TypeHolder>(otherTypeHandle.Target);
|
||||
|
||||
if (type == otherType)
|
||||
return 1;
|
||||
@@ -1002,37 +1085,39 @@ namespace FlaxEngine.Interop
|
||||
[UnmanagedCallersOnly]
|
||||
internal static byte TypeIsAssignableFrom(ManagedHandle typeHandle, ManagedHandle otherTypeHandle)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type otherType = Unsafe.As<Type>(otherTypeHandle.Target);
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
Type otherType = Unsafe.As<TypeHolder>(otherTypeHandle.Target);
|
||||
return (byte)(type.IsAssignableFrom(otherType) ? 1 : 0);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static byte TypeIsValueType(ManagedHandle typeHandle)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
return (byte)(type.IsValueType ? 1 : 0);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static byte TypeIsEnum(ManagedHandle typeHandle)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
return (byte)(type.IsEnum ? 1 : 0);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static ManagedHandle GetClassParent(ManagedHandle typeHandle)
|
||||
internal static IntPtr GetClassParent(ManagedHandle typeHandle)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
return GetTypeGCHandle(type.BaseType);
|
||||
TypeHolder typeHolder = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
TypeHolder baseTypeHolder = GetTypeHolder(typeHolder.type.BaseType);
|
||||
return baseTypeHolder.managedClassPointer;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static ManagedHandle GetElementClass(ManagedHandle typeHandle)
|
||||
internal static IntPtr GetElementClass(ManagedHandle typeHandle)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
return GetTypeGCHandle(type.GetElementType());
|
||||
TypeHolder typeHolder = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
TypeHolder elementTypeHolder = GetTypeHolder(typeHolder.type.GetElementType());
|
||||
return elementTypeHolder.managedClassPointer;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
@@ -1123,32 +1208,35 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static ManagedHandle GetTypeClass(ManagedHandle typeHandle)
|
||||
internal static IntPtr GetTypeClass(ManagedHandle typeHandle)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
if (type.IsByRef)
|
||||
type = type.GetElementType(); // Drop reference type (&) to get actual value type
|
||||
return GetTypeGCHandle(type);
|
||||
TypeHolder typeHolder = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
if (typeHolder.type.IsByRef)
|
||||
{
|
||||
// Drop reference type (&) to get actual value type
|
||||
return GetTypeHolder(typeHolder.type.GetElementType()).managedClassPointer;
|
||||
}
|
||||
return typeHolder.managedClassPointer;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static bool GetTypeIsPointer(ManagedHandle typeHandle)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
return type.IsPointer;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static bool GetTypeIsReference(ManagedHandle typeHandle)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
return type.IsByRef;
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
return !type.IsValueType; // Maybe also type.IsByRef?
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static uint GetTypeMTypesEnum(ManagedHandle typeHandle)
|
||||
{
|
||||
Type type = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
if (type.IsByRef)
|
||||
type = type.GetElementType(); // Drop reference type (&) to get actual value type
|
||||
MTypes monoType;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -181,6 +181,11 @@ void Screen::SetGameWindowMode(GameWindowMode windowMode)
|
||||
#endif
|
||||
}
|
||||
|
||||
Window* Screen::GetMainWindow()
|
||||
{
|
||||
return Engine::MainWindow;
|
||||
}
|
||||
|
||||
void ScreenService::Update()
|
||||
{
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -96,4 +96,10 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(Screen);
|
||||
/// </remarks>
|
||||
/// <param name="windowMode">The window mode.</param>
|
||||
API_PROPERTY() static void SetGameWindowMode(GameWindowMode windowMode);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the main window.
|
||||
/// </summary>
|
||||
/// <returns>The current window. Will be null if fails.</returns>
|
||||
API_PROPERTY() static Window* GetMainWindow();
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -364,13 +364,20 @@ namespace FlaxEngine
|
||||
/// <param name="point">The point (world-space).</param>
|
||||
/// <param name="axis">The axis (normalized).</param>
|
||||
/// <param name="angle">The angle (in degrees).</param>
|
||||
public void RotateAround(Vector3 point, Vector3 axis, float angle)
|
||||
/// /// <param name="orientActor">Whether to orient the actor the same amount as rotation.</param>
|
||||
public void RotateAround(Vector3 point, Vector3 axis, float angle, bool orientActor = true)
|
||||
{
|
||||
var transform = Transform;
|
||||
var q = Quaternion.RotationAxis(axis, angle * Mathf.DegreesToRadians);
|
||||
var dif = (transform.Translation - point) * q;
|
||||
transform.Translation = point + dif;
|
||||
transform.Orientation = q;
|
||||
if (Vector3.NearEqual(point, transform.Translation) && orientActor)
|
||||
transform.Orientation *= q;
|
||||
else
|
||||
{
|
||||
var dif = (transform.Translation - point) * q;
|
||||
transform.Translation = point + dif;
|
||||
if (orientActor)
|
||||
transform.Orientation *= q;
|
||||
}
|
||||
Transform = transform;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
@@ -32,7 +32,7 @@ void SceneTicking::TickData::RemoveTick(void* callee)
|
||||
{
|
||||
for (int32 i = 0; i < Ticks.Count(); i++)
|
||||
{
|
||||
if (Ticks[i].Callee == callee)
|
||||
if (Ticks.Get()[i].Callee == callee)
|
||||
{
|
||||
Ticks.RemoveAt(i);
|
||||
break;
|
||||
@@ -45,7 +45,7 @@ void SceneTicking::TickData::Tick()
|
||||
TickScripts(Scripts);
|
||||
|
||||
for (int32 i = 0; i < Ticks.Count(); i++)
|
||||
Ticks[i].Call();
|
||||
Ticks.Get()[i].Call();
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
@@ -54,7 +54,7 @@ void SceneTicking::TickData::RemoveTickExecuteInEditor(void* callee)
|
||||
{
|
||||
for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++)
|
||||
{
|
||||
if (TicksExecuteInEditor[i].Callee == callee)
|
||||
if (TicksExecuteInEditor.Get()[i].Callee == callee)
|
||||
{
|
||||
TicksExecuteInEditor.RemoveAt(i);
|
||||
break;
|
||||
@@ -67,7 +67,7 @@ void SceneTicking::TickData::TickExecuteInEditor()
|
||||
TickScripts(ScriptsExecuteInEditor);
|
||||
|
||||
for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++)
|
||||
TicksExecuteInEditor[i].Call();
|
||||
TicksExecuteInEditor.Get()[i].Call();
|
||||
}
|
||||
|
||||
#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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,5 +81,6 @@ public class Main : EngineModule
|
||||
/// <inheritdoc />
|
||||
public override void GetFilesToDeploy(List<string> files)
|
||||
{
|
||||
files.Add(Path.Combine(FolderPath, "Android/android_native_app_glue.h"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "Types.h"
|
||||
#if COMPILE_WITH_PROFILER
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#endif
|
||||
|
||||
enum class NetworkMessageIDs : uint8
|
||||
{
|
||||
@@ -35,4 +38,22 @@ public:
|
||||
static void OnNetworkMessageObjectDespawn(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
|
||||
static void OnNetworkMessageObjectRole(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
|
||||
static void OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
|
||||
|
||||
#if COMPILE_WITH_PROFILER
|
||||
|
||||
struct ProfilerEvent
|
||||
{
|
||||
uint16 Count = 0;
|
||||
uint16 DataSize = 0;
|
||||
uint16 MessageSize = 0;
|
||||
uint16 Receivers = 0;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Enables network usage profiling tools. Captures network objects replication and RPCs send statistics.
|
||||
/// </summary>
|
||||
static bool EnableProfiling;
|
||||
|
||||
static Dictionary<Pair<ScriptingTypeHandle, StringAnsiView>, ProfilerEvent> ProfilerEvents;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
#include "Engine/Platform/CPUInfo.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
|
||||
Array<NetworkPeer*> NetworkPeer::Peers;
|
||||
|
||||
namespace
|
||||
{
|
||||
Array<NetworkPeer*, HeapAllocation> Peers;
|
||||
uint32 LastHostId = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ API_CLASS(sealed, NoSpawn, Namespace = "FlaxEngine.Networking") class FLAXENGINE
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(NetworkPeer, ScriptingObject);
|
||||
|
||||
// List with all active peers.
|
||||
API_FIELD(ReadOnly) static Array<NetworkPeer*> Peers;
|
||||
|
||||
public:
|
||||
int HostId = -1;
|
||||
NetworkConfig Config;
|
||||
|
||||
@@ -40,6 +40,11 @@ bool NetworkReplicator::EnableLog = false;
|
||||
#define NETWORK_REPLICATOR_LOG(messageType, format, ...)
|
||||
#endif
|
||||
|
||||
#if COMPILE_WITH_PROFILER
|
||||
bool NetworkInternal::EnableProfiling = false;
|
||||
Dictionary<Pair<ScriptingTypeHandle, StringAnsiView>, NetworkInternal::ProfilerEvent> NetworkInternal::ProfilerEvents;
|
||||
#endif
|
||||
|
||||
PACK_STRUCT(struct NetworkMessageObjectReplicate
|
||||
{
|
||||
NetworkMessageIDs ID = NetworkMessageIDs::ObjectReplicate;
|
||||
@@ -1806,6 +1811,7 @@ void NetworkInternal::NetworkReplicatorUpdate()
|
||||
NetworkMessage msg = peer->BeginSendMessage();
|
||||
msg.WriteStructure(msgData);
|
||||
msg.WriteBytes(stream->GetBuffer(), msgDataSize);
|
||||
uint32 dataSize = msgDataSize, messageSize = msg.Length;
|
||||
if (isClient)
|
||||
peer->EndSendMessage(NetworkChannelType::Unreliable, msg);
|
||||
else
|
||||
@@ -1824,6 +1830,8 @@ void NetworkInternal::NetworkReplicatorUpdate()
|
||||
msg = peer->BeginSendMessage();
|
||||
msg.WriteStructure(msgDataPart);
|
||||
msg.WriteBytes(stream->GetBuffer() + msgDataPart.PartStart, msgDataPart.PartSize);
|
||||
messageSize += msg.Length;
|
||||
dataSize += msgDataPart.PartSize;
|
||||
dataStart += msgDataPart.PartSize;
|
||||
if (isClient)
|
||||
peer->EndSendMessage(NetworkChannelType::Unreliable, msg);
|
||||
@@ -1832,7 +1840,18 @@ void NetworkInternal::NetworkReplicatorUpdate()
|
||||
}
|
||||
ASSERT_LOW_LAYER(dataStart == size);
|
||||
|
||||
// TODO: stats for bytes send per object type
|
||||
#if COMPILE_WITH_PROFILER
|
||||
// Network stats recording
|
||||
if (EnableProfiling)
|
||||
{
|
||||
const Pair<ScriptingTypeHandle, StringAnsiView> name(obj->GetTypeHandle(), StringAnsiView::Empty);
|
||||
auto& profileEvent = ProfilerEvents[name];
|
||||
profileEvent.Count++;
|
||||
profileEvent.DataSize += dataSize;
|
||||
profileEvent.MessageSize += messageSize;
|
||||
profileEvent.Receivers += isClient ? 1 : CachedTargets.Count();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1873,6 +1892,7 @@ void NetworkInternal::NetworkReplicatorUpdate()
|
||||
NetworkMessage msg = peer->BeginSendMessage();
|
||||
msg.WriteStructure(msgData);
|
||||
msg.WriteBytes(e.ArgsData.Get(), e.ArgsData.Length());
|
||||
uint32 dataSize = e.ArgsData.Length(), messageSize = msg.Length, receivers = 0;
|
||||
NetworkChannelType channel = (NetworkChannelType)e.Info.Channel;
|
||||
if (e.Info.Server && isClient)
|
||||
{
|
||||
@@ -1882,13 +1902,27 @@ void NetworkInternal::NetworkReplicatorUpdate()
|
||||
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Server RPC '{}::{}' called with non-empty list of targets is not supported (only server will receive it)", e.Name.First.ToString(), e.Name.Second.ToString());
|
||||
#endif
|
||||
peer->EndSendMessage(channel, msg);
|
||||
receivers = 1;
|
||||
}
|
||||
else if (e.Info.Client && (isServer || isHost))
|
||||
{
|
||||
// Server -> Client(s)
|
||||
BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, e.Targets, NetworkManager::LocalClientId);
|
||||
peer->EndSendMessage(channel, msg, CachedTargets);
|
||||
receivers = CachedTargets.Count();
|
||||
}
|
||||
|
||||
#if COMPILE_WITH_PROFILER
|
||||
// Network stats recording
|
||||
if (EnableProfiling && receivers)
|
||||
{
|
||||
auto& profileEvent = ProfilerEvents[e.Name];
|
||||
profileEvent.Count++;
|
||||
profileEvent.DataSize += dataSize;
|
||||
profileEvent.MessageSize += messageSize;
|
||||
profileEvent.Receivers += receivers;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
RpcQueue.Clear();
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
/// <summary>
|
||||
/// The network transport driver statistics container. Contains information about INetworkDriver usage and performance.
|
||||
/// </summary>
|
||||
API_STRUCT(Namespace="FlaxEngine.Networking") struct FLAXENGINE_API NetworkDriverStats
|
||||
API_STRUCT(Namespace="FlaxEngine.Networking", NoDefault) struct FLAXENGINE_API NetworkDriverStats
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkDriverStats);
|
||||
|
||||
|
||||
@@ -402,7 +402,7 @@ SceneRenderTask* ParticleEffect::GetRenderTask() const
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
Array<ParticleEffect::ParameterOverride> ParticleEffect::GetParametersOverrides()
|
||||
Array<ParticleEffect::ParameterOverride>& ParticleEffect::GetParametersOverrides()
|
||||
{
|
||||
CacheModifiedParameters();
|
||||
return _parametersOverrides;
|
||||
@@ -461,7 +461,6 @@ void ParticleEffect::CacheModifiedParameters()
|
||||
{
|
||||
if (_parameters.IsEmpty())
|
||||
return;
|
||||
|
||||
_parametersOverrides.Clear();
|
||||
auto& parameters = GetParameters();
|
||||
for (auto& param : parameters)
|
||||
|
||||
@@ -382,7 +382,7 @@ public:
|
||||
#if USE_EDITOR
|
||||
protected:
|
||||
// Exposed parameters overrides for Editor Undo.
|
||||
API_PROPERTY(Attributes="HideInEditor, Serialize") Array<ParticleEffect::ParameterOverride> GetParametersOverrides();
|
||||
API_PROPERTY(Attributes="HideInEditor, Serialize") Array<ParticleEffect::ParameterOverride>& GetParametersOverrides();
|
||||
API_PROPERTY() void SetParametersOverrides(const Array<ParticleEffect::ParameterOverride>& value);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -15,9 +15,11 @@ namespace
|
||||
{
|
||||
if (collection.IsEmpty())
|
||||
return;
|
||||
const auto c = collection.Get();
|
||||
for (int32 i = 0; i < collection.Count(); i++)
|
||||
{
|
||||
if (collection[i].First == collider || collection[i].Second == collider)
|
||||
const SimulationEventCallback::CollidersPair cc = c[i];
|
||||
if (cc.First == collider || cc.Second == collider)
|
||||
{
|
||||
collection.RemoveAt(i--);
|
||||
if (collection.IsEmpty())
|
||||
@@ -32,7 +34,8 @@ namespace
|
||||
return;
|
||||
for (auto i = collection.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Key.First == collider || i->Key.Second == collider)
|
||||
const SimulationEventCallback::CollidersPair cc = i->Key;
|
||||
if (cc.First == collider || cc.Second == collider)
|
||||
{
|
||||
collection.Remove(i);
|
||||
if (collection.IsEmpty())
|
||||
|
||||
@@ -86,27 +86,34 @@ NSString* AppleUtils::ToNSString(const char* string)
|
||||
|
||||
NSArray* AppleUtils::ParseArguments(NSString* argsString) {
|
||||
NSMutableArray *argsArray = [NSMutableArray array];
|
||||
NSScanner *scanner = [NSScanner scannerWithString:argsString];
|
||||
NSString *currentArg = nil;
|
||||
NSMutableString *currentArg = [NSMutableString string];
|
||||
BOOL insideQuotes = NO;
|
||||
|
||||
while (![scanner isAtEnd]) {
|
||||
if (insideQuotes) {
|
||||
[scanner scanUpToString:@"\"" intoString:¤tArg];
|
||||
[scanner scanString:@"\"" intoString:NULL];
|
||||
insideQuotes = NO;
|
||||
} else {
|
||||
[scanner scanUpToString:@" " intoString:¤tArg];
|
||||
[scanner scanString:@" " intoString:NULL];
|
||||
}
|
||||
for (NSInteger i = 0; i < argsString.length; ++i) {
|
||||
unichar c = [argsString characterAtIndex:i];
|
||||
|
||||
if ([currentArg isEqualToString:@"\""]) {
|
||||
insideQuotes = YES;
|
||||
} else if (currentArg) {
|
||||
[argsArray addObject:currentArg];
|
||||
if (c == '\"') {
|
||||
if (insideQuotes) {
|
||||
[argsArray addObject:[currentArg copy]];
|
||||
[currentArg setString:@""];
|
||||
insideQuotes = NO;
|
||||
} else {
|
||||
insideQuotes = YES;
|
||||
}
|
||||
} else if (c == ' ' && !insideQuotes) {
|
||||
if (currentArg.length > 0) {
|
||||
[argsArray addObject:[currentArg copy]];
|
||||
[currentArg setString:@""];
|
||||
}
|
||||
} else {
|
||||
[currentArg appendFormat:@"%C", c];
|
||||
}
|
||||
}
|
||||
|
||||
if (currentArg.length > 0) {
|
||||
[argsArray addObject:[currentArg copy]];
|
||||
}
|
||||
|
||||
return [argsArray copy];
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace FlaxEngine
|
||||
AllowDragAndDrop = true,
|
||||
IsRegularWindow = true,
|
||||
HasSizingFrame = true,
|
||||
ShowAfterFirstPaint = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(CreateWindowSettings);
|
||||
/// <summary>
|
||||
/// Enable/disable window auto-show after the first paint.
|
||||
/// </summary>
|
||||
API_FIELD() bool ShowAfterFirstPaint = false;
|
||||
API_FIELD() bool ShowAfterFirstPaint = true;
|
||||
|
||||
/// <summary>
|
||||
/// The custom data (platform dependant).
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
#endif
|
||||
#include "resource.h"
|
||||
|
||||
#define CLR_EXCEPTION 0xE0434352
|
||||
#define VCPP_EXCEPTION 0xE06D7363
|
||||
|
||||
const Char* WindowsPlatform::ApplicationWindowClass = TEXT("FlaxWindow");
|
||||
void* WindowsPlatform::Instance = nullptr;
|
||||
|
||||
@@ -272,6 +275,12 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
|
||||
LONG CALLBACK SehExceptionHandler(EXCEPTION_POINTERS* ep)
|
||||
{
|
||||
if (ep->ExceptionRecord->ExceptionCode == CLR_EXCEPTION)
|
||||
{
|
||||
// Pass CLR exceptions back to runtime
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
// Skip if engine already crashed
|
||||
if (Globals::FatalErrorOccurred)
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
|
||||
@@ -129,8 +129,15 @@ void ProfilerCPU::Thread::EndEvent()
|
||||
{
|
||||
const double time = Platform::GetTimeSeconds() * 1000.0;
|
||||
_depth--;
|
||||
Event& e = (Buffer.Last()--).Event();
|
||||
e.End = time;
|
||||
for (auto i = Buffer.Last(); i != Buffer.Begin(); --i)
|
||||
{
|
||||
Event& e = i.Event();
|
||||
if (e.End <= 0)
|
||||
{
|
||||
e.End = time;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ProfilerCPU::IsProfilingCurrentThread()
|
||||
@@ -205,7 +212,7 @@ int32 ProfilerCPU::BeginEvent(const char* name)
|
||||
|
||||
void ProfilerCPU::EndEvent(int32 index)
|
||||
{
|
||||
if (Enabled && Thread::Current)
|
||||
if (index != -1 && Thread::Current)
|
||||
Thread::Current->EndEvent(index);
|
||||
}
|
||||
|
||||
|
||||
@@ -121,15 +121,39 @@ public:
|
||||
EventBuffer* _buffer;
|
||||
int32 _index;
|
||||
|
||||
Iterator(EventBuffer* buffer, const int32 index)
|
||||
FORCE_INLINE Iterator(EventBuffer* buffer, const int32 index)
|
||||
: _buffer(buffer)
|
||||
, _index(index)
|
||||
{
|
||||
}
|
||||
|
||||
Iterator(const Iterator& i) = default;
|
||||
|
||||
public:
|
||||
FORCE_INLINE Iterator(const Iterator& other)
|
||||
: _buffer(other._buffer)
|
||||
, _index(other._index)
|
||||
{
|
||||
}
|
||||
|
||||
FORCE_INLINE Iterator(Iterator&& other) noexcept
|
||||
: _buffer(other._buffer)
|
||||
, _index(other._index)
|
||||
{
|
||||
}
|
||||
|
||||
FORCE_INLINE Iterator& operator=(Iterator&& other)
|
||||
{
|
||||
_buffer = other._buffer;
|
||||
_index = other._index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE Iterator& operator=(const Iterator& other)
|
||||
{
|
||||
_buffer = other._buffer;
|
||||
_index = other._index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE int32 Index() const
|
||||
{
|
||||
return _index;
|
||||
@@ -141,15 +165,13 @@ public:
|
||||
return _buffer->Get(_index);
|
||||
}
|
||||
|
||||
bool IsEnd() const
|
||||
FORCE_INLINE bool IsEnd() const
|
||||
{
|
||||
ASSERT_LOW_LAYER(_buffer);
|
||||
return _index == _buffer->_head;
|
||||
}
|
||||
|
||||
bool IsNotEnd() const
|
||||
FORCE_INLINE bool IsNotEnd() const
|
||||
{
|
||||
ASSERT_LOW_LAYER(_buffer);
|
||||
return _index != _buffer->_head;
|
||||
}
|
||||
|
||||
@@ -164,31 +186,27 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
Iterator& operator++()
|
||||
FORCE_INLINE Iterator& operator++()
|
||||
{
|
||||
ASSERT(_buffer);
|
||||
_index = (_index + 1) & _buffer->_capacityMask;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator operator++(int)
|
||||
FORCE_INLINE Iterator operator++(int)
|
||||
{
|
||||
ASSERT(_buffer);
|
||||
Iterator temp = *this;
|
||||
_index = (_index + 1) & _buffer->_capacityMask;
|
||||
return temp;
|
||||
}
|
||||
|
||||
Iterator& operator--()
|
||||
FORCE_INLINE Iterator& operator--()
|
||||
{
|
||||
ASSERT(_buffer);
|
||||
_index = (_index - 1) & _buffer->_capacityMask;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator operator--(int)
|
||||
FORCE_INLINE Iterator operator--(int)
|
||||
{
|
||||
ASSERT(_buffer);
|
||||
Iterator temp = *this;
|
||||
_index = (_index - 1) & _buffer->_capacityMask;
|
||||
return temp;
|
||||
|
||||
@@ -14,7 +14,7 @@ RenderStatsData RenderStatsData::Counter;
|
||||
int32 ProfilerGPU::_depth = 0;
|
||||
Array<GPUTimerQuery*> ProfilerGPU::_timerQueriesPool;
|
||||
Array<GPUTimerQuery*> ProfilerGPU::_timerQueriesFree;
|
||||
bool ProfilerGPU::Enabled = true;
|
||||
bool ProfilerGPU::Enabled = false;
|
||||
int32 ProfilerGPU::CurrentBuffer = 0;
|
||||
ProfilerGPU::EventBuffer ProfilerGPU::Buffers[PROFILER_GPU_EVENTS_FRAMES];
|
||||
|
||||
|
||||
@@ -3,14 +3,17 @@
|
||||
#if COMPILE_WITH_PROFILER
|
||||
|
||||
#include "ProfilingTools.h"
|
||||
#include "Engine/Core/Types/Pair.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Networking/NetworkInternal.h"
|
||||
|
||||
ProfilingTools::MainStats ProfilingTools::Stats;
|
||||
Array<ProfilingTools::ThreadStats, InlinedAllocation<64>> ProfilingTools::EventsCPU;
|
||||
Array<ProfilerGPU::Event> ProfilingTools::EventsGPU;
|
||||
Array<ProfilingTools::NetworkEventStat> ProfilingTools::EventsNetwork;
|
||||
|
||||
class ProfilingToolsService : public EngineService
|
||||
{
|
||||
@@ -120,6 +123,40 @@ void ProfilingToolsService::Update()
|
||||
frame.Extract(ProfilingTools::EventsGPU);
|
||||
}
|
||||
|
||||
// Get the last events from networking runtime
|
||||
{
|
||||
auto& networkEvents = ProfilingTools::EventsNetwork;
|
||||
networkEvents.Resize(NetworkInternal::ProfilerEvents.Count());
|
||||
int32 i = 0;
|
||||
for (const auto& e : NetworkInternal::ProfilerEvents)
|
||||
{
|
||||
const auto& src = e.Value;
|
||||
auto& dst = networkEvents[i++];
|
||||
dst.Count = src.Count;
|
||||
dst.DataSize = src.DataSize;
|
||||
dst.MessageSize = src.MessageSize;
|
||||
dst.Receivers = src.Receivers;
|
||||
const StringAnsiView& typeName = e.Key.First.GetType().Fullname;
|
||||
uint64 len = Math::Min<uint64>(typeName.Length(), ARRAY_COUNT(dst.Name) - 10);
|
||||
Platform::MemoryCopy(dst.Name, typeName.Get(), len);
|
||||
const StringAnsiView& name = e.Key.Second;
|
||||
if (name.HasChars())
|
||||
{
|
||||
uint64 pos = len;
|
||||
dst.Name[pos++] = ':';
|
||||
dst.Name[pos++] = ':';
|
||||
len = Math::Min<uint64>(name.Length(), ARRAY_COUNT(dst.Name) - pos - 1);
|
||||
Platform::MemoryCopy(dst.Name + pos, name.Get(), len);
|
||||
dst.Name[pos + len] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst.Name[len] = 0;
|
||||
}
|
||||
}
|
||||
NetworkInternal::ProfilerEvents.Clear();
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Print CPU events to the log
|
||||
{
|
||||
@@ -173,6 +210,19 @@ void ProfilingToolsService::Dispose()
|
||||
ProfilingTools::EventsCPU.Clear();
|
||||
ProfilingTools::EventsCPU.SetCapacity(0);
|
||||
ProfilingTools::EventsGPU.SetCapacity(0);
|
||||
ProfilingTools::EventsNetwork.SetCapacity(0);
|
||||
}
|
||||
|
||||
bool ProfilingTools::GetEnabled()
|
||||
{
|
||||
return ProfilerCPU::Enabled && ProfilerGPU::Enabled;
|
||||
}
|
||||
|
||||
void ProfilingTools::SetEnabled(bool enabled)
|
||||
{
|
||||
ProfilerCPU::Enabled = enabled;
|
||||
ProfilerGPU::Enabled = enabled;
|
||||
NetworkInternal::EnableProfiling = enabled;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -105,7 +105,35 @@ public:
|
||||
API_FIELD() Array<ProfilerCPU::Event> Events;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The network stat.
|
||||
/// </summary>
|
||||
API_STRUCT(NoDefault) struct NetworkEventStat
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkEventStat);
|
||||
|
||||
// Amount of occurrences.
|
||||
API_FIELD() uint16 Count;
|
||||
// Transferred data size (in bytes).
|
||||
API_FIELD() uint16 DataSize;
|
||||
// Transferred message (data+header) size (in bytes).
|
||||
API_FIELD() uint16 MessageSize;
|
||||
// Amount of peers that will receive this message.
|
||||
API_FIELD() uint16 Receivers;
|
||||
API_FIELD(Private, NoArray) byte Name[120];
|
||||
};
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Controls the engine profiler (CPU, GPU, etc.) usage.
|
||||
/// </summary>
|
||||
API_PROPERTY() static bool GetEnabled();
|
||||
|
||||
/// <summary>
|
||||
/// Controls the engine profiler (CPU, GPU, etc.) usage.
|
||||
/// </summary>
|
||||
API_PROPERTY() static void SetEnabled(bool enabled);
|
||||
|
||||
/// <summary>
|
||||
/// The current collected main stats by the profiler from the local session. Updated every frame.
|
||||
/// </summary>
|
||||
@@ -120,6 +148,11 @@ public:
|
||||
/// The GPU rendering profiler events.
|
||||
/// </summary>
|
||||
API_FIELD(ReadOnly) static Array<ProfilerGPU::Event> EventsGPU;
|
||||
|
||||
/// <summary>
|
||||
/// The networking profiler events.
|
||||
/// </summary>
|
||||
API_FIELD(ReadOnly) static Array<NetworkEventStat> EventsNetwork;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,8 @@ using System;
|
||||
namespace FlaxEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that a field or a property of a serializable class should be serialized. This class cannot be inherited.
|
||||
/// Indicates that a field or a property of a serializable class should be serialized.
|
||||
/// The <see cref="FlaxEngine.ShowInEditorAttribute"/> attribute is required to show hidden fields in the editor.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class SerializeAttribute : Attribute
|
||||
|
||||
@@ -1237,8 +1237,12 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
|
||||
return true;
|
||||
}
|
||||
|
||||
#if USE_NETCORE
|
||||
mInstance = instanceObject;
|
||||
#else
|
||||
// For value-types instance is the actual boxed object data, not te object itself
|
||||
mInstance = instanceObjectClass->IsValueType() ? MCore::Object::Unbox(instanceObject) : instanceObject;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Marshal parameters
|
||||
|
||||
@@ -51,6 +51,15 @@ public:
|
||||
/// <param name="name">The assembly name.</param>
|
||||
MAssembly(MDomain* domain, const StringAnsiView& name);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MAssembly"/> class.
|
||||
/// </summary>
|
||||
/// <param name="domain">The assembly domain.</param>
|
||||
/// <param name="name">The assembly name.</param>
|
||||
/// <param name="fullname">The assembly full name.</param>
|
||||
/// <param name="handle">The managed handle of the assembly.</param>
|
||||
MAssembly(MDomain* domain, const StringAnsiView& name, const StringAnsiView& fullname, void* handle);
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="MAssembly"/> class.
|
||||
/// </summary>
|
||||
|
||||
@@ -48,6 +48,18 @@ MAssembly::MAssembly(MDomain* domain, const StringAnsiView& name)
|
||||
{
|
||||
}
|
||||
|
||||
MAssembly::MAssembly(MDomain* domain, const StringAnsiView& name, const StringAnsiView& fullname, void* handle)
|
||||
: _domain(domain)
|
||||
, _isLoaded(false)
|
||||
, _isLoading(false)
|
||||
, _hasCachedClasses(false)
|
||||
, _reloadCount(0)
|
||||
, _name(name)
|
||||
, _fullname(fullname)
|
||||
, _handle(handle)
|
||||
{
|
||||
}
|
||||
|
||||
MAssembly::~MAssembly()
|
||||
{
|
||||
Unload();
|
||||
|
||||
@@ -190,4 +190,13 @@ public:
|
||||
static MClass* Double;
|
||||
static MClass* String;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Utilities for ScriptingObject management.
|
||||
/// </summary>
|
||||
struct FLAXENGINE_API ScriptingObject
|
||||
{
|
||||
static void SetInternalValues(MClass* klass, MObject* object, void* unmanagedPtr, const Guid* id);
|
||||
static MObject* CreateScriptingObject(MClass* klass, void* unmanagedPtr, const Guid* id);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ protected:
|
||||
#elif USE_NETCORE
|
||||
void* _handle;
|
||||
void* _type;
|
||||
int32 _fieldOffset;
|
||||
#endif
|
||||
|
||||
MClass* _parentClass;
|
||||
@@ -35,7 +36,7 @@ public:
|
||||
#if USE_MONO
|
||||
explicit MField(MonoClassField* monoField, const char* name, MClass* parentClass);
|
||||
#elif USE_NETCORE
|
||||
MField(MClass* parentClass, void* handle, const char* name, void* type, MFieldAttributes attributes);
|
||||
MField(MClass* parentClass, void* handle, const char* name, void* type, int fieldOffset, MFieldAttributes attributes);
|
||||
#endif
|
||||
|
||||
public:
|
||||
@@ -102,6 +103,16 @@ public:
|
||||
/// <param name="result">The return value of undefined type.</param>
|
||||
void GetValue(MObject* instance, void* result) const;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves value currently set in the field on the specified object instance. If field is static object instance can be null.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Value will be a pointer.
|
||||
/// </remarks>
|
||||
/// <param name="instance">The object of given type to get value from.</param>
|
||||
/// <param name="result">The return value of undefined type.</param>
|
||||
void GetValueReference(MObject* instance, void* result) const;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves value currently set in the field on the specified object instance. If field is static object instance can be null. If returned value is a value type it will be boxed.
|
||||
/// </summary>
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace FlaxEngine
|
||||
// Construct missing native object if managed objects gets created in managed world
|
||||
if (__unmanagedPtr == IntPtr.Zero)
|
||||
{
|
||||
Internal_ManagedInstanceCreated(this);
|
||||
Internal_ManagedInstanceCreated(this, FlaxEngine.Interop.NativeInterop.GetTypeHolder(GetType()).managedClassPointer);
|
||||
if (__unmanagedPtr == IntPtr.Zero)
|
||||
throw new Exception($"Failed to create native instance for object of type {GetType().FullName} (assembly: {GetType().Assembly.FullName}).");
|
||||
}
|
||||
@@ -320,7 +320,7 @@ namespace FlaxEngine
|
||||
internal static partial Object Internal_Create2(string typeName);
|
||||
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ManagedInstanceCreated", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
|
||||
internal static partial void Internal_ManagedInstanceCreated(Object managedInstance);
|
||||
internal static partial void Internal_ManagedInstanceCreated(Object managedInstance, IntPtr theKlass);
|
||||
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ManagedInstanceDeleted", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
|
||||
internal static partial void Internal_ManagedInstanceDeleted(IntPtr nativeInstance);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Scripting/Internal/InternalCalls.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MCore.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MAssembly.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||
@@ -214,6 +215,7 @@ void* GetCustomAttribute(const MClass* klass, const MClass* attributeClass);
|
||||
struct NativeClassDefinitions
|
||||
{
|
||||
void* typeHandle;
|
||||
MClass* nativePointer;
|
||||
const char* name;
|
||||
const char* fullname;
|
||||
const char* namespace_;
|
||||
@@ -233,6 +235,7 @@ struct NativeFieldDefinitions
|
||||
const char* name;
|
||||
void* fieldHandle;
|
||||
void* fieldType;
|
||||
int fieldOffset;
|
||||
MFieldAttributes fieldAttributes;
|
||||
};
|
||||
|
||||
@@ -341,8 +344,8 @@ void MCore::Object::Init(MObject* obj)
|
||||
MClass* MCore::Object::GetClass(MObject* obj)
|
||||
{
|
||||
ASSERT(obj);
|
||||
MType* typeHandle = GetObjectType(obj);
|
||||
return GetOrCreateClass(typeHandle);
|
||||
static void* GetObjectClassPtr = GetStaticMethodPointer(TEXT("GetObjectClass"));
|
||||
return (MClass*)CallStaticMethod<MClass*, void*>(GetObjectClassPtr, obj);
|
||||
}
|
||||
|
||||
MString* MCore::Object::ToString(MObject* obj)
|
||||
@@ -574,8 +577,7 @@ MObject* MCore::Exception::GetNotSupported(const char* msg)
|
||||
MClass* MCore::Type::GetClass(MType* type)
|
||||
{
|
||||
static void* GetTypeClassPtr = GetStaticMethodPointer(TEXT("GetTypeClass"));
|
||||
type = (MType*)CallStaticMethod<void*, void*>(GetTypeClassPtr, type);
|
||||
return GetOrCreateClass(type);
|
||||
return CallStaticMethod<MClass*, void*>(GetTypeClassPtr, type);
|
||||
}
|
||||
|
||||
MType* MCore::Type::GetElementType(MType* type)
|
||||
@@ -612,6 +614,18 @@ bool MCore::Type::IsReference(MType* type)
|
||||
return CallStaticMethod<bool, void*>(GetTypeIsReferencePtr, type);
|
||||
}
|
||||
|
||||
void MCore::ScriptingObject::SetInternalValues(MClass* klass, MObject* object, void* unmanagedPtr, const Guid* id)
|
||||
{
|
||||
static void* ScriptingObjectSetInternalValuesPtr = GetStaticMethodPointer(TEXT("ScriptingObjectSetInternalValues"));
|
||||
CallStaticMethod<void, MObject*, void*, const Guid*>(ScriptingObjectSetInternalValuesPtr, object, unmanagedPtr, id);
|
||||
}
|
||||
|
||||
MObject* MCore::ScriptingObject::CreateScriptingObject(MClass* klass, void* unmanagedPtr, const Guid* id)
|
||||
{
|
||||
static void* ScriptingObjectSetInternalValuesPtr = GetStaticMethodPointer(TEXT("ScriptingObjectCreate"));
|
||||
return CallStaticMethod<MObject*, void*, void*, const Guid*>(ScriptingObjectSetInternalValuesPtr, klass->_handle, unmanagedPtr, id);
|
||||
}
|
||||
|
||||
const MAssembly::ClassesDictionary& MAssembly::GetClasses() const
|
||||
{
|
||||
if (_hasCachedClasses || !IsLoaded())
|
||||
@@ -640,10 +654,16 @@ const MAssembly::ClassesDictionary& MAssembly::GetClasses() const
|
||||
MClass* klass = New<MClass>(this, managedClass.typeHandle, managedClass.name, managedClass.fullname, managedClass.namespace_, managedClass.typeAttributes);
|
||||
_classes.Add(klass->GetFullName(), klass);
|
||||
|
||||
managedClass.nativePointer = klass;
|
||||
|
||||
MCore::GC::FreeMemory((void*)managedClasses[i].name);
|
||||
MCore::GC::FreeMemory((void*)managedClasses[i].fullname);
|
||||
MCore::GC::FreeMemory((void*)managedClasses[i].namespace_);
|
||||
}
|
||||
|
||||
static void* RegisterManagedClassNativePointersPtr = GetStaticMethodPointer(TEXT("RegisterManagedClassNativePointers"));
|
||||
CallStaticMethod<void, NativeClassDefinitions**, int>(RegisterManagedClassNativePointersPtr, &managedClasses, classCount);
|
||||
|
||||
MCore::GC::FreeMemory(managedClasses);
|
||||
|
||||
const auto endTime = DateTime::NowUTC();
|
||||
@@ -658,6 +678,39 @@ const MAssembly::ClassesDictionary& MAssembly::GetClasses() const
|
||||
return _classes;
|
||||
}
|
||||
|
||||
void GetAssemblyName(void* assemblyHandle, StringAnsi& name, StringAnsi& fullname)
|
||||
{
|
||||
static void* GetAssemblyNamePtr = GetStaticMethodPointer(TEXT("GetAssemblyName"));
|
||||
const char* name_;
|
||||
const char* fullname_;
|
||||
CallStaticMethod<void, void*, const char**, const char**>(GetAssemblyNamePtr, assemblyHandle, &name_, &fullname_);
|
||||
name = name_;
|
||||
fullname = fullname_;
|
||||
MCore::GC::FreeMemory((void*)name_);
|
||||
MCore::GC::FreeMemory((void*)fullname_);
|
||||
}
|
||||
|
||||
DEFINE_INTERNAL_CALL(void) NativeInterop_CreateClass(NativeClassDefinitions* managedClass, void* assemblyHandle)
|
||||
{
|
||||
MAssembly* assembly = GetAssembly(assemblyHandle);
|
||||
if (assembly == nullptr)
|
||||
{
|
||||
StringAnsi assemblyName;
|
||||
StringAnsi assemblyFullName;
|
||||
GetAssemblyName(assemblyHandle, assemblyName, assemblyFullName);
|
||||
|
||||
assembly = New<MAssembly>(nullptr, assemblyName, assemblyFullName, assemblyHandle);
|
||||
CachedAssemblyHandles.Add(assemblyHandle, assembly);
|
||||
}
|
||||
|
||||
MClass* klass = New<MClass>(assembly, managedClass->typeHandle, managedClass->name, managedClass->fullname, managedClass->namespace_, managedClass->typeAttributes);
|
||||
if (assembly != nullptr)
|
||||
{
|
||||
const_cast<MAssembly::ClassesDictionary&>(assembly->GetClasses()).Add(klass->GetFullName(), klass);
|
||||
}
|
||||
managedClass->nativePointer = klass;
|
||||
}
|
||||
|
||||
bool MAssembly::LoadCorlib()
|
||||
{
|
||||
if (IsLoaded())
|
||||
@@ -677,14 +730,9 @@ bool MAssembly::LoadCorlib()
|
||||
|
||||
// Load
|
||||
{
|
||||
const char* name;
|
||||
const char* fullname;
|
||||
static void* GetAssemblyByNamePtr = GetStaticMethodPointer(TEXT("GetAssemblyByName"));
|
||||
_handle = CallStaticMethod<void*, const char*, const char**, const char**>(GetAssemblyByNamePtr, "System.Private.CoreLib", &name, &fullname);
|
||||
_name = name;
|
||||
_fullname = fullname;
|
||||
MCore::GC::FreeMemory((void*)name);
|
||||
MCore::GC::FreeMemory((void*)fullname);
|
||||
_handle = CallStaticMethod<void*, const char*>(GetAssemblyByNamePtr, "System.Private.CoreLib");
|
||||
GetAssemblyName(_handle, _name, _fullname);
|
||||
}
|
||||
if (_handle == nullptr)
|
||||
{
|
||||
@@ -703,20 +751,14 @@ bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePa
|
||||
{
|
||||
// TODO: Use new hostfxr delegate load_assembly_bytes? (.NET 8+)
|
||||
// Open .Net assembly
|
||||
const StringAnsi assemblyPathAnsi = assemblyPath.ToStringAnsi();
|
||||
const char* name;
|
||||
const char* fullname;
|
||||
static void* LoadAssemblyImagePtr = GetStaticMethodPointer(TEXT("LoadAssemblyImage"));
|
||||
_handle = CallStaticMethod<void*, const char*, const char**, const char**>(LoadAssemblyImagePtr, assemblyPathAnsi.Get(), &name, &fullname);
|
||||
_name = name;
|
||||
_fullname = fullname;
|
||||
MCore::GC::FreeMemory((void*)name);
|
||||
MCore::GC::FreeMemory((void*)fullname);
|
||||
_handle = CallStaticMethod<void*, const Char*>(LoadAssemblyImagePtr, assemblyPath.Get());
|
||||
if (_handle == nullptr)
|
||||
{
|
||||
Log::CLRInnerException(TEXT(".NET assembly image is invalid at ") + assemblyPath);
|
||||
return true;
|
||||
}
|
||||
GetAssemblyName(_handle, _name, _fullname);
|
||||
CachedAssemblyHandles.Add(_handle, this);
|
||||
|
||||
// Provide new path of hot-reloaded native library path for managed DllImport
|
||||
@@ -846,8 +888,7 @@ MType* MClass::GetType() const
|
||||
MClass* MClass::GetBaseClass() const
|
||||
{
|
||||
static void* GetClassParentPtr = GetStaticMethodPointer(TEXT("GetClassParent"));
|
||||
MType* parentTypeHandle = CallStaticMethod<MType*, void*>(GetClassParentPtr, _handle);
|
||||
return GetOrCreateClass(parentTypeHandle);
|
||||
return CallStaticMethod<MClass*, void*>(GetClassParentPtr, _handle);
|
||||
}
|
||||
|
||||
bool MClass::IsSubClassOf(const MClass* klass, bool checkInterfaces) const
|
||||
@@ -882,8 +923,7 @@ uint32 MClass::GetInstanceSize() const
|
||||
MClass* MClass::GetElementClass() const
|
||||
{
|
||||
static void* GetElementClassPtr = GetStaticMethodPointer(TEXT("GetElementClass"));
|
||||
MType* elementTypeHandle = CallStaticMethod<MType*, void*>(GetElementClassPtr, _handle);
|
||||
return GetOrCreateClass(elementTypeHandle);
|
||||
return CallStaticMethod<MClass*, void*>(GetElementClassPtr, _handle);
|
||||
}
|
||||
|
||||
MMethod* MClass::GetMethod(const char* name, int32 numParams) const
|
||||
@@ -942,7 +982,7 @@ const Array<MField*>& MClass::GetFields() const
|
||||
for (int32 i = 0; i < numFields; i++)
|
||||
{
|
||||
NativeFieldDefinitions& definition = fields[i];
|
||||
MField* field = New<MField>(const_cast<MClass*>(this), definition.fieldHandle, definition.name, definition.fieldType, definition.fieldAttributes);
|
||||
MField* field = New<MField>(const_cast<MClass*>(this), definition.fieldHandle, definition.name, definition.fieldType, definition.fieldOffset, definition.fieldAttributes);
|
||||
_fields.Add(field);
|
||||
MCore::GC::FreeMemory((void*)definition.name);
|
||||
}
|
||||
@@ -1023,7 +1063,7 @@ bool MClass::HasAttribute(const MClass* monoClass) const
|
||||
|
||||
bool MClass::HasAttribute() const
|
||||
{
|
||||
return GetCustomAttribute(this, nullptr) != nullptr;
|
||||
return !GetAttributes().IsEmpty();
|
||||
}
|
||||
|
||||
MObject* MClass::GetAttribute(const MClass* monoClass) const
|
||||
@@ -1133,11 +1173,12 @@ MException::~MException()
|
||||
Delete(InnerException);
|
||||
}
|
||||
|
||||
MField::MField(MClass* parentClass, void* handle, const char* name, void* type, MFieldAttributes attributes)
|
||||
MField::MField(MClass* parentClass, void* handle, const char* name, void* type, int fieldOffset, MFieldAttributes attributes)
|
||||
: _handle(handle)
|
||||
, _type(type)
|
||||
, _parentClass(parentClass)
|
||||
, _name(name)
|
||||
, _fieldOffset(fieldOffset)
|
||||
, _hasCachedAttributes(false)
|
||||
{
|
||||
switch (attributes & MFieldAttributes::FieldAccessMask)
|
||||
@@ -1173,8 +1214,7 @@ MType* MField::GetType() const
|
||||
|
||||
int32 MField::GetOffset() const
|
||||
{
|
||||
static void* FieldGetOffsetPtr = GetStaticMethodPointer(TEXT("FieldGetOffset"));
|
||||
return CallStaticMethod<int32, void*>(FieldGetOffsetPtr, _handle);
|
||||
return _fieldOffset;
|
||||
}
|
||||
|
||||
void MField::GetValue(MObject* instance, void* result) const
|
||||
@@ -1183,6 +1223,12 @@ void MField::GetValue(MObject* instance, void* result) const
|
||||
CallStaticMethod<void, void*, void*, void*>(FieldGetValuePtr, instance, _handle, result);
|
||||
}
|
||||
|
||||
void MField::GetValueReference(MObject* instance, void* result) const
|
||||
{
|
||||
static void* FieldGetValueReferencePtr = GetStaticMethodPointer(TEXT("FieldGetValueReferenceWithOffset"));
|
||||
CallStaticMethod<void, void*, int, void*>(FieldGetValueReferencePtr, instance, _fieldOffset, result);
|
||||
}
|
||||
|
||||
MObject* MField::GetValueBoxed(MObject* instance) const
|
||||
{
|
||||
static void* FieldGetValueBoxedPtr = GetStaticMethodPointer(TEXT("FieldGetValueBoxed"));
|
||||
@@ -1515,8 +1561,7 @@ void* GetCustomAttribute(const MClass* klass, const MClass* attributeClass)
|
||||
const Array<MObject*>& attributes = klass->GetAttributes();
|
||||
for (MObject* attr : attributes)
|
||||
{
|
||||
MType* typeHandle = GetObjectType(attr);
|
||||
MClass* attrClass = GetOrCreateClass(typeHandle);
|
||||
MClass* attrClass = MCore::Object::GetClass(attr);
|
||||
if (attrClass == attributeClass)
|
||||
return attr;
|
||||
}
|
||||
|
||||
@@ -2127,6 +2127,44 @@ const Array<MObject*>& MProperty::GetAttributes() const
|
||||
return _attributes;
|
||||
}
|
||||
|
||||
void MCore::ScriptingObject::SetInternalValues(MClass* klass, MObject* object, void* unmanagedPtr, const Guid* id)
|
||||
{
|
||||
// Set handle to unmanaged object
|
||||
const MField* monoUnmanagedPtrField = klass->GetField("__unmanagedPtr");
|
||||
if (monoUnmanagedPtrField)
|
||||
{
|
||||
const void* param = unmanagedPtr;
|
||||
monoUnmanagedPtrField->SetValue(managedInstance, ¶m);
|
||||
}
|
||||
if (id != nullptr)
|
||||
{
|
||||
// Set object id
|
||||
const MField* monoIdField = klass->GetField("__internalId");
|
||||
if (monoIdField)
|
||||
{
|
||||
monoIdField->SetValue(managedInstance, (void*)id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MObject* MCore::ScriptingObject::CreateScriptingObject(MClass* klass, void* unmanagedPtr, const Guid* id)
|
||||
{
|
||||
// Ensure to have managed domain attached (this can be called from custom native thread, eg. content loader)
|
||||
MCore::Thread::Attach();
|
||||
|
||||
// Allocate managed instance
|
||||
MObject* managedInstance = MCore::Object::New(klass);
|
||||
if (managedInstance)
|
||||
{
|
||||
// Set unmanaged object handle and id
|
||||
MCore::ScriptingObject::SetInternalValues(klass, managedInstance, unmanagedPtr, _id);
|
||||
|
||||
// Initialize managed instance (calls constructor)
|
||||
MCore::Object::Init(managedInstance);
|
||||
}
|
||||
return managedInstance;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if USE_MONO && PLATFORM_WIN32 && !USE_MONO_DYNAMIC_LIB
|
||||
|
||||
@@ -565,4 +565,13 @@ const Array<MObject*>& MProperty::GetAttributes() const
|
||||
return _attributes;
|
||||
}
|
||||
|
||||
void MCore::ScriptingObject::SetInternalValues(MClass* klass, MObject* object, void* unmanagedPtr, const Guid* id)
|
||||
{
|
||||
}
|
||||
|
||||
MObject* MCore::ScriptingObject::CreateScriptingObject(MClass* klass, void* unmanagedPtr, const Guid* id)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace
|
||||
MMethod* _method_LateFixedUpdate = nullptr;
|
||||
MMethod* _method_Draw = nullptr;
|
||||
MMethod* _method_Exit = nullptr;
|
||||
Array<BinaryModule*, InlinedAllocation<64>> _nonNativeModules;
|
||||
Dictionary<StringAnsi, BinaryModule*, InlinedAllocation<64>> _nonNativeModules;
|
||||
#if USE_EDITOR
|
||||
bool LastBinariesLoadTriggeredCompilation = false;
|
||||
#endif
|
||||
@@ -334,6 +334,8 @@ bool Scripting::LoadBinaryModules(const String& path, const String& projectFolde
|
||||
|
||||
// Check if that module has been already registered
|
||||
BinaryModule* module = BinaryModule::GetModule(nameAnsi);
|
||||
if (!module)
|
||||
_nonNativeModules.TryGet(nameAnsi, module);
|
||||
if (!module)
|
||||
{
|
||||
// C++
|
||||
@@ -403,7 +405,7 @@ bool Scripting::LoadBinaryModules(const String& path, const String& projectFolde
|
||||
{
|
||||
// Create module if native library is not used
|
||||
module = New<ManagedBinaryModule>(nameAnsi);
|
||||
_nonNativeModules.Add(module);
|
||||
_nonNativeModules.Add(nameAnsi, module);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -135,8 +135,13 @@ namespace FlaxEngine
|
||||
{
|
||||
if (e.ExceptionObject is Exception exception)
|
||||
{
|
||||
Debug.LogError("Unhandled Exception: " + exception.Message);
|
||||
Debug.LogException(exception);
|
||||
if (e.IsTerminating && !System.Diagnostics.Debugger.IsAttached)
|
||||
Platform.Fatal($"Unhandled Exception: {exception}");
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Unhandled Exception: {exception.Message}");
|
||||
Debug.LogException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,6 +282,12 @@ namespace FlaxEngine
|
||||
TextBoxBackgroundSelected = Color.FromBgra(0xFF3F3F46),
|
||||
CollectionBackgroundColor = Color.FromBgra(0x14CCCCCC),
|
||||
SharedTooltip = new Tooltip(),
|
||||
Statusbar = new Style.StatusbarStyle()
|
||||
{
|
||||
PlayMode = Color.FromBgra(0xFF2F9135),
|
||||
Failed = Color.FromBgra(0xFF9C2424),
|
||||
Loading = Color.FromBgra(0xFF2D2D30)
|
||||
}
|
||||
};
|
||||
style.DragWindow = style.BackgroundSelected * 0.7f;
|
||||
|
||||
|
||||
@@ -180,10 +180,14 @@ ScriptingObject* ScriptingObject::ToNative(MObject* obj)
|
||||
#if USE_CSHARP
|
||||
if (obj)
|
||||
{
|
||||
// TODO: cache the field offset from object and read directly from object pointer
|
||||
#if USE_MONO
|
||||
const auto ptrField = MCore::Object::GetClass(obj)->GetField(ScriptingObject_unmanagedPtr);
|
||||
CHECK_RETURN(ptrField, nullptr);
|
||||
ptrField->GetValue(obj, &ptr);
|
||||
#else
|
||||
static const MField* ptrField = MCore::Object::GetClass(obj)->GetField(ScriptingObject_unmanagedPtr);
|
||||
ptrField->GetValueReference(obj, &ptr);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
return ptr;
|
||||
@@ -274,12 +278,7 @@ bool ScriptingObject::CreateManaged()
|
||||
if (const auto monoClass = GetClass())
|
||||
{
|
||||
// Reset managed to unmanaged pointer
|
||||
const MField* monoUnmanagedPtrField = monoClass->GetField(ScriptingObject_unmanagedPtr);
|
||||
if (monoUnmanagedPtrField)
|
||||
{
|
||||
void* param = nullptr;
|
||||
monoUnmanagedPtrField->SetValue(managedInstance, ¶m);
|
||||
}
|
||||
MCore::ScriptingObject::SetInternalValues(monoClass, managedInstance, nullptr, nullptr);
|
||||
}
|
||||
MCore::GCHandle::Free(handle);
|
||||
return true;
|
||||
@@ -305,34 +304,12 @@ MObject* ScriptingObject::CreateManagedInternal()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure to have managed domain attached (this can be called from custom native thread, eg. content loader)
|
||||
MCore::Thread::Attach();
|
||||
|
||||
// Allocate managed instance
|
||||
MObject* managedInstance = MCore::Object::New(monoClass);
|
||||
MObject* managedInstance = MCore::ScriptingObject::CreateScriptingObject(monoClass, this, &_id);
|
||||
if (managedInstance == nullptr)
|
||||
{
|
||||
LOG(Warning, "Failed to create new instance of the object of type {0}", String(monoClass->GetFullName()));
|
||||
}
|
||||
|
||||
// Set handle to unmanaged object
|
||||
const MField* monoUnmanagedPtrField = monoClass->GetField(ScriptingObject_unmanagedPtr);
|
||||
if (monoUnmanagedPtrField)
|
||||
{
|
||||
const void* value = this;
|
||||
monoUnmanagedPtrField->SetValue(managedInstance, &value);
|
||||
}
|
||||
|
||||
// Set object id
|
||||
const MField* monoIdField = monoClass->GetField(ScriptingObject_id);
|
||||
if (monoIdField)
|
||||
{
|
||||
monoIdField->SetValue(managedInstance, (void*)&_id);
|
||||
}
|
||||
|
||||
// Initialize managed instance (calls constructor)
|
||||
MCore::Object::Init(managedInstance);
|
||||
|
||||
return managedInstance;
|
||||
}
|
||||
|
||||
@@ -349,12 +326,7 @@ void ScriptingObject::DestroyManaged()
|
||||
{
|
||||
if (const auto monoClass = GetClass())
|
||||
{
|
||||
const MField* monoUnmanagedPtrField = monoClass->GetField(ScriptingObject_unmanagedPtr);
|
||||
if (monoUnmanagedPtrField)
|
||||
{
|
||||
void* param = nullptr;
|
||||
monoUnmanagedPtrField->SetValue(managedInstance, ¶m);
|
||||
}
|
||||
MCore::ScriptingObject::SetInternalValues(monoClass, managedInstance, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,12 +450,7 @@ bool ManagedScriptingObject::CreateManaged()
|
||||
if (const auto monoClass = GetClass())
|
||||
{
|
||||
// Reset managed to unmanaged pointer
|
||||
const MField* monoUnmanagedPtrField = monoClass->GetField(ScriptingObject_unmanagedPtr);
|
||||
if (monoUnmanagedPtrField)
|
||||
{
|
||||
void* param = nullptr;
|
||||
monoUnmanagedPtrField->SetValue(managedInstance, ¶m);
|
||||
}
|
||||
MCore::ScriptingObject::SetInternalValues(monoClass, managedInstance, nullptr, nullptr);
|
||||
}
|
||||
MCore::GCHandle::Free(handle);
|
||||
return true;
|
||||
@@ -605,10 +572,8 @@ DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_Create2(MString* typeNameObj)
|
||||
return managedInstance;
|
||||
}
|
||||
|
||||
DEFINE_INTERNAL_CALL(void) ObjectInternal_ManagedInstanceCreated(MObject* managedInstance)
|
||||
DEFINE_INTERNAL_CALL(void) ObjectInternal_ManagedInstanceCreated(MObject* managedInstance, MClass* typeClass)
|
||||
{
|
||||
MClass* typeClass = MCore::Object::GetClass(managedInstance);
|
||||
|
||||
// Get the assembly with that class
|
||||
auto module = ManagedBinaryModule::FindModule(typeClass);
|
||||
if (module == nullptr)
|
||||
@@ -645,22 +610,8 @@ DEFINE_INTERNAL_CALL(void) ObjectInternal_ManagedInstanceCreated(MObject* manage
|
||||
}
|
||||
|
||||
MClass* monoClass = obj->GetClass();
|
||||
|
||||
// Set handle to unmanaged object
|
||||
const MField* monoUnmanagedPtrField = monoClass->GetField(ScriptingObject_unmanagedPtr);
|
||||
if (monoUnmanagedPtrField)
|
||||
{
|
||||
const void* value = obj;
|
||||
monoUnmanagedPtrField->SetValue(managedInstance, &value);
|
||||
}
|
||||
|
||||
// Set object id
|
||||
const MField* monoIdField = monoClass->GetField(ScriptingObject_id);
|
||||
if (monoIdField)
|
||||
{
|
||||
const Guid id = obj->GetID();
|
||||
monoIdField->SetValue(managedInstance, (void*)&id);
|
||||
}
|
||||
const Guid id = obj->GetID();
|
||||
MCore::ScriptingObject::SetInternalValues(monoClass, managedInstance, obj, &id);
|
||||
|
||||
// Register object
|
||||
if (!obj->IsRegistered())
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -566,12 +566,17 @@ bool ImportMaterials(ImportedModelData& result, AssimpImporterData& data, String
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsMeshInvalid(const aiMesh* aMesh)
|
||||
{
|
||||
return aMesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE || aMesh->mNumVertices == 0 || aMesh->mNumFaces == 0 || aMesh->mFaces[0].mNumIndices != 3;
|
||||
}
|
||||
|
||||
bool ImportMesh(int32 i, ImportedModelData& result, AssimpImporterData& data, String& errorMsg)
|
||||
{
|
||||
const auto aMesh = data.Scene->mMeshes[i];
|
||||
|
||||
// Skip invalid meshes
|
||||
if (aMesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE || aMesh->mNumVertices == 0 || aMesh->mNumFaces == 0 || aMesh->mFaces[0].mNumIndices != 3)
|
||||
if (IsMeshInvalid(aMesh))
|
||||
return false;
|
||||
|
||||
// Skip unused meshes
|
||||
|
||||
@@ -923,8 +923,6 @@ bool ImportMesh(int32 index, ImportedModelData& result, OpenFbxImporterData& dat
|
||||
const auto aMesh = data.Scene->getMesh(index);
|
||||
const auto aGeometry = aMesh->getGeometry();
|
||||
const auto trianglesCount = aGeometry->getVertexCount() / 3;
|
||||
|
||||
// Skip invalid meshes
|
||||
if (IsMeshInvalid(aMesh))
|
||||
return false;
|
||||
|
||||
@@ -1272,6 +1270,22 @@ bool ModelTool::ImportDataOpenFBX(const char* path, ImportedModelData& data, Opt
|
||||
const auto meshIndex = Math::Clamp<int32>(options.ObjectIndex, 0, meshCount - 1);
|
||||
if (ImportMesh(meshIndex, data, *context, errorMsg))
|
||||
return true;
|
||||
|
||||
// Let the firstly imported mesh import all materials from all meshes (index 0 is importing all following ones before itself during splitting - see code above)
|
||||
if (options.ObjectIndex == 1)
|
||||
{
|
||||
for (int32 i = 0; i < meshCount; i++)
|
||||
{
|
||||
const auto aMesh = context->Scene->getMesh(i);
|
||||
if (i == 1 || IsMeshInvalid(aMesh))
|
||||
continue;
|
||||
for (int32 j = 0; j < aMesh->getMaterialCount(); j++)
|
||||
{
|
||||
const ofbx::Material* aMaterial = aMesh->getMaterial(j);
|
||||
context->AddMaterial(data, aMaterial);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -910,10 +910,6 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
|
||||
{
|
||||
auto& texture = data.Textures[i];
|
||||
|
||||
// When splitting imported meshes allow only the first mesh to import assets (mesh[0] is imported after all following ones so import assets during mesh[1])
|
||||
if (!options.SplitObjects && options.ObjectIndex != 1 && options.ObjectIndex != -1)
|
||||
continue;
|
||||
|
||||
// Auto-import textures
|
||||
if (autoImportOutput.IsEmpty() || (data.Types & ImportDataTypes::Textures) == ImportDataTypes::None || texture.FilePath.IsEmpty())
|
||||
continue;
|
||||
@@ -1478,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);
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Float2 Size => Texture?.Size ?? Float2.Zero;
|
||||
public Float2 Size => Texture != null ? Texture.Size : Float2.Zero;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Draw(Rectangle rect, Color color)
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Float2 Size => Texture?.Size ?? Float2.Zero;
|
||||
public Float2 Size => Texture != null ? Texture.Size : Float2.Zero;
|
||||
|
||||
/// <inheritdoc />
|
||||
public unsafe void Draw(Rectangle rect, Color color)
|
||||
|
||||
@@ -84,17 +84,23 @@ namespace FlaxEngine.GUI
|
||||
/// </summary>
|
||||
[EditorDisplay("Border Style"), EditorOrder(2010), ExpandGroups]
|
||||
public bool HasBorder { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the border thickness.
|
||||
/// </summary>
|
||||
[EditorDisplay("Border Style"), EditorOrder(2011), Limit(0)]
|
||||
public float BorderThickness { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the border.
|
||||
/// </summary>
|
||||
[EditorDisplay("Border Style"), EditorOrder(2011), ExpandGroups]
|
||||
[EditorDisplay("Border Style"), EditorOrder(2012)]
|
||||
public Color BorderColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the border color when button is highlighted.
|
||||
/// </summary>
|
||||
[EditorDisplay("Border Style"), EditorOrder(2012)]
|
||||
[EditorDisplay("Border Style"), EditorOrder(2013)]
|
||||
public Color BorderColorHighlighted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -252,7 +258,7 @@ namespace FlaxEngine.GUI
|
||||
else
|
||||
Render2D.FillRectangle(clientRect, backgroundColor);
|
||||
if (HasBorder)
|
||||
Render2D.DrawRectangle(clientRect, borderColor);
|
||||
Render2D.DrawRectangle(clientRect, borderColor, BorderThickness);
|
||||
|
||||
// Draw text
|
||||
Render2D.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center);
|
||||
|
||||
@@ -107,17 +107,29 @@ namespace FlaxEngine.GUI
|
||||
CacheBox();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to have a border.
|
||||
/// </summary>
|
||||
[EditorDisplay("Border Style"), EditorOrder(2010), Tooltip("Whether to have a border."), ExpandGroups]
|
||||
public bool HasBorder { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the border thickness.
|
||||
/// </summary>
|
||||
[EditorDisplay("Border Style"), EditorOrder(2011), Tooltip("The thickness of the border."), Limit(0)]
|
||||
public float BorderThickness { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the border.
|
||||
/// </summary>
|
||||
[EditorDisplay("Border Style"), EditorOrder(2010), ExpandGroups]
|
||||
[EditorDisplay("Border Style"), EditorOrder(2012)]
|
||||
public Color BorderColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the border color when checkbox is hovered.
|
||||
/// </summary>
|
||||
[EditorDisplay("Border Style"), EditorOrder(2011)]
|
||||
[EditorDisplay("Border Style"), EditorOrder(2013)]
|
||||
public Color BorderColorHighlighted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -221,12 +233,15 @@ namespace FlaxEngine.GUI
|
||||
bool enabled = EnabledInHierarchy;
|
||||
|
||||
// Border
|
||||
Color borderColor = BorderColor;
|
||||
if (!enabled)
|
||||
borderColor *= 0.5f;
|
||||
else if (_isPressed || _mouseOverBox || IsNavFocused)
|
||||
borderColor = BorderColorHighlighted;
|
||||
Render2D.DrawRectangle(_box.MakeExpanded(-2.0f), borderColor);
|
||||
if (HasBorder)
|
||||
{
|
||||
Color borderColor = BorderColor;
|
||||
if (!enabled)
|
||||
borderColor *= 0.5f;
|
||||
else if (_isPressed || _mouseOverBox || IsNavFocused)
|
||||
borderColor = BorderColorHighlighted;
|
||||
Render2D.DrawRectangle(_box.MakeExpanded(-2.0f), borderColor, BorderThickness);
|
||||
}
|
||||
|
||||
// Icon
|
||||
if (_state != CheckBoxState.Default)
|
||||
|
||||
@@ -74,10 +74,16 @@ namespace FlaxEngine.GUI
|
||||
[EditorDisplay("Text Style"), EditorOrder(2022), Tooltip("The text wrapping within the control bounds.")]
|
||||
public TextWrapping Wrapping { get; set; } = TextWrapping.NoWrap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text wrapping within the control bounds.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2023), Tooltip("The gap between lines when wrapping and more than a single line is displayed."), Limit(0f)]
|
||||
public float BaseLinesGapScale { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the font.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2023)]
|
||||
[EditorDisplay("Text Style"), EditorOrder(2024)]
|
||||
public FontReference Font
|
||||
{
|
||||
get => _font;
|
||||
@@ -99,7 +105,7 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data.
|
||||
/// </summary>
|
||||
[EditorDisplay("Text Style"), EditorOrder(2024)]
|
||||
[EditorDisplay("Text Style"), EditorOrder(2025)]
|
||||
public MaterialBase Material { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -227,7 +233,7 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
}
|
||||
|
||||
Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, 1.0f, scale);
|
||||
Render2D.DrawText(_font.GetFont(), Material, _text, rect, color, hAlignment, wAlignment, Wrapping, BaseLinesGapScale, scale);
|
||||
|
||||
if (ClipText)
|
||||
Render2D.PopClip();
|
||||
@@ -249,6 +255,7 @@ namespace FlaxEngine.GUI
|
||||
else if (_autoWidth && !_autoHeight)
|
||||
layout.Bounds.Size.Y = Height - Margin.Height;
|
||||
_textSize = font.MeasureText(_text, ref layout);
|
||||
_textSize.Y *= BaseLinesGapScale;
|
||||
|
||||
// Check if size is controlled via text
|
||||
if (_autoWidth || _autoHeight)
|
||||
|
||||
@@ -268,7 +268,7 @@ public class Slider : ContainerControl
|
||||
// Draw track fill
|
||||
if (FillTrack)
|
||||
{
|
||||
var fillLineRect = new Rectangle(_thumbSize.X / 2, (Height - TrackHeight - 2) / 2, Width - (Width - _thumbCenter) - _thumbSize.X / 2, TrackHeight + 2);
|
||||
var fillLineRect = new Rectangle(_thumbSize.X / 2 - 1, (Height - TrackHeight - 2) / 2, Width - (Width - _thumbCenter) - _thumbSize.X / 2, TrackHeight + 2);
|
||||
Render2D.PushClip(ref fillLineRect);
|
||||
if (FillTrackBrush != null)
|
||||
FillTrackBrush.Draw(lineRect, TrackFillLineColor);
|
||||
|
||||
@@ -155,7 +155,8 @@ namespace FlaxEngine.GUI
|
||||
if (IsMouseOver || IsNavFocused)
|
||||
backColor = BackgroundSelectedColor;
|
||||
Render2D.FillRectangle(rect, backColor);
|
||||
Render2D.DrawRectangle(rect, IsFocused ? BorderSelectedColor : BorderColor);
|
||||
if (HasBorder)
|
||||
Render2D.DrawRectangle(rect, IsFocused ? BorderSelectedColor : BorderColor, BorderThickness);
|
||||
|
||||
// Apply view offset and clip mask
|
||||
if (ClipText)
|
||||
|
||||
@@ -11,6 +11,11 @@ namespace FlaxEngine.GUI
|
||||
/// </summary>
|
||||
public abstract class TextBoxBase : ContainerControl
|
||||
{
|
||||
/// <summary>
|
||||
/// The delete control character (used for text filtering).
|
||||
/// </summary>
|
||||
protected const char DelChar = (char)0x7F;
|
||||
|
||||
/// <summary>
|
||||
/// The text separators (used for words skipping).
|
||||
/// </summary>
|
||||
@@ -270,16 +275,28 @@ namespace FlaxEngine.GUI
|
||||
[EditorDisplay("Background Style"), EditorOrder(2002), Tooltip("The speed of the selection background flashing animation.")]
|
||||
public float BackgroundSelectedFlashSpeed { get; set; } = 6.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to have a border.
|
||||
/// </summary>
|
||||
[EditorDisplay("Border Style"), EditorOrder(2010), Tooltip("Whether to have a border."), ExpandGroups]
|
||||
public bool HasBorder { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the border thickness.
|
||||
/// </summary>
|
||||
[EditorDisplay("Border Style"), EditorOrder(2011), Tooltip("The thickness of the border."), Limit(0)]
|
||||
public float BorderThickness { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the border (Transparent if not used).
|
||||
/// </summary>
|
||||
[EditorDisplay("Border Style"), EditorOrder(2010), Tooltip("The color of the border (Transparent if not used)."), ExpandGroups]
|
||||
[EditorDisplay("Border Style"), EditorOrder(2012), Tooltip("The color of the border (Transparent if not used).")]
|
||||
public Color BorderColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the border when control is focused (Transparent if not used).
|
||||
/// </summary>
|
||||
[EditorDisplay("Border Style"), EditorOrder(2011), Tooltip("The color of the border when control is focused (Transparent if not used)")]
|
||||
[EditorDisplay("Border Style"), EditorOrder(2013), Tooltip("The color of the border when control is focused (Transparent if not used)")]
|
||||
public Color BorderSelectedColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -351,6 +368,10 @@ namespace FlaxEngine.GUI
|
||||
if (value.IndexOf('\r') != -1)
|
||||
value = value.Replace("\r", "");
|
||||
|
||||
// Filter text (handle backspace control character)
|
||||
if (value.IndexOf(DelChar) != -1)
|
||||
value = value.Replace(DelChar.ToString(), "");
|
||||
|
||||
// Clamp length
|
||||
if (value.Length > MaxLength)
|
||||
value = value.Substring(0, MaxLength);
|
||||
@@ -673,6 +694,8 @@ namespace FlaxEngine.GUI
|
||||
// Filter text
|
||||
if (str.IndexOf('\r') != -1)
|
||||
str = str.Replace("\r", "");
|
||||
if (str.IndexOf(DelChar) != -1)
|
||||
str = str.Replace(DelChar.ToString(), "");
|
||||
if (!IsMultiline && str.IndexOf('\n') != -1)
|
||||
str = str.Replace("\n", "");
|
||||
|
||||
@@ -1327,6 +1350,15 @@ namespace FlaxEngine.GUI
|
||||
if (IsReadOnly)
|
||||
return true;
|
||||
|
||||
if (ctrDown)
|
||||
{
|
||||
int prevWordBegin = FindPrevWordBegin();
|
||||
_text = _text.Remove(prevWordBegin, CaretPosition - prevWordBegin);
|
||||
SetSelection(prevWordBegin);
|
||||
OnTextChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
int left = SelectionLeft;
|
||||
if (HasSelection)
|
||||
{
|
||||
|
||||
@@ -360,7 +360,7 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
var containerControl = child as ContainerControl;
|
||||
var childAtRecursive = containerControl?.GetChildAtRecursive(childLocation);
|
||||
if (childAtRecursive != null)
|
||||
if (childAtRecursive != null && childAtRecursive.Visible)
|
||||
{
|
||||
child = childAtRecursive;
|
||||
}
|
||||
@@ -507,15 +507,19 @@ namespace FlaxEngine.GUI
|
||||
|
||||
// Perform automatic navigation based on the layout
|
||||
var result = NavigationRaycast(direction, location, visited);
|
||||
if (result == null && direction == NavDirection.Next)
|
||||
var rightMostLocation = location;
|
||||
if (result == null && (direction == NavDirection.Next || direction == NavDirection.Previous))
|
||||
{
|
||||
// Try wrap the navigation over the layout based on the direction
|
||||
var visitedWrap = new List<Control>(visited);
|
||||
result = NavigationWrap(direction, location, visitedWrap);
|
||||
result = NavigationWrap(direction, location, visitedWrap, out rightMostLocation);
|
||||
}
|
||||
if (result != null)
|
||||
{
|
||||
result = result.OnNavigate(direction, result.PointFromParent(location), this, visited);
|
||||
// HACK: only the 'previous' direction needs the rightMostLocation so i used a ternary conditional operator.
|
||||
// The rightMostLocation can probably become a 'desired raycast origin' that gets calculated correctly in the NavigationWrap method.
|
||||
var useLocation = direction == NavDirection.Previous ? rightMostLocation : location;
|
||||
result = result.OnNavigate(direction, result.PointFromParent(useLocation), this, visited);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
@@ -551,8 +555,9 @@ namespace FlaxEngine.GUI
|
||||
/// <param name="direction">The navigation direction.</param>
|
||||
/// <param name="location">The navigation start location (in the control-space).</param>
|
||||
/// <param name="visited">The list with visited controls. Used to skip recursive navigation calls when doing traversal across the UI hierarchy.</param>
|
||||
/// <param name="rightMostLocation">Returns the rightmost location of the parent container for the raycast used by the child container</param>
|
||||
/// <returns>The target navigation control or null if didn't performed any navigation.</returns>
|
||||
protected virtual Control NavigationWrap(NavDirection direction, Float2 location, List<Control> visited)
|
||||
protected virtual Control NavigationWrap(NavDirection direction, Float2 location, List<Control> visited, out Float2 rightMostLocation)
|
||||
{
|
||||
// This searches form a child that calls this navigation event (see Control.OnNavigate) to determinate the layout wrapping size based on that child size
|
||||
var currentChild = RootWindow?.FocusedControl;
|
||||
@@ -566,15 +571,22 @@ namespace FlaxEngine.GUI
|
||||
case NavDirection.Next:
|
||||
predictedLocation = new Float2(0, location.Y + layoutSize.Y);
|
||||
break;
|
||||
case NavDirection.Previous:
|
||||
predictedLocation = new Float2(Size.X, location.Y - layoutSize.Y);
|
||||
break;
|
||||
}
|
||||
if (new Rectangle(Float2.Zero, Size).Contains(ref predictedLocation))
|
||||
{
|
||||
var result = NavigationRaycast(direction, predictedLocation, visited);
|
||||
if (result != null)
|
||||
return result;
|
||||
{
|
||||
rightMostLocation = predictedLocation;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Parent?.NavigationWrap(direction, PointToParent(ref location), visited);
|
||||
rightMostLocation = location;
|
||||
return Parent?.NavigationWrap(direction, PointToParent(ref location), visited, out rightMostLocation);
|
||||
}
|
||||
|
||||
private static bool CanGetAutoFocus(Control c)
|
||||
@@ -613,6 +625,10 @@ namespace FlaxEngine.GUI
|
||||
uiDir1 = new Float2(1, 0);
|
||||
uiDir2 = new Float2(0, 1);
|
||||
break;
|
||||
case NavDirection.Previous:
|
||||
uiDir1 = new Float2(-1, 0);
|
||||
uiDir2 = new Float2(0, -1);
|
||||
break;
|
||||
}
|
||||
Control result = null;
|
||||
var minDistance = float.MaxValue;
|
||||
|
||||
@@ -634,6 +634,7 @@ namespace FlaxEngine.GUI
|
||||
case NavDirection.Left: return new Float2(0, size.Y * 0.5f);
|
||||
case NavDirection.Right: return new Float2(size.X, size.Y * 0.5f);
|
||||
case NavDirection.Next: return Float2.Zero;
|
||||
case NavDirection.Previous: return size;
|
||||
default: return size * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,5 +202,10 @@ namespace FlaxEngine.GUI
|
||||
/// The next item (right with layout wrapping).
|
||||
/// </summary>
|
||||
Next,
|
||||
|
||||
/// <summary>
|
||||
/// The previous item (left with layout wrapping).
|
||||
/// </summary>
|
||||
Previous,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace FlaxEngine.GUI
|
||||
/// <summary>
|
||||
/// Gets or sets the blur strength. Defines how blurry the background is. Larger numbers increase blur, resulting in a larger runtime cost on the GPU.
|
||||
/// </summary>
|
||||
[EditorOrder(0), Limit(0, 100, 0.0f)]
|
||||
[EditorOrder(0), Limit(0, 100, 0.1f)]
|
||||
public float BlurStrength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -29,10 +29,9 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
public override void DrawSelf()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
base.DrawSelf();
|
||||
var size = Size;
|
||||
var strength = BlurStrength;
|
||||
if (BlurScaleWithSize)
|
||||
|
||||
@@ -164,6 +164,12 @@ namespace FlaxEngine.GUI
|
||||
[EditorOrder(200)]
|
||||
public Color ProgressNormal;
|
||||
|
||||
/// <summary>
|
||||
/// The status bar style
|
||||
/// </summary>
|
||||
[EditorOrder(210)]
|
||||
public StatusbarStyle Statusbar;
|
||||
|
||||
/// <summary>
|
||||
/// The arrow right icon.
|
||||
/// </summary>
|
||||
@@ -241,5 +247,27 @@ namespace FlaxEngine.GUI
|
||||
/// </summary>
|
||||
[EditorOrder(340)]
|
||||
public Tooltip SharedTooltip;
|
||||
|
||||
/// <summary>
|
||||
/// Style for the Statusbar
|
||||
/// </summary>
|
||||
[System.Serializable, ShowInEditor]
|
||||
public struct StatusbarStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Color of the Statusbar when in Play Mode
|
||||
/// </summary>
|
||||
public Color PlayMode;
|
||||
|
||||
/// <summary>
|
||||
/// Color of the Statusbar when in loading state (e.g. when importing assets)
|
||||
/// </summary>
|
||||
public Color Loading;
|
||||
|
||||
/// <summary>
|
||||
/// Color of the Statusbar in its failed state (e.g. with compilation errors)
|
||||
/// </summary>
|
||||
public Color Failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,26 +68,12 @@ namespace FlaxEngine.GUI
|
||||
var parentWin = target.Root;
|
||||
if (parentWin == null)
|
||||
return;
|
||||
float dpiScale = target.RootWindow.DpiScale;
|
||||
var dpiScale = target.RootWindow.DpiScale;
|
||||
var dpiSize = Size * dpiScale;
|
||||
var locationWS = target.PointToWindow(location);
|
||||
var locationSS = parentWin.PointToScreen(locationWS);
|
||||
var monitorBounds = Platform.GetMonitorBounds(locationSS);
|
||||
var rightBottomMonitorBounds = monitorBounds.BottomRight;
|
||||
var rightBottomLocationSS = locationSS + dpiSize;
|
||||
|
||||
// Prioritize tooltip placement within parent window, fall back to virtual desktop
|
||||
if (rightBottomMonitorBounds.Y < rightBottomLocationSS.Y)
|
||||
{
|
||||
// Direction: up
|
||||
locationSS.Y -= dpiSize.Y;
|
||||
}
|
||||
if (rightBottomMonitorBounds.X < rightBottomLocationSS.X)
|
||||
{
|
||||
// Direction: left
|
||||
locationSS.X -= dpiSize.X;
|
||||
}
|
||||
_showTarget = target;
|
||||
WrapPosition(ref locationSS);
|
||||
|
||||
// Create window
|
||||
var desc = CreateWindowSettings.Default;
|
||||
@@ -106,6 +92,7 @@ namespace FlaxEngine.GUI
|
||||
desc.IsTopmost = true;
|
||||
desc.IsRegularWindow = false;
|
||||
desc.HasSizingFrame = false;
|
||||
desc.ShowAfterFirstPaint = true;
|
||||
_window = Platform.CreateWindow(ref desc);
|
||||
if (_window == null)
|
||||
throw new InvalidOperationException("Failed to create tooltip window.");
|
||||
@@ -192,11 +179,41 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
}
|
||||
|
||||
private void WrapPosition(ref Float2 locationSS, float flipOffset = 0.0f)
|
||||
{
|
||||
if (_showTarget?.RootWindow == null)
|
||||
return;
|
||||
|
||||
// Calculate popup direction
|
||||
var dpiScale = _showTarget.RootWindow.DpiScale;
|
||||
var dpiSize = Size * dpiScale;
|
||||
var monitorBounds = Platform.GetMonitorBounds(locationSS);
|
||||
var rightBottomMonitorBounds = monitorBounds.BottomRight;
|
||||
var rightBottomLocationSS = locationSS + dpiSize;
|
||||
|
||||
// Prioritize tooltip placement within parent window, fall back to virtual desktop
|
||||
if (rightBottomMonitorBounds.Y < rightBottomLocationSS.Y)
|
||||
{
|
||||
// Direction: up
|
||||
locationSS.Y -= dpiSize.Y + flipOffset;
|
||||
}
|
||||
if (rightBottomMonitorBounds.X < rightBottomLocationSS.X)
|
||||
{
|
||||
// Direction: left
|
||||
locationSS.X -= dpiSize.X + flipOffset * 2;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
// Auto hide if mouse leaves control area
|
||||
// Move window with mouse location
|
||||
var mousePos = Input.MouseScreenPosition;
|
||||
WrapPosition(ref mousePos, 10);
|
||||
if (_window)
|
||||
_window.Position = mousePos + new Float2(15, 10);
|
||||
|
||||
// Auto hide if mouse leaves control area
|
||||
var location = _showTarget.PointFromScreen(mousePos);
|
||||
if (!_showTarget.OnTestTooltipOverControl(ref location))
|
||||
{
|
||||
|
||||
@@ -493,7 +493,8 @@ namespace FlaxEngine
|
||||
if (_renderer)
|
||||
{
|
||||
#if FLAX_EDITOR
|
||||
_editorTask?.RemoveCustomPostFx(_renderer);
|
||||
if (_editorTask != null)
|
||||
_editorTask.RemoveCustomPostFx(_renderer);
|
||||
#endif
|
||||
SceneRenderTask.RemoveGlobalCustomPostFx(_renderer);
|
||||
_renderer.Canvas = null;
|
||||
|
||||
@@ -204,7 +204,7 @@ namespace FlaxEngine
|
||||
up = value;
|
||||
Internal_SetNavTargets(__unmanagedPtr, GetUnmanagedPtr(up), GetUnmanagedPtr(down), GetUnmanagedPtr(left), GetUnmanagedPtr(right));
|
||||
if (_control != null)
|
||||
_control.NavTargetUp = value?.Control;
|
||||
_control.NavTargetUp = value != null ? value.Control : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ namespace FlaxEngine
|
||||
down = value;
|
||||
Internal_SetNavTargets(__unmanagedPtr, GetUnmanagedPtr(up), GetUnmanagedPtr(down), GetUnmanagedPtr(left), GetUnmanagedPtr(right));
|
||||
if (_control != null)
|
||||
_control.NavTargetDown = value?.Control;
|
||||
_control.NavTargetDown = value != null ? value.Control : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ namespace FlaxEngine
|
||||
left = value;
|
||||
Internal_SetNavTargets(__unmanagedPtr, GetUnmanagedPtr(up), GetUnmanagedPtr(down), GetUnmanagedPtr(left), GetUnmanagedPtr(right));
|
||||
if (_control != null)
|
||||
_control.NavTargetLeft = value?.Control;
|
||||
_control.NavTargetLeft = value != null ? value.Control : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ namespace FlaxEngine
|
||||
right = value;
|
||||
Internal_SetNavTargets(__unmanagedPtr, GetUnmanagedPtr(up), GetUnmanagedPtr(down), GetUnmanagedPtr(left), GetUnmanagedPtr(right));
|
||||
if (_control != null)
|
||||
_control.NavTargetRight = value?.Control;
|
||||
_control.NavTargetRight = value != null ? value.Control : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,11 +40,11 @@ ShaderGraphValue::ShaderGraphValue(const Variant& v)
|
||||
break;
|
||||
case VariantType::Float:
|
||||
Type = VariantType::Types::Float;
|
||||
Value = String::Format(TEXT("{}"), v.AsFloat);
|
||||
Value = String::Format(TEXT("{:.8f}"), v.AsFloat);
|
||||
break;
|
||||
case VariantType::Double:
|
||||
Type = VariantType::Types::Float;
|
||||
Value = String::Format(TEXT("{}"), (float)v.AsDouble);
|
||||
Value = String::Format(TEXT("{:.8f}"), (float)v.AsDouble);
|
||||
break;
|
||||
case VariantType::Float2:
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user