Merge remote-tracking branch 'origin/master' into 1.10
# Conflicts: # Source/Engine/Level/Prefabs/Prefab.Apply.cpp
This commit is contained in:
@@ -69,8 +69,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
Values.SetReferenceValue(prefabInstance);
|
||||
|
||||
// Display prefab UI (when displaying object inside Prefab Window then display only nested prefabs)
|
||||
var prefabId = prefab.ID;
|
||||
Editor.GetPrefabNestedObject(ref prefabId, ref prefabObjectId, out var nestedPrefabId, out var nestedPrefabObjectId);
|
||||
prefab.GetNestedObject(ref prefabObjectId, out var nestedPrefabId, out var nestedPrefabObjectId);
|
||||
var nestedPrefab = FlaxEngine.Content.Load<Prefab>(nestedPrefabId);
|
||||
var panel = layout.CustomContainer<UniformGridPanel>();
|
||||
panel.CustomControl.Height = 20.0f;
|
||||
|
||||
@@ -1550,9 +1550,9 @@ namespace FlaxEditor
|
||||
// Handle case when Game window is not selected in tab view
|
||||
var dockedTo = gameWin.ParentDockPanel;
|
||||
if (dockedTo != null && dockedTo.SelectedTab != gameWin && dockedTo.SelectedTab != null)
|
||||
result = dockedTo.SelectedTab.Size * root.DpiScale;
|
||||
result = dockedTo.SelectedTab.Size;
|
||||
else
|
||||
result = gameWin.Viewport.Size * root.DpiScale;
|
||||
result = gameWin.Viewport.Size;
|
||||
|
||||
result = Float2.Round(result);
|
||||
}
|
||||
|
||||
@@ -617,21 +617,6 @@ void ManagedEditor::WipeOutLeftoverSceneObjects()
|
||||
ObjectsRemovalService::Flush();
|
||||
}
|
||||
|
||||
void ManagedEditor::GetPrefabNestedObject(const Guid& prefabId, const Guid& prefabObjectId, Guid& outPrefabId, Guid& outPrefabObjectId)
|
||||
{
|
||||
outPrefabId = Guid::Empty;
|
||||
outPrefabObjectId = Guid::Empty;
|
||||
const auto prefab = Content::Load<Prefab>(prefabId);
|
||||
if (!prefab)
|
||||
return;
|
||||
const ISerializable::DeserializeStream** prefabObjectDataPtr = prefab->ObjectsDataCache.TryGet(prefabObjectId);
|
||||
if (!prefabObjectDataPtr)
|
||||
return;
|
||||
const ISerializable::DeserializeStream& prefabObjectData = **prefabObjectDataPtr;
|
||||
JsonTools::GetGuidIfValid(outPrefabId, prefabObjectData, "PrefabID");
|
||||
JsonTools::GetGuidIfValid(outPrefabObjectId, prefabObjectData, "PrefabObjectID");
|
||||
}
|
||||
|
||||
void ManagedEditor::OnEditorAssemblyLoaded(MAssembly* assembly)
|
||||
{
|
||||
ASSERT(!HasManagedInstance());
|
||||
|
||||
@@ -259,7 +259,6 @@ public:
|
||||
API_FUNCTION(Internal) static Array<VisualScriptLocal> GetVisualScriptLocals();
|
||||
API_FUNCTION(Internal) static bool EvaluateVisualScriptLocal(VisualScript* script, API_PARAM(Ref) VisualScriptLocal& local);
|
||||
API_FUNCTION(Internal) static void WipeOutLeftoverSceneObjects();
|
||||
API_FUNCTION(Internal) static void GetPrefabNestedObject(API_PARAM(Ref) const Guid& prefabId, API_PARAM(Ref) const Guid& prefabObjectId, API_PARAM(Out) Guid& outPrefabId, API_PARAM(Out) Guid& outPrefabObjectId);
|
||||
|
||||
private:
|
||||
void OnEditorAssemblyLoaded(MAssembly* assembly);
|
||||
|
||||
@@ -33,12 +33,17 @@ namespace FlaxEditor.Actions
|
||||
// Check if this object comes from another nested prefab (to break link only from the top-level prefab)
|
||||
Item nested;
|
||||
nested.ID = ID;
|
||||
Editor.GetPrefabNestedObject(ref PrefabID, ref PrefabObjectID, out nested.PrefabID, out nested.PrefabObjectID);
|
||||
if (nested.PrefabID != Guid.Empty && nested.PrefabObjectID != Guid.Empty)
|
||||
var prefab = FlaxEngine.Content.Load<Prefab>(PrefabID);
|
||||
if (prefab != null &&
|
||||
prefab.GetNestedObject(ref PrefabObjectID, out nested.PrefabID, out nested.PrefabObjectID) &&
|
||||
nested.PrefabID != Guid.Empty &&
|
||||
nested.PrefabObjectID != Guid.Empty)
|
||||
{
|
||||
nestedPrefabLinks.Add(nested);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize]
|
||||
private readonly bool _isBreak;
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <param name="knowledge">The knowledge container to access.</param>
|
||||
/// <param name="value">The value to set.</param>
|
||||
/// <returns>True if set value value, otherwise false.</returns>
|
||||
/// <returns>True if set value, otherwise false.</returns>
|
||||
public bool Set(BehaviorKnowledge knowledge, object value)
|
||||
{
|
||||
return knowledge != null && knowledge.Set(Path, value);
|
||||
|
||||
@@ -16,27 +16,27 @@ public:
|
||||
/// <summary>
|
||||
/// If checked, audio playback will be disabled in build game. Can be used if game uses custom audio playback engine.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(0), DefaultValue(false), EditorDisplay(\"General\")")
|
||||
API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"General\")")
|
||||
bool DisableAudio = false;
|
||||
|
||||
/// <summary>
|
||||
/// The doppler effect factor. Scale for source and listener velocities. Default is 1.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(100), DefaultValue(1.0f), EditorDisplay(\"General\")")
|
||||
API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"General\")")
|
||||
float DopplerFactor = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// True if mute all audio playback when game has no use focus.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(200), DefaultValue(true), EditorDisplay(\"General\", \"Mute On Focus Loss\")")
|
||||
API_FIELD(Attributes="EditorOrder(200), EditorDisplay(\"General\", \"Mute On Focus Loss\")")
|
||||
bool MuteOnFocusLoss = true;
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables HRTF audio for in-engine processing of 3D audio (if supported by platform).
|
||||
/// If enabled, the user should be using two-channel/headphones audio output and have all other surround virtualization disabled (Atmos, DTS:X, vendor specific, etc.)
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(300), DefaultValue(true), EditorDisplay(\"Spatial Audio\")")
|
||||
bool EnableHRTF = true;
|
||||
API_FIELD(Attributes="EditorOrder(300), EditorDisplay(\"Spatial Audio\")")
|
||||
bool EnableHRTF = false;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -38,6 +38,28 @@
|
||||
}
|
||||
#endif
|
||||
|
||||
const Char* GetOpenALErrorString(int error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case AL_NO_ERROR:
|
||||
return TEXT("AL_NO_ERROR");
|
||||
case AL_INVALID_NAME:
|
||||
return TEXT("AL_INVALID_NAME");
|
||||
case AL_INVALID_ENUM:
|
||||
return TEXT("AL_INVALID_ENUM");
|
||||
case AL_INVALID_VALUE:
|
||||
return TEXT("AL_INVALID_VALUE");
|
||||
case AL_INVALID_OPERATION:
|
||||
return TEXT("AL_INVALID_OPERATION");
|
||||
case AL_OUT_OF_MEMORY:
|
||||
return TEXT("AL_OUT_OF_MEMORY");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return TEXT("???");
|
||||
}
|
||||
|
||||
namespace ALC
|
||||
{
|
||||
struct SourceData
|
||||
@@ -181,6 +203,12 @@ namespace ALC
|
||||
{
|
||||
states.Add({ source->GetState(), source->GetTime() });
|
||||
source->Stop();
|
||||
if (source->SourceID)
|
||||
{
|
||||
alDeleteSources(1, &source->SourceID);
|
||||
ALC_CHECK_ERROR(alDeleteSources);
|
||||
source->SourceID = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,28 +281,6 @@ ALenum GetOpenALBufferFormat(uint32 numChannels, uint32 bitDepth)
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Char* GetOpenALErrorString(int error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case AL_NO_ERROR:
|
||||
return TEXT("AL_NO_ERROR");
|
||||
case AL_INVALID_NAME:
|
||||
return TEXT("AL_INVALID_NAME");
|
||||
case AL_INVALID_ENUM:
|
||||
return TEXT("AL_INVALID_ENUM");
|
||||
case AL_INVALID_VALUE:
|
||||
return TEXT("AL_INVALID_VALUE");
|
||||
case AL_INVALID_OPERATION:
|
||||
return TEXT("AL_INVALID_OPERATION");
|
||||
case AL_OUT_OF_MEMORY:
|
||||
return TEXT("AL_OUT_OF_MEMORY");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return TEXT("???");
|
||||
}
|
||||
|
||||
void AudioBackendOAL::Listener_Reset()
|
||||
{
|
||||
alListenerf(AL_GAIN, Audio::GetVolume());
|
||||
|
||||
@@ -3158,6 +3158,13 @@ Variant Variant::Parse(const StringView& text, const VariantType& type)
|
||||
return result;
|
||||
}
|
||||
|
||||
Variant Variant::Typename(const StringAnsiView& value)
|
||||
{
|
||||
Variant result;
|
||||
result.SetTypename(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||
{
|
||||
if (v.Type == to)
|
||||
|
||||
@@ -410,6 +410,8 @@ public:
|
||||
return MoveTemp(v);
|
||||
}
|
||||
|
||||
static Variant Typename(const StringAnsiView& value);
|
||||
|
||||
static bool CanCast(const Variant& v, const VariantType& to);
|
||||
static Variant Cast(const Variant& v, const VariantType& to);
|
||||
static bool NearEqual(const Variant& a, const Variant& b, float epsilon = 1e-6f);
|
||||
|
||||
@@ -30,7 +30,7 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(Screen);
|
||||
API_PROPERTY() static void SetIsFullscreen(bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the window size.
|
||||
/// Gets the window size (in screen-space, includes DPI scale).
|
||||
/// </summary>
|
||||
/// <returns>The value</returns>
|
||||
API_PROPERTY() static Float2 GetSize();
|
||||
@@ -50,7 +50,7 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(Screen);
|
||||
API_FUNCTION() static Float2 GameViewportToScreen(const Float2& viewportPos);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the window size.
|
||||
/// Sets the window size (in screen-space, includes DPI scale).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Resizing may not happen immediately. It will be performed before next frame rendering.
|
||||
|
||||
@@ -191,8 +191,11 @@ bool TerrainMaterialShader::Load()
|
||||
psDesc.DepthFunc = ComparisonFunc::Less;
|
||||
psDesc.HS = nullptr;
|
||||
psDesc.DS = nullptr;
|
||||
// TODO: masked terrain materials (depth pass should clip holes)
|
||||
psDesc.PS = nullptr;
|
||||
if (EnumHasAnyFlags(_info.UsageFlags, MaterialUsageFlags::UseMask))
|
||||
{
|
||||
psDesc.PS = _shader->GetPS("PS_Depth");
|
||||
}
|
||||
_cache.Depth.Init(psDesc);
|
||||
|
||||
return false;
|
||||
|
||||
@@ -38,6 +38,10 @@
|
||||
|
||||
#define ACTOR_ORIENTATION_EPSILON 0.000000001f
|
||||
|
||||
// Start loop over actor children/scripts from the beginning to account for any newly added or removed actors.
|
||||
#define ACTOR_LOOP_START_MODIFIED_HIERARCHY() _isHierarchyDirty = false
|
||||
#define ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY() if (_isHierarchyDirty) { _isHierarchyDirty = false; i = -1; }
|
||||
|
||||
namespace
|
||||
{
|
||||
Actor* GetChildByPrefabObjectId(Actor* a, const Guid& prefabObjectId)
|
||||
@@ -75,6 +79,7 @@ Actor::Actor(const SpawnParams& params)
|
||||
, _isActiveInHierarchy(true)
|
||||
, _isPrefabRoot(false)
|
||||
, _isEnabled(false)
|
||||
, _isHierarchyDirty(false)
|
||||
, _layer(0)
|
||||
, _staticFlags(StaticFlags::FullyStatic)
|
||||
, _localTransform(Transform::Identity)
|
||||
@@ -100,7 +105,7 @@ void Actor::SetSceneInHierarchy(Scene* scene)
|
||||
|
||||
for (int32 i = 0; i < Children.Count(); i++)
|
||||
{
|
||||
Children[i]->SetSceneInHierarchy(scene);
|
||||
Children.Get()[i]->SetSceneInHierarchy(scene);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,9 +115,11 @@ void Actor::OnEnableInHierarchy()
|
||||
{
|
||||
OnEnable();
|
||||
|
||||
ACTOR_LOOP_START_MODIFIED_HIERARCHY();
|
||||
for (int32 i = 0; i < Children.Count(); i++)
|
||||
{
|
||||
Children[i]->OnEnableInHierarchy();
|
||||
Children.Get()[i]->OnEnableInHierarchy();
|
||||
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,9 +128,11 @@ void Actor::OnDisableInHierarchy()
|
||||
{
|
||||
if (IsActiveInHierarchy() && GetScene() && _isEnabled)
|
||||
{
|
||||
ACTOR_LOOP_START_MODIFIED_HIERARCHY();
|
||||
for (int32 i = 0; i < Children.Count(); i++)
|
||||
{
|
||||
Children[i]->OnDisableInHierarchy();
|
||||
Children.Get()[i]->OnDisableInHierarchy();
|
||||
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
|
||||
}
|
||||
|
||||
OnDisable();
|
||||
@@ -166,6 +175,7 @@ void Actor::OnDeleteObject()
|
||||
{
|
||||
// Unlink from the parent
|
||||
_parent->Children.RemoveKeepOrder(this);
|
||||
_parent->_isHierarchyDirty = true;
|
||||
_parent = nullptr;
|
||||
_scene = nullptr;
|
||||
}
|
||||
@@ -174,6 +184,7 @@ void Actor::OnDeleteObject()
|
||||
{
|
||||
// Unlink from the parent
|
||||
_parent->Children.RemoveKeepOrder(this);
|
||||
_parent->_isHierarchyDirty = true;
|
||||
_parent = nullptr;
|
||||
_scene = nullptr;
|
||||
}
|
||||
@@ -193,7 +204,7 @@ void Actor::OnDeleteObject()
|
||||
#endif
|
||||
for (int32 i = 0; i < Children.Count(); i++)
|
||||
{
|
||||
auto e = Children[i];
|
||||
auto e = Children.Get()[i];
|
||||
ASSERT(e->_parent == this);
|
||||
e->_parent = nullptr;
|
||||
e->DeleteObject();
|
||||
@@ -209,7 +220,7 @@ void Actor::OnDeleteObject()
|
||||
#endif
|
||||
for (int32 i = 0; i < Scripts.Count(); i++)
|
||||
{
|
||||
auto script = Scripts[i];
|
||||
auto script = Scripts.Get()[i];
|
||||
ASSERT(script->_parent == this);
|
||||
if (script->_wasAwakeCalled)
|
||||
{
|
||||
@@ -290,6 +301,7 @@ void Actor::SetParent(Actor* value, bool worldPositionsStays, bool canBreakPrefa
|
||||
if (_parent)
|
||||
{
|
||||
_parent->Children.RemoveKeepOrder(this);
|
||||
_parent->_isHierarchyDirty = true;
|
||||
}
|
||||
|
||||
// Set value
|
||||
@@ -299,6 +311,7 @@ void Actor::SetParent(Actor* value, bool worldPositionsStays, bool canBreakPrefa
|
||||
if (_parent)
|
||||
{
|
||||
_parent->Children.Add(this);
|
||||
_parent->_isHierarchyDirty = true;
|
||||
}
|
||||
|
||||
// Sync scene change if need to
|
||||
@@ -371,8 +384,6 @@ void Actor::SetOrderInParent(int32 index)
|
||||
{
|
||||
if (!_parent)
|
||||
return;
|
||||
|
||||
// Cache data
|
||||
auto& parentChildren = _parent->Children;
|
||||
const int32 currentIndex = parentChildren.Find(this);
|
||||
ASSERT(currentIndex != INVALID_INDEX);
|
||||
@@ -381,8 +392,6 @@ void Actor::SetOrderInParent(int32 index)
|
||||
if (currentIndex != index)
|
||||
{
|
||||
parentChildren.RemoveAtKeepOrder(currentIndex);
|
||||
|
||||
// Check if index is invalid
|
||||
if (index < 0 || index >= parentChildren.Count())
|
||||
{
|
||||
// Append at the end
|
||||
@@ -393,6 +402,7 @@ void Actor::SetOrderInParent(int32 index)
|
||||
// Change order
|
||||
parentChildren.Insert(index, this);
|
||||
}
|
||||
_parent->_isHierarchyDirty = true;
|
||||
|
||||
// Fire event
|
||||
OnOrderInParentChanged();
|
||||
@@ -895,9 +905,7 @@ void Actor::BreakPrefabLink()
|
||||
|
||||
void Actor::Initialize()
|
||||
{
|
||||
#if ENABLE_ASSERTION
|
||||
CHECK(!IsDuringPlay());
|
||||
#endif
|
||||
CHECK_DEBUG(!IsDuringPlay());
|
||||
|
||||
// Cache
|
||||
if (_parent)
|
||||
@@ -911,9 +919,7 @@ void Actor::Initialize()
|
||||
|
||||
void Actor::BeginPlay(SceneBeginData* data)
|
||||
{
|
||||
#if ENABLE_ASSERTION
|
||||
CHECK(!IsDuringPlay());
|
||||
#endif
|
||||
CHECK_DEBUG(!IsDuringPlay());
|
||||
|
||||
// Set flag
|
||||
Flags |= ObjectFlags::IsDuringPlay;
|
||||
@@ -921,11 +927,15 @@ void Actor::BeginPlay(SceneBeginData* data)
|
||||
OnBeginPlay();
|
||||
|
||||
// Update scripts
|
||||
ACTOR_LOOP_START_MODIFIED_HIERARCHY();
|
||||
for (int32 i = 0; i < Scripts.Count(); i++)
|
||||
{
|
||||
auto e = Scripts.Get()[i];
|
||||
if (!e->IsDuringPlay())
|
||||
{
|
||||
e->BeginPlay(data);
|
||||
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
|
||||
}
|
||||
}
|
||||
|
||||
// Update children
|
||||
@@ -933,7 +943,10 @@ void Actor::BeginPlay(SceneBeginData* data)
|
||||
{
|
||||
auto e = Children.Get()[i];
|
||||
if (!e->IsDuringPlay())
|
||||
{
|
||||
e->BeginPlay(data);
|
||||
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
|
||||
}
|
||||
}
|
||||
|
||||
// Fire events for scripting
|
||||
@@ -945,9 +958,7 @@ void Actor::BeginPlay(SceneBeginData* data)
|
||||
|
||||
void Actor::EndPlay()
|
||||
{
|
||||
#if ENABLE_ASSERTION
|
||||
CHECK(IsDuringPlay());
|
||||
#endif
|
||||
CHECK_DEBUG(IsDuringPlay());
|
||||
|
||||
// Fire event for scripting
|
||||
if (IsActiveInHierarchy() && GetScene())
|
||||
@@ -974,19 +985,27 @@ void Actor::EndPlay()
|
||||
Flags &= ~ObjectFlags::IsDuringPlay;
|
||||
|
||||
// Call event deeper
|
||||
ACTOR_LOOP_START_MODIFIED_HIERARCHY();
|
||||
for (int32 i = 0; i < Children.Count(); i++)
|
||||
{
|
||||
auto e = Children.Get()[i];
|
||||
if (e->IsDuringPlay())
|
||||
{
|
||||
e->EndPlay();
|
||||
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
|
||||
}
|
||||
}
|
||||
|
||||
// Inform attached scripts
|
||||
ACTOR_LOOP_START_MODIFIED_HIERARCHY();
|
||||
for (int32 i = 0; i < Scripts.Count(); i++)
|
||||
{
|
||||
auto e = Scripts.Get()[i];
|
||||
if (e->IsDuringPlay())
|
||||
{
|
||||
e->EndPlay();
|
||||
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup managed object
|
||||
@@ -1191,31 +1210,34 @@ void Actor::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
|
||||
void Actor::OnEnable()
|
||||
{
|
||||
#if ENABLE_ASSERTION
|
||||
CHECK(!_isEnabled);
|
||||
#endif
|
||||
CHECK_DEBUG(!_isEnabled);
|
||||
_isEnabled = true;
|
||||
|
||||
ACTOR_LOOP_START_MODIFIED_HIERARCHY();
|
||||
for (int32 i = 0; i < Scripts.Count(); i++)
|
||||
{
|
||||
auto script = Scripts[i];
|
||||
if (script->GetEnabled() && !script->_wasStartCalled)
|
||||
{
|
||||
script->Start();
|
||||
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
|
||||
}
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < Scripts.Count(); i++)
|
||||
{
|
||||
auto script = Scripts[i];
|
||||
if (script->GetEnabled() && !script->_wasEnableCalled)
|
||||
{
|
||||
script->Enable();
|
||||
ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Actor::OnDisable()
|
||||
{
|
||||
#if ENABLE_ASSERTION
|
||||
CHECK(_isEnabled);
|
||||
#endif
|
||||
CHECK_DEBUG(_isEnabled);
|
||||
_isEnabled = false;
|
||||
|
||||
for (int32 i = Scripts.Count() - 1; i >= 0; i--)
|
||||
@@ -1302,12 +1324,8 @@ void Actor::OnLayerChanged()
|
||||
BoundingBox Actor::GetBoxWithChildren() const
|
||||
{
|
||||
BoundingBox result = GetBox();
|
||||
|
||||
for (int32 i = 0; i < Children.Count(); i++)
|
||||
{
|
||||
BoundingBox::Merge(result, Children[i]->GetBoxWithChildren(), result);
|
||||
}
|
||||
|
||||
BoundingBox::Merge(result, Children.Get()[i]->GetBoxWithChildren(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1322,9 +1340,7 @@ BoundingBox Actor::GetEditorBoxChildren() const
|
||||
{
|
||||
BoundingBox result = GetEditorBox();
|
||||
for (int32 i = 0; i < Children.Count(); i++)
|
||||
{
|
||||
BoundingBox::Merge(result, Children[i]->GetEditorBoxChildren(), result);
|
||||
}
|
||||
BoundingBox::Merge(result, Children.Get()[i]->GetEditorBoxChildren(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ protected:
|
||||
uint16 _isActiveInHierarchy : 1;
|
||||
uint16 _isPrefabRoot : 1;
|
||||
uint16 _isEnabled : 1;
|
||||
uint16 _isHierarchyDirty : 1;
|
||||
uint16 _drawNoCulling : 1;
|
||||
uint16 _drawCategory : 4;
|
||||
byte _layer;
|
||||
|
||||
@@ -246,11 +246,16 @@ Ray Camera::ConvertMouseToRay(const Float2& mousePosition, const Viewport& viewp
|
||||
Viewport Camera::GetViewport() const
|
||||
{
|
||||
Viewport result = Viewport(Float2::Zero);
|
||||
float dpiScale = Platform::GetDpiScale();
|
||||
|
||||
#if USE_EDITOR
|
||||
// Editor
|
||||
if (Editor::Managed)
|
||||
{
|
||||
result.Size = Editor::Managed->GetGameWindowSize();
|
||||
if (auto* window = Editor::Managed->GetGameWindow())
|
||||
dpiScale = window->GetDpiScale();
|
||||
}
|
||||
#else
|
||||
// Game
|
||||
auto mainWin = Engine::MainWindow;
|
||||
@@ -258,9 +263,13 @@ Viewport Camera::GetViewport() const
|
||||
{
|
||||
const auto size = mainWin->GetClientSize();
|
||||
result.Size = size;
|
||||
dpiScale = mainWin->GetDpiScale();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Remove DPI scale (game viewport coords are unscaled)
|
||||
result.Size /= dpiScale;
|
||||
|
||||
// Fallback to the default value
|
||||
if (result.Size.MinValue() <= ZeroTolerance)
|
||||
result.Size = Float2(1280, 720);
|
||||
|
||||
@@ -1655,13 +1655,16 @@ Array<Actor*> Level::GetActors(const MClass* type, bool activeOnly)
|
||||
return result;
|
||||
}
|
||||
|
||||
Array<Script*> Level::GetScripts(const MClass* type)
|
||||
Array<Script*> Level::GetScripts(const MClass* type, Actor* root)
|
||||
{
|
||||
Array<Script*> result;
|
||||
CHECK_RETURN(type, result);
|
||||
ScopeLock lock(ScenesLock);
|
||||
for (int32 i = 0; i < Scenes.Count(); i++)
|
||||
::GetScripts(type, type->IsInterface(), Scenes[i], result);
|
||||
const bool isInterface = type->IsInterface();
|
||||
if (root)
|
||||
::GetScripts(type, isInterface, root, result);
|
||||
else for (int32 i = 0; i < Scenes.Count(); i++)
|
||||
::GetScripts(type, isInterface, Scenes[i], result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -102,13 +102,14 @@ namespace FlaxEngine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds all the scripts of the given type in all the loaded scenes.
|
||||
/// Finds all the scripts of the given type in an actor or all the loaded scenes.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the object.</typeparam>
|
||||
/// <param name="root">The root to find scripts. If null, will search in all scenes</param>
|
||||
/// <returns>Found scripts list.</returns>
|
||||
public static T[] GetScripts<T>() where T : Script
|
||||
public static T[] GetScripts<T>(Actor root = null) where T : Script
|
||||
{
|
||||
var scripts = GetScripts(typeof(T));
|
||||
var scripts = GetScripts(typeof(T), root);
|
||||
if (typeof(T) == typeof(Script))
|
||||
return (T[])scripts;
|
||||
if (scripts.Length == 0)
|
||||
|
||||
@@ -469,11 +469,12 @@ public:
|
||||
API_FUNCTION() static Array<Actor*> GetActors(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, bool activeOnly = false);
|
||||
|
||||
/// <summary>
|
||||
/// Finds all the scripts of the given type in all the loaded scenes.
|
||||
/// Finds all the scripts of the given type in an actor or all the loaded scenes.
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the script to search for. Includes any scripts derived from the type.</param>
|
||||
/// <param name="root">The root to find scripts. If null, will search in all scenes.</param>
|
||||
/// <returns>Found scripts list.</returns>
|
||||
API_FUNCTION() static Array<Script*> GetScripts(API_PARAM(Attributes="TypeReference(typeof(Script))") const MClass* type);
|
||||
API_FUNCTION() static Array<Script*> GetScripts(API_PARAM(Attributes="TypeReference(typeof(Script))") const MClass* type, Actor* root = nullptr);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find scene with given ID.
|
||||
|
||||
@@ -712,7 +712,7 @@ bool Prefab::ApplyAll(Actor* targetActor)
|
||||
for (int32 i = 0; i < nestedPrefabIds.Count(); i++)
|
||||
{
|
||||
const auto nestedPrefab = Content::LoadAsync<Prefab>(nestedPrefabIds[i]);
|
||||
if (nestedPrefab && nestedPrefab != this && (nestedPrefab->Flags & ObjectFlags::WasMarkedToDelete) == ObjectFlags::None)
|
||||
if (nestedPrefab && nestedPrefab != this && EnumHasNoneFlags(nestedPrefab->Flags, ObjectFlags::WasMarkedToDelete))
|
||||
{
|
||||
allPrefabs.Add(nestedPrefab);
|
||||
}
|
||||
@@ -822,7 +822,37 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
rapidjson_flax::StringBuffer dataBuffer;
|
||||
{
|
||||
CompactJsonWriter writerObj(dataBuffer);
|
||||
PrefabInstanceData::SerializeObjects(*targetObjects.Value, writerObj);
|
||||
JsonWriter& writer = writerObj;
|
||||
writer.StartArray();
|
||||
for (int32 i = 0; i < targetObjects->Count(); i++)
|
||||
{
|
||||
SceneObject* obj = targetObjects.Value->At(i);
|
||||
|
||||
// Check the whole chain of prefab references to be valid for this object
|
||||
bool brokenPrefab = false;
|
||||
Guid nestedPrefabId = obj->GetPrefabID(), nestedPrefabObjectId = obj->GetPrefabObjectID();
|
||||
while (!brokenPrefab && nestedPrefabId.IsValid() && nestedPrefabObjectId.IsValid())
|
||||
{
|
||||
auto prefab = Content::Load<Prefab>(nestedPrefabId);
|
||||
if (prefab)
|
||||
{
|
||||
prefab->GetNestedObject(nestedPrefabObjectId, nestedPrefabId, nestedPrefabObjectId);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Warning, "Missing prefab {0}.", nestedPrefabId);
|
||||
brokenPrefab = true;
|
||||
}
|
||||
}
|
||||
if (brokenPrefab)
|
||||
{
|
||||
LOG(Warning, "Broken prefab reference on object {0}. Breaking linkage to inline object inside prefab.", GetObjectName(obj));
|
||||
obj->BreakPrefabLink();
|
||||
}
|
||||
|
||||
writer.SceneObject(obj);
|
||||
}
|
||||
writer.EndArray();
|
||||
}
|
||||
|
||||
// Parse json document and modify serialized data to extract only modified properties
|
||||
@@ -851,7 +881,7 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
SceneObject* obj = targetObjects.Value->At(i);
|
||||
auto data = it->GetObject();
|
||||
|
||||
// Check if object is from that prefab
|
||||
// Check if object is from this prefab
|
||||
if (obj->GetPrefabID() == prefabId)
|
||||
{
|
||||
if (!obj->GetPrefabObjectID().IsValid())
|
||||
@@ -925,7 +955,7 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
{
|
||||
const SceneObject* obj = targetObjects->At(i);
|
||||
|
||||
// Check if object is from that prefab
|
||||
// Check if object is from this prefab
|
||||
if (obj->GetPrefabID() == prefabId)
|
||||
{
|
||||
// Map prefab instance to existing prefab object
|
||||
|
||||
@@ -94,6 +94,24 @@ SceneObject* Prefab::GetDefaultInstance(const Guid& objectId)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Prefab::GetNestedObject(const Guid& objectId, Guid& outPrefabId, Guid& outObjectId) const
|
||||
{
|
||||
if (WaitForLoaded())
|
||||
return false;
|
||||
bool result = false;
|
||||
Guid result1 = Guid::Empty, result2 = Guid::Empty;
|
||||
const ISerializable::DeserializeStream** prefabObjectDataPtr = ObjectsDataCache.TryGet(objectId);
|
||||
if (prefabObjectDataPtr)
|
||||
{
|
||||
const ISerializable::DeserializeStream& prefabObjectData = **prefabObjectDataPtr;
|
||||
result = JsonTools::GetGuidIfValid(result1, prefabObjectData, "PrefabID") &&
|
||||
JsonTools::GetGuidIfValid(result2, prefabObjectData, "PrefabObjectID");
|
||||
}
|
||||
outPrefabId = result1;
|
||||
outObjectId = result2;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Prefab::DeleteDefaultInstance()
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
@@ -70,6 +70,15 @@ public:
|
||||
/// <returns>The object of the prefab loaded from the prefab. Contains the default values. It's not added to gameplay but deserialized with postLoad and init event fired.</returns>
|
||||
API_FUNCTION() SceneObject* GetDefaultInstance(API_PARAM(Ref) const Guid& objectId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reference to the other nested prefab for a specific prefab object.
|
||||
/// </summary>
|
||||
/// <param name="objectId">The ID of the object in this prefab.</param>
|
||||
/// <param name="outPrefabId">Result ID of the prefab asset referenced by the given object.</param>
|
||||
/// <param name="outObjectId">Result ID of the prefab object referenced by the given object.</param>
|
||||
/// <returns>True if got valid reference, otherwise false.</returns>
|
||||
API_FUNCTION() bool GetNestedObject(API_PARAM(Ref) const Guid& objectId, API_PARAM(Out) Guid& outPrefabId, API_PARAM(Out) Guid& outObjectId) const;
|
||||
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// Applies the difference from the prefab object instance, saves the changes and synchronizes them with the active instances of the prefab asset.
|
||||
|
||||
@@ -246,9 +246,7 @@ SceneObject* SceneObjectsFactory::Spawn(Context& context, const ISerializable::D
|
||||
|
||||
void SceneObjectsFactory::Deserialize(Context& context, SceneObject* obj, ISerializable::DeserializeStream& stream)
|
||||
{
|
||||
#if ENABLE_ASSERTION
|
||||
CHECK(obj);
|
||||
#endif
|
||||
CHECK_DEBUG(obj);
|
||||
ISerializeModifier* modifier = context.GetModifier();
|
||||
LogContextScope logContext(obj->GetID());
|
||||
|
||||
|
||||
@@ -38,6 +38,16 @@ namespace FlaxEngine
|
||||
_typeName = typeName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit cast operator from type name to string.
|
||||
/// </summary>
|
||||
/// <param name="s">The soft type reference.</param>
|
||||
/// <returns>The type name.</returns>
|
||||
public static implicit operator string(SoftTypeReference s)
|
||||
{
|
||||
return s._typeName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soft type reference from full name.
|
||||
/// </summary>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/// The soft reference to the scripting type contained in the scripting assembly.
|
||||
/// </summary>
|
||||
template<typename T = ScriptingObject>
|
||||
API_STRUCT(InBuild) struct SoftTypeReference
|
||||
API_STRUCT(InBuild, MarshalAs=StringAnsi) struct SoftTypeReference
|
||||
{
|
||||
protected:
|
||||
StringAnsi _typeName;
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE SoftTypeReference& operator=(const StringAnsiView& s)
|
||||
FORCE_INLINE SoftTypeReference& operator=(const StringAnsiView& s) noexcept
|
||||
{
|
||||
_typeName = s;
|
||||
return *this;
|
||||
@@ -95,6 +95,16 @@ public:
|
||||
return _typeName.HasChars();
|
||||
}
|
||||
|
||||
operator StringAnsi() const
|
||||
{
|
||||
return _typeName;
|
||||
}
|
||||
|
||||
String ToString() const
|
||||
{
|
||||
return _typeName.ToString();
|
||||
}
|
||||
|
||||
public:
|
||||
// Gets the type full name (eg. FlaxEngine.Actor).
|
||||
StringAnsiView GetTypeName() const
|
||||
|
||||
@@ -86,7 +86,7 @@ public:
|
||||
|
||||
void String(const StringAnsi& value)
|
||||
{
|
||||
String(value.Get(), static_cast<unsigned>(value.Length()));
|
||||
String(value.Get(), value.Length());
|
||||
}
|
||||
|
||||
FORCE_INLINE void RawValue(const StringAnsi& str)
|
||||
|
||||
@@ -614,4 +614,78 @@ TEST_CASE("Prefabs")
|
||||
instanceB->DeleteObject();
|
||||
Content::DeleteAsset(prefab);
|
||||
}
|
||||
SECTION("Test Applying Prefab With Missing Nested Prefab")
|
||||
{
|
||||
// https://github.com/FlaxEngine/FlaxEngine/issues/3244
|
||||
|
||||
// Create Prefab B with just root object
|
||||
AssetReference<Prefab> prefabB = Content::CreateVirtualAsset<Prefab>();
|
||||
REQUIRE(prefabB);
|
||||
Guid id;
|
||||
Guid::Parse("25dbe4b0416be0777a6ce59e8788b10f", id);
|
||||
prefabB->ChangeID(id);
|
||||
auto prefabBInit = prefabB->Init(Prefab::TypeName,
|
||||
"["
|
||||
"{"
|
||||
"\"ID\": \"aac6b9644492fbca1a6ab0a7904a557e\","
|
||||
"\"TypeName\": \"FlaxEngine.ExponentialHeightFog\","
|
||||
"\"Name\": \"Prefab B.Root\""
|
||||
"}"
|
||||
"]");
|
||||
REQUIRE(!prefabBInit);
|
||||
|
||||
// Create Prefab A with nested Prefab B attached to the root
|
||||
AssetReference<Prefab> prefabA = Content::CreateVirtualAsset<Prefab>();
|
||||
REQUIRE(prefabA);
|
||||
Guid::Parse("4cb744714f746e31855f41815612d14b", id);
|
||||
prefabA->ChangeID(id);
|
||||
auto prefabAInit = prefabA->Init(Prefab::TypeName,
|
||||
"["
|
||||
"{"
|
||||
"\"ID\": \"244274a04cc60d56a2f024bfeef5772d\","
|
||||
"\"TypeName\": \"FlaxEngine.SpotLight\","
|
||||
"\"Name\": \"Prefab A.Root\""
|
||||
"},"
|
||||
"{"
|
||||
"\"ID\": \"1e51f1094f430733333f8280e78dfcc3\","
|
||||
"\"PrefabID\": \"25dbe4b0416be0777a6ce59e8788b10f\","
|
||||
"\"PrefabObjectID\": \"aac6b9644492fbca1a6ab0a7904a557e\","
|
||||
"\"ParentID\": \"244274a04cc60d56a2f024bfeef5772d\""
|
||||
"}"
|
||||
"]");
|
||||
REQUIRE(!prefabAInit);
|
||||
|
||||
// Spawn test instances of both prefabs
|
||||
ScriptingObjectReference<Actor> instanceA = PrefabManager::SpawnPrefab(prefabA);
|
||||
ScriptingObjectReference<Actor> instanceB = PrefabManager::SpawnPrefab(prefabB);
|
||||
|
||||
// Delete nested prefab
|
||||
Content::DeleteAsset(prefabB);
|
||||
|
||||
// Apply instance A and verify it's fine even tho prefab B doesn't exist anymore
|
||||
bool applyResult = PrefabManager::ApplyAll(instanceA);
|
||||
REQUIRE(!applyResult);
|
||||
|
||||
// Check state of objects
|
||||
REQUIRE(instanceA);
|
||||
REQUIRE(instanceA->Children.Count() == 1);
|
||||
REQUIRE(instanceA->Children[0] != nullptr);
|
||||
REQUIRE(instanceA->Children[0]->Is<ExponentialHeightFog>());
|
||||
REQUIRE(instanceB);
|
||||
REQUIRE(instanceB->Is<ExponentialHeightFog>());
|
||||
|
||||
// Verify if prefab has new data to properly spawn another prefab
|
||||
ScriptingObjectReference<Actor> instanceC = PrefabManager::SpawnPrefab(prefabA);
|
||||
REQUIRE(instanceC);
|
||||
REQUIRE(instanceC->Children.Count() == 1);
|
||||
REQUIRE(instanceC->Children[0] != nullptr);
|
||||
REQUIRE(instanceC->Children[0]->Is<ExponentialHeightFog>());
|
||||
|
||||
// Cleanup
|
||||
instanceA->DeleteObject();
|
||||
instanceB->DeleteObject();
|
||||
instanceC->DeleteObject();
|
||||
Content::DeleteAsset(prefabA);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
#include "Engine/Scripting/SerializableScriptingObject.h"
|
||||
#include "Engine/Scripting/SoftTypeReference.h"
|
||||
#include "Engine/Content/SceneReference.h"
|
||||
|
||||
// Test default values init on fields.
|
||||
API_STRUCT(NoDefault) struct TestDefaultValues
|
||||
@@ -124,10 +126,17 @@ API_STRUCT(NoDefault) struct TestStruct : public ISerializable
|
||||
API_FIELD() Float3 Vector = Float3::One;
|
||||
// Ref
|
||||
API_FIELD() ScriptingObject* Object = nullptr;
|
||||
// Soft Type Ref
|
||||
API_FIELD() SoftTypeReference<ScriptingObject> SoftTypeRef;
|
||||
// Scene Ref
|
||||
API_FIELD() SceneReference SceneRef;
|
||||
|
||||
friend bool operator==(const TestStruct& lhs, const TestStruct& rhs)
|
||||
{
|
||||
return lhs.Vector == rhs.Vector && lhs.Object == rhs.Object;
|
||||
return lhs.Vector == rhs.Vector &&
|
||||
lhs.Object == rhs.Object &&
|
||||
lhs.SoftTypeRef == rhs.SoftTypeRef &&
|
||||
lhs.SceneRef == rhs.SceneRef;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
1
Source/ThirdParty/rapidjson/writer.h
vendored
1
Source/ThirdParty/rapidjson/writer.h
vendored
@@ -202,7 +202,6 @@ public:
|
||||
}
|
||||
|
||||
bool String(const Ch* str, SizeType length, bool copy = false) {
|
||||
RAPIDJSON_ASSERT(str != 0);
|
||||
(void)copy;
|
||||
Prefix(kStringType);
|
||||
return EndValue(WriteString(str, length));
|
||||
|
||||
@@ -1605,41 +1605,43 @@ namespace Flax.Build.Bindings
|
||||
if (fieldInfo.IsStatic || fieldInfo.IsConstexpr)
|
||||
continue;
|
||||
|
||||
var marshalType = fieldInfo.Type;
|
||||
var apiType = FindApiTypeInfo(buildData, marshalType, structureInfo);
|
||||
if (apiType != null && apiType.MarshalAs != null)
|
||||
marshalType = apiType.MarshalAs;
|
||||
bool internalType = apiType is StructureInfo fieldStructureInfo && UseCustomMarshalling(buildData, fieldStructureInfo, structureInfo);
|
||||
string type, originalType;
|
||||
if (fieldInfo.Type.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
|
||||
if (marshalType.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
|
||||
{
|
||||
// Fixed-size array that needs to be inlined into structure instead of passing it as managed array
|
||||
fieldInfo.Type.IsArray = false;
|
||||
originalType = type = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo);
|
||||
fieldInfo.Type.IsArray = true;
|
||||
marshalType.IsArray = false;
|
||||
originalType = type = GenerateCSharpNativeToManaged(buildData, marshalType, structureInfo);
|
||||
marshalType.IsArray = true;
|
||||
}
|
||||
else
|
||||
originalType = type = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo);
|
||||
|
||||
originalType = type = GenerateCSharpNativeToManaged(buildData, marshalType, structureInfo);
|
||||
if (apiType != null && apiType.MarshalAs != null)
|
||||
Log.Error("marshal as into type: " + type);
|
||||
structContents.Append(structIndent).Append("public ");
|
||||
|
||||
var apiType = FindApiTypeInfo(buildData, fieldInfo.Type, structureInfo);
|
||||
bool internalType = apiType is StructureInfo fieldStructureInfo && UseCustomMarshalling(buildData, fieldStructureInfo, structureInfo);
|
||||
string internalTypeMarshaller = "";
|
||||
|
||||
if (fieldInfo.Type.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
|
||||
if (marshalType.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
|
||||
{
|
||||
#if USE_NETCORE
|
||||
if (GenerateCSharpUseFixedBuffer(originalType))
|
||||
{
|
||||
// Use fixed statement with primitive types of buffers
|
||||
structContents.Append($"fixed {originalType} {fieldInfo.Name}0[{fieldInfo.Type.ArraySize}];").AppendLine();
|
||||
structContents.Append($"fixed {originalType} {fieldInfo.Name}0[{marshalType.ArraySize}];").AppendLine();
|
||||
|
||||
// Copy fixed-size array
|
||||
toManagedContent.AppendLine($"FlaxEngine.Utils.MemoryCopy(new IntPtr(managed.{fieldInfo.Name}0), new IntPtr(unmanaged.{fieldInfo.Name}0), sizeof({originalType}) * {fieldInfo.Type.ArraySize}ul);");
|
||||
toNativeContent.AppendLine($"FlaxEngine.Utils.MemoryCopy(new IntPtr(unmanaged.{fieldInfo.Name}0), new IntPtr(managed.{fieldInfo.Name}0), sizeof({originalType}) * {fieldInfo.Type.ArraySize}ul);");
|
||||
toManagedContent.AppendLine($"FlaxEngine.Utils.MemoryCopy(new IntPtr(managed.{fieldInfo.Name}0), new IntPtr(unmanaged.{fieldInfo.Name}0), sizeof({originalType}) * {marshalType.ArraySize}ul);");
|
||||
toNativeContent.AppendLine($"FlaxEngine.Utils.MemoryCopy(new IntPtr(unmanaged.{fieldInfo.Name}0), new IntPtr(managed.{fieldInfo.Name}0), sizeof({originalType}) * {marshalType.ArraySize}ul);");
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// Padding in structs for fixed-size array
|
||||
structContents.Append(type).Append(' ').Append(fieldInfo.Name).Append("0;").AppendLine();
|
||||
for (int i = 1; i < fieldInfo.Type.ArraySize; i++)
|
||||
for (int i = 1; i < marshalType.ArraySize; i++)
|
||||
{
|
||||
GenerateCSharpAttributes(buildData, structContents, structIndent, structureInfo, fieldInfo, fieldInfo.IsStatic);
|
||||
structContents.Append(structIndent).Append("public ");
|
||||
@@ -1649,7 +1651,7 @@ namespace Flax.Build.Bindings
|
||||
// Copy fixed-size array item one-by-one
|
||||
if (fieldInfo.Access == AccessLevel.Public || fieldInfo.Access == AccessLevel.Internal)
|
||||
{
|
||||
for (int i = 0; i < fieldInfo.Type.ArraySize; i++)
|
||||
for (int i = 0; i < marshalType.ArraySize; i++)
|
||||
{
|
||||
toManagedContent.AppendLine($"managed.{fieldInfo.Name}{i} = unmanaged.{fieldInfo.Name}{i};");
|
||||
toNativeContent.AppendLine($"unmanaged.{fieldInfo.Name}{i} = managed.{fieldInfo.Name}{i};");
|
||||
@@ -1664,23 +1666,23 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fieldInfo.Type.IsObjectRef || fieldInfo.Type.Type == "Dictionary")
|
||||
if (marshalType.IsObjectRef || marshalType.Type == "Dictionary")
|
||||
type = "IntPtr";
|
||||
else if (fieldInfo.Type.IsPtr && !originalType.EndsWith("*"))
|
||||
else if (marshalType.IsPtr && !originalType.EndsWith("*"))
|
||||
type = "IntPtr";
|
||||
else if (fieldInfo.Type.Type == "Array" || fieldInfo.Type.Type == "Span" || fieldInfo.Type.Type == "DataContainer" || fieldInfo.Type.Type == "BytesContainer")
|
||||
else if (marshalType.Type == "Array" || marshalType.Type == "Span" || marshalType.Type == "DataContainer" || marshalType.Type == "BytesContainer")
|
||||
{
|
||||
type = "IntPtr";
|
||||
apiType = FindApiTypeInfo(buildData, fieldInfo.Type.GenericArgs[0], structureInfo);
|
||||
apiType = FindApiTypeInfo(buildData, marshalType.GenericArgs[0], structureInfo);
|
||||
internalType = apiType is StructureInfo elementStructureInfo && UseCustomMarshalling(buildData, elementStructureInfo, structureInfo);
|
||||
}
|
||||
else if (fieldInfo.Type.Type == "Version")
|
||||
else if (marshalType.Type == "Version")
|
||||
type = "IntPtr";
|
||||
else if (type == "string")
|
||||
type = "IntPtr";
|
||||
else if (type == "bool")
|
||||
type = "byte";
|
||||
else if (fieldInfo.Type.Type == "Variant")
|
||||
else if (marshalType.Type == "Variant")
|
||||
type = "IntPtr";
|
||||
else if (internalType)
|
||||
{
|
||||
@@ -1695,9 +1697,9 @@ namespace Flax.Build.Bindings
|
||||
// Generate struct constructor/getter and deconstructor/setter function
|
||||
toManagedContent.Append("managed.").Append(fieldInfo.Name).Append(" = ");
|
||||
toNativeContent.Append("unmanaged.").Append(fieldInfo.Name).Append(" = ");
|
||||
if (fieldInfo.Type.IsObjectRef)
|
||||
if (marshalType.IsObjectRef)
|
||||
{
|
||||
var managedType = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type.GenericArgs[0], structureInfo);
|
||||
var managedType = GenerateCSharpNativeToManaged(buildData, marshalType.GenericArgs[0], structureInfo);
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{managedType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
@@ -1705,7 +1707,7 @@ namespace Flax.Build.Bindings
|
||||
// Permanent ScriptingObject handle is passed from native side, do not release it
|
||||
//freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
}
|
||||
else if (fieldInfo.Type.Type == "ScriptingObject")
|
||||
else if (marshalType.Type == "ScriptingObject")
|
||||
{
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<FlaxEngine.Object>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;");
|
||||
@@ -1714,7 +1716,7 @@ namespace Flax.Build.Bindings
|
||||
// Permanent ScriptingObject handle is passed from native side, do not release it
|
||||
//freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
}
|
||||
else if (fieldInfo.Type.IsPtr && originalType != "IntPtr" && !originalType.EndsWith("*"))
|
||||
else if (marshalType.IsPtr && originalType != "IntPtr" && !originalType.EndsWith("*"))
|
||||
{
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;");
|
||||
@@ -1723,14 +1725,14 @@ namespace Flax.Build.Bindings
|
||||
// Permanent ScriptingObject handle is passed from native side, do not release it
|
||||
//freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
}
|
||||
else if (fieldInfo.Type.Type == "Dictionary")
|
||||
else if (marshalType.Type == "Dictionary")
|
||||
{
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
|
||||
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak);");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
}
|
||||
else if (fieldInfo.Type.Type == "Array" || fieldInfo.Type.Type == "Span" || fieldInfo.Type.Type == "DataContainer" || fieldInfo.Type.Type == "BytesContainer")
|
||||
else if (marshalType.Type == "Array" || marshalType.Type == "Span" || marshalType.Type == "DataContainer" || marshalType.Type == "BytesContainer")
|
||||
{
|
||||
string originalElementType = originalType.Substring(0, originalType.Length - 2);
|
||||
if (internalType)
|
||||
@@ -1744,7 +1746,7 @@ namespace Flax.Build.Bindings
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.NativeToManaged.Free(value); }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
}
|
||||
else if (fieldInfo.Type.GenericArgs[0].IsObjectRef)
|
||||
else if (marshalType.GenericArgs[0].IsObjectRef)
|
||||
{
|
||||
// Array elements passed as GCHandles
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>(Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)) : null;");
|
||||
@@ -1763,7 +1765,7 @@ namespace Flax.Build.Bindings
|
||||
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
}
|
||||
}
|
||||
else if (fieldInfo.Type.Type == "Version")
|
||||
else if (marshalType.Type == "Version")
|
||||
{
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
|
||||
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak);");
|
||||
@@ -1782,7 +1784,7 @@ namespace Flax.Build.Bindings
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != 0;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name} ? (byte)1 : (byte)0;");
|
||||
}
|
||||
else if (fieldInfo.Type.Type == "Variant")
|
||||
else if (marshalType.Type == "Variant")
|
||||
{
|
||||
// Variant passed as boxed object handle
|
||||
toManagedContent.AppendLine($"ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged.{fieldInfo.Name});");
|
||||
|
||||
@@ -169,6 +169,8 @@ namespace Flax.Build.Bindings
|
||||
return $"Variant(StringAnsiView({value}))";
|
||||
if (typeInfo.IsObjectRef)
|
||||
return $"Variant({value}.Get())";
|
||||
if (typeInfo.Type == "SoftTypeReference")
|
||||
return $"Variant::Typename(StringAnsiView({value}))";
|
||||
if (typeInfo.IsArray)
|
||||
{
|
||||
var wrapperName = GenerateCppWrapperNativeToVariantMethodName(typeInfo);
|
||||
@@ -227,6 +229,8 @@ namespace Flax.Build.Bindings
|
||||
return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((ScriptingObject*){value})";
|
||||
if (typeInfo.IsObjectRef)
|
||||
return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((Asset*){value})";
|
||||
if (typeInfo.Type == "SoftTypeReference")
|
||||
return $"(StringAnsiView){value}";
|
||||
if (typeInfo.IsArray)
|
||||
throw new Exception($"Not supported type to convert from the Variant to fixed-size array '{typeInfo}[{typeInfo.ArraySize}]'.");
|
||||
if (typeInfo.Type == "Array" && typeInfo.GenericArgs != null)
|
||||
@@ -258,10 +262,24 @@ namespace Flax.Build.Bindings
|
||||
if (apiType.IsScriptingObject)
|
||||
return $"ScriptingObject::Cast<{typeInfo.Type}>((ScriptingObject*){value})";
|
||||
if (apiType.IsStruct && !CppInBuildVariantStructures.Contains(apiType.Name))
|
||||
{
|
||||
var name = apiType.FullNameNative;
|
||||
if (typeInfo.GenericArgs != null)
|
||||
{
|
||||
name += '<';
|
||||
for (var i = 0; i < typeInfo.GenericArgs.Count; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
name += ", ";
|
||||
name += typeInfo.GenericArgs[i];
|
||||
}
|
||||
name += '>';
|
||||
}
|
||||
if (typeInfo.IsPtr)
|
||||
return $"({apiType.FullNameNative}*){value}.AsBlob.Data";
|
||||
return $"({name}*){value}.AsBlob.Data";
|
||||
else
|
||||
return $"*({apiType.FullNameNative}*){value}.AsBlob.Data";
|
||||
return $"*({name}*){value}.AsBlob.Data";
|
||||
}
|
||||
}
|
||||
|
||||
if (typeInfo.IsPtr)
|
||||
|
||||
@@ -559,7 +559,7 @@ namespace Flax.Build
|
||||
foreach (var project in projects)
|
||||
{
|
||||
Log.Verbose(project.Name + " -> " + project.Path);
|
||||
project.Generate(solutionPath);
|
||||
project.Generate(solutionPath, project == mainSolutionProject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -638,7 +638,7 @@ namespace Flax.Build
|
||||
using (new ProfileEventScope("GenerateProject"))
|
||||
{
|
||||
Log.Verbose("Project " + rulesProjectName + " -> " + project.Path);
|
||||
dotNetProjectGenerator.GenerateProject(project, solutionPath);
|
||||
dotNetProjectGenerator.GenerateProject(project, solutionPath, project == mainSolutionProject);
|
||||
}
|
||||
|
||||
projects.Add(project);
|
||||
|
||||
@@ -34,23 +34,50 @@ namespace Flax.Build.Platforms
|
||||
|
||||
// Find Android NDK folder path
|
||||
var sdkPath = Environment.GetEnvironmentVariable("ANDROID_NDK");
|
||||
if (string.IsNullOrEmpty(sdkPath))
|
||||
FindNDKVersion(sdkPath);
|
||||
if (!IsValid)
|
||||
{
|
||||
// Look for ndk installed side-by-side with an sdk
|
||||
if (AndroidSdk.Instance.IsValid && Directory.Exists(Path.Combine(AndroidSdk.Instance.RootPath, "ndk")))
|
||||
if (!string.IsNullOrEmpty(sdkPath))
|
||||
{
|
||||
var subdirs = Directory.GetDirectories(Path.Combine(AndroidSdk.Instance.RootPath, "ndk"));
|
||||
if (subdirs.Length != 0)
|
||||
Log.Warning(string.Format("Specified Android NDK folder in ANDROID_NDK env variable doesn't contain valid NDK ({0})", sdkPath));
|
||||
}
|
||||
|
||||
// Look for ndk installed side-by-side with a sdk
|
||||
if (AndroidSdk.Instance.IsValid)
|
||||
{
|
||||
Utilities.SortVersionDirectories(subdirs);
|
||||
sdkPath = subdirs.Last();
|
||||
sdkPath = Path.Combine(AndroidSdk.Instance.RootPath, "ndk");
|
||||
FindNDKVersion(sdkPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!Directory.Exists(sdkPath))
|
||||
|
||||
private void FindNDKVersion(string folder)
|
||||
{
|
||||
Log.Warning(string.Format("Specified Android NDK folder in ANDROID_NDK env variable doesn't exist ({0})", sdkPath));
|
||||
// Skip if folder is invalid or missing
|
||||
if (string.IsNullOrEmpty(folder) || !Directory.Exists(folder))
|
||||
return;
|
||||
|
||||
// Check that explicit folder
|
||||
FindNDK(folder);
|
||||
if (IsValid)
|
||||
return;
|
||||
|
||||
// Check folders with versions
|
||||
var subDirs = Directory.GetDirectories(folder);
|
||||
if (subDirs.Length != 0)
|
||||
{
|
||||
Utilities.SortVersionDirectories(subDirs);
|
||||
FindNDK(subDirs.Last());
|
||||
}
|
||||
|
||||
if (!IsValid)
|
||||
{
|
||||
Log.Warning(string.Format("Failed to detect Android NDK version ({0})", folder));
|
||||
}
|
||||
}
|
||||
|
||||
private void FindNDK(string sdkPath)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(sdkPath) && Directory.Exists(sdkPath))
|
||||
{
|
||||
var sourceProperties = Path.Combine(sdkPath, "source.properties");
|
||||
@@ -72,15 +99,10 @@ namespace Flax.Build.Platforms
|
||||
Version = v;
|
||||
IsValid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning(string.Format("Failed to detect Android NDK version ({0})", sdkPath));
|
||||
}
|
||||
}
|
||||
if (IsValid)
|
||||
{
|
||||
RootPath = sdkPath;
|
||||
IsValid = true;
|
||||
Log.Info(string.Format("Found Android NDK {1} at {0}", RootPath, Version));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,9 +253,9 @@ namespace Flax.Build.Projects
|
||||
/// <summary>
|
||||
/// Generates the project.
|
||||
/// </summary>
|
||||
public virtual void Generate(string solutionPath)
|
||||
public virtual void Generate(string solutionPath, bool isMainProject)
|
||||
{
|
||||
Generator.GenerateProject(this, solutionPath);
|
||||
Generator.GenerateProject(this, solutionPath, isMainProject);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Flax.Build.Projects
|
||||
/// Generates the project.
|
||||
/// </summary>
|
||||
/// <param name="project">The project.</param>
|
||||
public abstract void GenerateProject(Project project, string solutionPath);
|
||||
public abstract void GenerateProject(Project project, string solutionPath, bool isMainProject);
|
||||
|
||||
/// <summary>
|
||||
/// Generates the solution.
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
public override TargetType? Type => TargetType.DotNet;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GenerateProject(Project project, string solutionPath)
|
||||
public override void GenerateProject(Project project, string solutionPath, bool isMainProject)
|
||||
{
|
||||
var csProjectFileContent = new StringBuilder();
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
public override TargetType? Type => TargetType.DotNetCore;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GenerateProject(Project project, string solutionPath)
|
||||
public override void GenerateProject(Project project, string solutionPath, bool isMainProject)
|
||||
{
|
||||
var dotnetSdk = DotNetSdk.Instance;
|
||||
var csProjectFileContent = new StringBuilder();
|
||||
@@ -110,7 +110,6 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
//csProjectFileContent.AppendLine(" <CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>"); // TODO: use it to reduce burden of framework libs
|
||||
|
||||
// Custom .targets file for overriding MSBuild build tasks, only invoke Flax.Build once per Flax project
|
||||
bool isMainProject = Globals.Project.IsCSharpOnlyProject && Globals.Project.ProjectFolderPath == project.WorkspaceRootPath && project.Name != "BuildScripts" && (Globals.Project.Name != "Flax" || project.Name != "FlaxEngine");
|
||||
var flaxBuildTargetsFilename = isMainProject ? "Flax.Build.CSharp.targets" : "Flax.Build.CSharp.SkipBuild.targets";
|
||||
var cacheProjectsPath = Utilities.MakePathRelativeTo(Path.Combine(Globals.Root, "Cache", "Projects"), projectDirectory);
|
||||
var flaxBuildTargetsPath = !string.IsNullOrEmpty(cacheProjectsPath) ? Path.Combine(cacheProjectsPath, flaxBuildTargetsFilename) : flaxBuildTargetsFilename;
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GenerateProject(Project project, string solutionPath)
|
||||
public override void GenerateProject(Project project, string solutionPath, bool isMainProject)
|
||||
{
|
||||
var vcProjectFileContent = new StringBuilder();
|
||||
var vcFiltersFileContent = new StringBuilder();
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
public override Guid ProjectTypeGuid => ProjectTypeGuids.Android;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Generate(string solutionPath)
|
||||
public override void Generate(string solutionPath, bool isMainProject)
|
||||
{
|
||||
// Try to reuse the existing project guid from existing files
|
||||
ProjectGuid = GetProjectGuid(Path, Name);
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Flax.Build.Projects.VisualStudioCode
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GenerateProject(Project project, string solutionPath)
|
||||
public override void GenerateProject(Project project, string solutionPath, bool isMainProject)
|
||||
{
|
||||
// Not used, solution contains all projects definitions
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Flax.Build.Projects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GenerateProject(Project project, string solutionPath)
|
||||
public override void GenerateProject(Project project, string solutionPath, bool isMainProject)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user