Merge remote-tracking branch 'origin/1.5' into dotnet7
This commit is contained in:
@@ -4,15 +4,16 @@
|
||||
#include "Cache/AssetsCache.h"
|
||||
#include "Storage/ContentStorageManager.h"
|
||||
#include "Loading/Tasks/LoadAssetDataTask.h"
|
||||
#include "Factories/BinaryAssetFactory.h"
|
||||
#include "Engine/ContentImporters/AssetsImportingManager.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Serialization/JsonTools.h"
|
||||
#include "Engine/Debug/Exceptions/JsonParseException.h"
|
||||
#include "Factories/BinaryAssetFactory.h"
|
||||
#include "Engine/Threading/ThreadPoolTask.h"
|
||||
#if USE_EDITOR
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#endif
|
||||
|
||||
REGISTER_BINARY_ASSET_ABSTRACT(BinaryAsset, "FlaxEngine.BinaryAsset");
|
||||
@@ -123,6 +124,12 @@ void BinaryAsset::GetImportMetadata(String& path, String& username) const
|
||||
{
|
||||
path = JsonTools::GetString(document, "ImportPath");
|
||||
username = JsonTools::GetString(document, "ImportUsername");
|
||||
if (path.HasChars() && FileSystem::IsRelative(path))
|
||||
{
|
||||
// Convert path back to thr absolute (eg. if stored in relative format)
|
||||
path = Globals::ProjectFolder / path;
|
||||
StringUtils::PathRemoveRelativeParts(path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "Content.h"
|
||||
#include "JsonAsset.h"
|
||||
#include "SceneReference.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
#include "Cache/AssetsCache.h"
|
||||
#include "Storage/ContentStorageManager.h"
|
||||
#include "Storage/JsonStorageProxy.h"
|
||||
@@ -39,6 +41,16 @@ String AssetInfo::ToString() const
|
||||
return String::Format(TEXT("ID: {0}, TypeName: {1}, Path: \'{2}\'"), ID, TypeName, Path);
|
||||
}
|
||||
|
||||
void FLAXENGINE_API Serialization::Serialize(ISerializable::SerializeStream& stream, const SceneReference& v, const void* otherObj)
|
||||
{
|
||||
Serialize(stream, v.ID, otherObj);
|
||||
}
|
||||
|
||||
void FLAXENGINE_API Serialization::Deserialize(ISerializable::DeserializeStream& stream, SceneReference& v, ISerializeModifier* modifier)
|
||||
{
|
||||
Deserialize(stream, v.ID, modifier);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// Assets
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Types/Guid.h"
|
||||
#include "Engine/Core/ISerializable.h"
|
||||
|
||||
/// <summary>
|
||||
/// Represents the reference to the scene asset. Stores the unique ID of the scene to reference. Can be used to load the selected scene.
|
||||
@@ -15,4 +16,37 @@ API_STRUCT(NoDefault) struct FLAXENGINE_API SceneReference
|
||||
/// The identifier of the scene asset (and the scene object).
|
||||
/// </summary>
|
||||
API_FIELD() Guid ID;
|
||||
|
||||
FORCE_INLINE bool operator==(const SceneReference& other) const
|
||||
{
|
||||
return ID == other.ID;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator!=(const SceneReference& other) const
|
||||
{
|
||||
return ID != other.ID;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct TIsPODType<SceneReference>
|
||||
{
|
||||
enum { Value = true };
|
||||
};
|
||||
|
||||
inline uint32 GetHash(const SceneReference& key)
|
||||
{
|
||||
return GetHash(key.ID);
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
namespace Serialization
|
||||
{
|
||||
inline bool ShouldSerialize(const SceneReference& v, const void* otherObj)
|
||||
{
|
||||
return !otherObj || v != *(SceneReference*)otherObj;
|
||||
}
|
||||
void FLAXENGINE_API Serialize(ISerializable::SerializeStream& stream, const SceneReference& v, const void* otherObj);
|
||||
void FLAXENGINE_API Deserialize(ISerializable::DeserializeStream& stream, SceneReference& v, ISerializeModifier* modifier);
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#if COMPILE_WITH_ASSETS_IMPORTER
|
||||
|
||||
#include "AssetsImportingManager.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Utilities.h"
|
||||
#include "Engine/Serialization/JsonWriters.h"
|
||||
#include "Engine/Threading/MainThreadTask.h"
|
||||
@@ -10,9 +11,9 @@
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Content/Cache/AssetsCache.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "ImportTexture.h"
|
||||
#include "ImportModelFile.h"
|
||||
#include "ImportAudio.h"
|
||||
@@ -71,6 +72,7 @@ AssetsImportingManagerService AssetsImportingManagerServiceInstance;
|
||||
|
||||
Array<AssetImporter> AssetsImportingManager::Importers;
|
||||
Array<AssetCreator> AssetsImportingManager::Creators;
|
||||
bool AssetsImportingManager::UseImportPathRelative = false;
|
||||
|
||||
CreateAssetContext::CreateAssetContext(const StringView& inputPath, const StringView& outputPath, const Guid& id, void* arg)
|
||||
{
|
||||
@@ -161,7 +163,15 @@ bool CreateAssetContext::AllocateChunk(int32 index)
|
||||
void CreateAssetContext::AddMeta(JsonWriter& writer) const
|
||||
{
|
||||
writer.JKEY("ImportPath");
|
||||
writer.String(InputPath);
|
||||
if (AssetsImportingManager::UseImportPathRelative && !FileSystem::IsRelative(InputPath))
|
||||
{
|
||||
const String relativePath = FileSystem::ConvertAbsolutePathToRelative(Globals::ProjectFolder, InputPath);
|
||||
writer.String(relativePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.String(InputPath);
|
||||
}
|
||||
writer.JKEY("ImportUsername");
|
||||
writer.String(Platform::GetUserName());
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@ public:
|
||||
/// </summary>
|
||||
static Array<AssetCreator> Creators;
|
||||
|
||||
/// <summary>
|
||||
/// If true store asset import path relative to the current workspace, otherwise will store absolute path.
|
||||
/// </summary>
|
||||
static bool UseImportPathRelative;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// The create texture tag (using internal import pipeline to crate texture asset from custom image source).
|
||||
|
||||
@@ -12,7 +12,8 @@ FLAXENGINE_API String* TagsListDebug = nullptr;
|
||||
|
||||
const String& Tag::ToString() const
|
||||
{
|
||||
return Index >= 0 && Index < Tags::List.Count() ? Tags::List.Get()[Index] : String::Empty;
|
||||
const int32 index = (int32)Index - 1;
|
||||
return index >= 0 && index < Tags::List.Count() ? Tags::List.Get()[index] : String::Empty;
|
||||
}
|
||||
|
||||
bool Tag::operator==(const StringView& other) const
|
||||
@@ -27,7 +28,7 @@ bool Tag::operator!=(const StringView& other) const
|
||||
|
||||
void FLAXENGINE_API Serialization::Serialize(ISerializable::SerializeStream& stream, const Tag& v, const void* otherObj)
|
||||
{
|
||||
if (v.Index != -1)
|
||||
if (v.Index != 0)
|
||||
stream.String(v.ToString());
|
||||
else
|
||||
stream.String("", 0);
|
||||
@@ -42,11 +43,11 @@ Tag Tags::Get(const StringView& tagName)
|
||||
{
|
||||
if (tagName.IsEmpty())
|
||||
return Tag();
|
||||
Tag tag = List.Find(tagName);
|
||||
if (tag.Index == -1 && tagName.HasChars())
|
||||
Tag tag(List.Find(tagName) + 1);
|
||||
if (tag.Index == 0 && tagName.HasChars())
|
||||
{
|
||||
tag.Index = List.Count();
|
||||
List.AddOne() = tagName;
|
||||
tag.Index = List.Count();
|
||||
#if !BUILD_RELEASE
|
||||
TagsListDebug = List.Get();
|
||||
#endif
|
||||
@@ -56,7 +57,7 @@ Tag Tags::Get(const StringView& tagName)
|
||||
|
||||
bool Tags::HasTag(const Array<Tag>& list, const Tag& tag)
|
||||
{
|
||||
if (tag.Index == -1)
|
||||
if (tag.Index == 0)
|
||||
return false;
|
||||
const String& tagName = tag.ToString();
|
||||
for (const Tag& e : list)
|
||||
@@ -70,7 +71,7 @@ bool Tags::HasTag(const Array<Tag>& list, const Tag& tag)
|
||||
|
||||
bool Tags::HasTagExact(const Array<Tag>& list, const Tag& tag)
|
||||
{
|
||||
if (tag.Index == -1)
|
||||
if (tag.Index == 0)
|
||||
return false;
|
||||
return list.Contains(tag);
|
||||
}
|
||||
@@ -119,7 +120,7 @@ bool Tags::HasAllExact(const Array<Tag>& list, const Array<Tag>& tags)
|
||||
return true;
|
||||
}
|
||||
|
||||
const String& Tags::GetTagName(int32 tag)
|
||||
const String& Tags::GetTagName(uint32 tag)
|
||||
{
|
||||
return Tag(tag).ToString();
|
||||
}
|
||||
|
||||
@@ -1,33 +1,29 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
[TypeConverter(typeof(TypeConverters.TagConverter))]
|
||||
partial struct Tag : IEquatable<Tag>, IEquatable<string>, IComparable, IComparable<Tag>, IComparable<string>
|
||||
{
|
||||
/// <summary>
|
||||
/// The default <see cref="Tag"/>.
|
||||
/// </summary>
|
||||
public static Tag Default => new Tag(-1);
|
||||
public static Tag Default => new Tag(0);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Tag" /> struct.
|
||||
/// </summary>
|
||||
/// <param name="index">The tag index.</param>
|
||||
public Tag(int index)
|
||||
public Tag(uint index)
|
||||
{
|
||||
Index = index;
|
||||
}
|
||||
|
||||
[System.Runtime.Serialization.OnDeserializing]
|
||||
internal void OnDeserializing(System.Runtime.Serialization.StreamingContext context)
|
||||
{
|
||||
// Initialize structure with default values to replicate C++ deserialization behavior
|
||||
Index = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two tags.
|
||||
/// </summary>
|
||||
@@ -50,6 +46,28 @@ namespace FlaxEngine
|
||||
return left.Index == right.Index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two tags.
|
||||
/// </summary>
|
||||
/// <param name="left">The lft tag.</param>
|
||||
/// <param name="right">The right tag name.</param>
|
||||
/// <returns>True if both values are equal, otherwise false.</returns>
|
||||
public static bool operator ==(Tag left, string right)
|
||||
{
|
||||
return string.Equals(left.ToString(), right, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two tags.
|
||||
/// </summary>
|
||||
/// <param name="left">The lft tag.</param>
|
||||
/// <param name="right">The right tag name.</param>
|
||||
/// <returns>True if both values are not equal, otherwise false.</returns>
|
||||
public static bool operator !=(Tag left, string right)
|
||||
{
|
||||
return !string.Equals(left.ToString(), right, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if tag is valid.
|
||||
/// </summary>
|
||||
@@ -58,7 +76,7 @@ namespace FlaxEngine
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator bool(Tag tag)
|
||||
{
|
||||
return tag.Index != -1;
|
||||
return tag.Index != 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -104,7 +122,7 @@ namespace FlaxEngine
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Index;
|
||||
return (int)Index;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -124,7 +142,7 @@ namespace FlaxEngine
|
||||
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
|
||||
public static bool HasTag(this Tag[] list, Tag tag)
|
||||
{
|
||||
if (tag.Index == -1)
|
||||
if (tag.Index == 0)
|
||||
return false;
|
||||
string tagName = tag.ToString();
|
||||
foreach (Tag e in list)
|
||||
@@ -144,7 +162,7 @@ namespace FlaxEngine
|
||||
/// <returns>True if given tag is contained by the list of tags. Returns false for empty list.</returns>
|
||||
public static bool HasTagExact(this Tag[] list, Tag tag)
|
||||
{
|
||||
if (tag.Index == -1)
|
||||
if (tag.Index == 0)
|
||||
return false;
|
||||
if (list == null)
|
||||
return false;
|
||||
@@ -233,3 +251,40 @@ namespace FlaxEngine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class TagConverter : TypeConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
return true;
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
if (str.Length == 0)
|
||||
return Tag.Default;
|
||||
return Tags.Get(str);
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
var v = (Tag)value;
|
||||
return v.ToString();
|
||||
}
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ API_STRUCT(NoDefault) struct FLAXENGINE_API Tag
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(Tag);
|
||||
|
||||
/// <summary>
|
||||
/// Index of the tag (in global Level.Tags list).
|
||||
/// Index of the tag (in global Level.Tags list). Index 0 is invalid. 1 is the first index.
|
||||
/// </summary>
|
||||
API_FIELD() int32 Index = -1;
|
||||
API_FIELD() uint32 Index = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tag name.
|
||||
@@ -26,14 +26,14 @@ API_STRUCT(NoDefault) struct FLAXENGINE_API Tag
|
||||
public:
|
||||
Tag() = default;
|
||||
|
||||
FORCE_INLINE Tag(int32 index)
|
||||
FORCE_INLINE explicit Tag(uint32 index)
|
||||
: Index(index)
|
||||
{
|
||||
}
|
||||
|
||||
FORCE_INLINE operator bool() const
|
||||
{
|
||||
return Index != -1;
|
||||
return Index != 0;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator==(const Tag& other) const
|
||||
@@ -142,7 +142,7 @@ public:
|
||||
static bool HasAllExact(const Array<Tag>& list, const Array<Tag>& tags);
|
||||
|
||||
private:
|
||||
API_FUNCTION(NoProxy) static const String& GetTagName(int32 tag);
|
||||
API_FUNCTION(NoProxy) static const String& GetTagName(uint32 tag);
|
||||
};
|
||||
|
||||
#if !BUILD_RELEASE
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "Types.h"
|
||||
#include "Engine/Core/ISerializable.h"
|
||||
#include "Engine/Level/Tags.h"
|
||||
|
||||
/// <summary>
|
||||
/// Physical materials are used to define the response of a physical object when interacting dynamically with the world.
|
||||
@@ -69,6 +70,12 @@ public:
|
||||
API_FIELD(Attributes="EditorOrder(10), EditorDisplay(\"Physical Material\")")
|
||||
float Density = 1000.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Physical material tag used to identify it (eg. `Surface.Wood`). Can be used to play proper footstep sounds when walking over object with that material.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Physical Material\")")
|
||||
Tag Tag;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the PhysX material.
|
||||
|
||||
@@ -119,6 +119,11 @@ public:
|
||||
|
||||
// Tries to cast native interface object to scripting object instance. Returns null if fails.
|
||||
static ScriptingObject* FromInterface(void* interfaceObj, const ScriptingTypeHandle& interfaceType);
|
||||
template<typename T>
|
||||
static ScriptingObject* FromInterface(T* interfaceObj)
|
||||
{
|
||||
return FromInterface(interfaceObj, T::TypeInitializer);
|
||||
}
|
||||
static void* ToInterface(ScriptingObject* obj, const ScriptingTypeHandle& interfaceType);
|
||||
template<typename T>
|
||||
static T* ToInterface(ScriptingObject* obj)
|
||||
|
||||
@@ -21,7 +21,9 @@ TEST_CASE("Scripting")
|
||||
CHECK(object->Is<TestClassNative>());
|
||||
TestClassNative* testClass = (TestClassNative*)object;
|
||||
CHECK(testClass->SimpleField == 1);
|
||||
int32 methodResult = testClass->Test(TEXT("123"));
|
||||
CHECK(testClass->SimpleStruct.Object == nullptr);
|
||||
CHECK(testClass->SimpleStruct.Vector == Float3::One);
|
||||
int32 methodResult = testClass->TestMethod(TEXT("123"));
|
||||
CHECK(methodResult == 3);
|
||||
|
||||
// Test managed class
|
||||
@@ -34,7 +36,80 @@ TEST_CASE("Scripting")
|
||||
MObject* managed = testClass->GetOrCreateManagedInstance(); // Ensure to create C# object and run it's ctor
|
||||
CHECK(managed);
|
||||
CHECK(testClass->SimpleField == 2);
|
||||
methodResult = testClass->Test(TEXT("123"));
|
||||
CHECK(testClass->SimpleStruct.Object == testClass);
|
||||
CHECK(testClass->SimpleStruct.Vector == Float3::UnitX);
|
||||
methodResult = testClass->TestMethod(TEXT("123"));
|
||||
CHECK(methodResult == 6);
|
||||
}
|
||||
|
||||
SECTION("Test Event")
|
||||
{
|
||||
ScriptingTypeHandle type = Scripting::FindScriptingType("FlaxEngine.TestClassManaged");
|
||||
CHECK(type);
|
||||
ScriptingObject* object = Scripting::NewObject(type.GetType().ManagedClass);
|
||||
CHECK(object);
|
||||
MObject* managed = object->GetOrCreateManagedInstance(); // Ensure to create C# object and run it's ctor
|
||||
CHECK(managed);
|
||||
TestClassNative* testClass = (TestClassNative*)object;
|
||||
CHECK(testClass->SimpleField == 2);
|
||||
String str1 = TEXT("1");
|
||||
String str2 = TEXT("2");
|
||||
Array<TestStruct> arr1 = { testClass->SimpleStruct };
|
||||
Array<TestStruct> arr2 = { testClass->SimpleStruct };
|
||||
testClass->SimpleEvent(1, Float3::One, str1, str2, arr1, arr2);
|
||||
CHECK(testClass->SimpleField == 4);
|
||||
CHECK(str2 == TEXT("4"));
|
||||
CHECK(arr2.Count() == 2);
|
||||
CHECK(arr2[0].Vector == Float3::Half);
|
||||
CHECK(arr2[0].Object == nullptr);
|
||||
CHECK(arr2[1].Vector == testClass->SimpleStruct.Vector);
|
||||
CHECK(arr2[1].Object == testClass);
|
||||
}
|
||||
SECTION("Test Interface")
|
||||
{
|
||||
// Test native interface implementation
|
||||
ScriptingTypeHandle type = Scripting::FindScriptingType("FlaxEngine.TestClassNative");
|
||||
CHECK(type);
|
||||
ScriptingObject* object = Scripting::NewObject(type.GetType().ManagedClass);
|
||||
CHECK(object);
|
||||
TestClassNative* testClass = (TestClassNative*)object;
|
||||
int32 methodResult = testClass->TestInterfaceMethod(TEXT("123"));
|
||||
CHECK(methodResult == 3);
|
||||
ITestInterface* interface = ScriptingObject::ToInterface<ITestInterface>(object);
|
||||
CHECK(interface);
|
||||
methodResult = interface->TestInterfaceMethod(TEXT("1234"));
|
||||
CHECK(methodResult == 4);
|
||||
ScriptingObject* interfaceObject = ScriptingObject::FromInterface<ITestInterface>(interface);
|
||||
CHECK(interfaceObject);
|
||||
CHECK(interfaceObject == object);
|
||||
|
||||
// Test managed interface override
|
||||
type = Scripting::FindScriptingType("FlaxEngine.TestClassManaged");
|
||||
CHECK(type);
|
||||
object = Scripting::NewObject(type.GetType().ManagedClass);
|
||||
CHECK(object);
|
||||
testClass = (TestClassNative*)object;
|
||||
methodResult = testClass->TestInterfaceMethod(TEXT("123"));
|
||||
CHECK(methodResult == 6);
|
||||
interface = ScriptingObject::ToInterface<ITestInterface>(object);
|
||||
CHECK(interface);
|
||||
methodResult = interface->TestInterfaceMethod(TEXT("1234"));
|
||||
CHECK(methodResult == 8);
|
||||
interfaceObject = ScriptingObject::FromInterface<ITestInterface>(interface);
|
||||
CHECK(interfaceObject);
|
||||
CHECK(interfaceObject == object);
|
||||
|
||||
// Test managed interface implementation
|
||||
type = Scripting::FindScriptingType("FlaxEngine.TestInterfaceManaged");
|
||||
CHECK(type);
|
||||
object = Scripting::NewObject(type.GetType().ManagedClass);
|
||||
CHECK(object);
|
||||
interface = ScriptingObject::ToInterface<ITestInterface>(object);
|
||||
CHECK(interface);
|
||||
methodResult = interface->TestInterfaceMethod(TEXT("1234"));
|
||||
CHECK(methodResult == 4);
|
||||
interfaceObject = ScriptingObject::FromInterface<ITestInterface>(interface);
|
||||
CHECK(interfaceObject);
|
||||
CHECK(interfaceObject == object);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,48 @@
|
||||
#if FLAX_TESTS
|
||||
namespace FlaxEngine
|
||||
{
|
||||
partial struct TestStruct : System.IEquatable<TestStruct>
|
||||
{
|
||||
/// <summary></summary>
|
||||
public static bool operator ==(TestStruct left, TestStruct right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary></summary>
|
||||
public static bool operator !=(TestStruct left, TestStruct right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(TestStruct other)
|
||||
{
|
||||
return Vector.Equals(other.Vector) && Equals(Object, other.Object);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is TestStruct other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (Vector.GetHashCode() * 397) ^ (Object != null ? Object.GetHashCode() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Vector={Vector}, Object={Object?.ToString() ?? "null"}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test class.
|
||||
/// </summary>
|
||||
@@ -10,13 +52,65 @@ namespace FlaxEngine
|
||||
{
|
||||
TestClassManaged()
|
||||
{
|
||||
// Test setting C++ values from C#
|
||||
SimpleField = 2;
|
||||
SimpleStruct = new TestStruct
|
||||
{
|
||||
Vector = Float3.UnitX,
|
||||
Object = this,
|
||||
};
|
||||
SimpleEvent += OnSimpleEvent;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Test(string str)
|
||||
public override int TestMethod(string str)
|
||||
{
|
||||
return str.Length + base.Test(str);
|
||||
// Test C++ base method invocation
|
||||
return str.Length + base.TestMethod(str);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int TestInterfaceMethod(string str)
|
||||
{
|
||||
// Test C++ base method invocation
|
||||
return str.Length + base.TestInterfaceMethod(str);
|
||||
}
|
||||
|
||||
private void OnSimpleEvent(int arg1, Float3 arg2, string arg3, ref string arg4, TestStruct[] arg5, ref TestStruct[] arg6)
|
||||
{
|
||||
// Verify that C++ passed proper data to C# via event bindings
|
||||
if (arg1 == 1 &&
|
||||
arg2 == Float3.One &&
|
||||
arg3 == "1" &&
|
||||
arg4 == "2" &&
|
||||
arg5 != null && arg5.Length == 1 && arg5[0] == SimpleStruct &&
|
||||
arg6 != null && arg6.Length == 1 && arg6[0] == SimpleStruct)
|
||||
{
|
||||
// Test passing data back from C# to C++
|
||||
SimpleField = 4;
|
||||
arg4 = "4";
|
||||
arg6 = new TestStruct[2]
|
||||
{
|
||||
new TestStruct
|
||||
{
|
||||
Vector = Float3.Half,
|
||||
Object = null,
|
||||
},
|
||||
SimpleStruct,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test interface in C#.
|
||||
/// </summary>
|
||||
public class TestInterfaceManaged : Object, ITestInterface
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int TestInterfaceMethod(string str)
|
||||
{
|
||||
return str.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/ISerializable.h"
|
||||
#include "Engine/Core/Math/Vector3.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
|
||||
// Test structure.
|
||||
API_STRUCT(NoDefault) struct TestStruct : public ISerializable
|
||||
{
|
||||
API_AUTO_SERIALIZATION();
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(TestStruct);
|
||||
|
||||
// Var
|
||||
API_FIELD() Float3 Vector = Float3::One;
|
||||
// Ref
|
||||
API_FIELD() ScriptingObject* Object = nullptr;
|
||||
};
|
||||
|
||||
// Test interface.
|
||||
API_INTERFACE() class ITestInterface
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(ITestInterface);
|
||||
~ITestInterface() = default;
|
||||
|
||||
// Test abstract method
|
||||
API_FUNCTION() virtual int32 TestInterfaceMethod(const String& str) = 0;
|
||||
};
|
||||
|
||||
// Test class.
|
||||
API_CLASS() class TestClassNative : public ScriptingObject, public ISerializable
|
||||
API_CLASS() class TestClassNative : public ScriptingObject, public ISerializable, public ITestInterface
|
||||
{
|
||||
API_AUTO_SERIALIZATION();
|
||||
DECLARE_SCRIPTING_TYPE(TestClassNative);
|
||||
@@ -15,8 +39,19 @@ public:
|
||||
// Test value
|
||||
API_FIELD() int32 SimpleField = 1;
|
||||
|
||||
// Test struct
|
||||
API_FIELD() TestStruct SimpleStruct;
|
||||
|
||||
// Test event
|
||||
API_EVENT() Delegate<int32, Float3, const String&, String&, const Array<TestStruct>&, Array<TestStruct>&> SimpleEvent;
|
||||
|
||||
// Test virtual method
|
||||
API_FUNCTION() virtual int32 Test(const String& str)
|
||||
API_FUNCTION() virtual int32 TestMethod(const String& str)
|
||||
{
|
||||
return str.Length();
|
||||
}
|
||||
|
||||
int32 TestInterfaceMethod(const String& str) override
|
||||
{
|
||||
return str.Length();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user