Merge branch 'stabilization/2106' of https://github.com/aws-lumberyard/o3de into carlito/stabilization/2106

main
chcurran 5 years ago
commit 168042dee4

@ -10,7 +10,7 @@
#
if(NOT PROJECT_NAME)
cmake_minimum_required(VERSION 3.19)
cmake_minimum_required(VERSION 3.20)
project(AutomatedTesting
LANGUAGES C CXX
VERSION 1.0.0.0

@ -40,6 +40,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
AutomatedTesting.GameLauncher
AutomatedTesting.Assets
COMPONENT
Smoke
Sandbox
)
endif()

@ -15,7 +15,7 @@ from automatedtesting_shared.base import TestAutomationBase
import ly_test_tools.environment.file_system as file_system
@pytest.mark.SUITE_sandbox
@pytest.mark.SUITE_smoke
@pytest.mark.parametrize("launcher_platform", ["windows_editor"])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("level", ["temp_level"])

@ -119,9 +119,6 @@
]
}
},
"DefaultMaterial": {
"SurfaceType": "Default_1"
},
"MaterialLibrary": {
"assetId": {
"guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}"

@ -101,9 +101,6 @@
]
}
},
"DefaultMaterial": {
"SurfaceType": "Default_1"
},
"MaterialLibrary": {
"assetId": {
"guid": "{6AA79EE4-7EC3-5717-87AE-EDD7D886FD7F}"

@ -107,9 +107,6 @@
]
}
},
"DefaultMaterial": {
"SurfaceType": "Default_1"
},
"MaterialLibrary": {
"assetId": {
"guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}"

@ -119,9 +119,6 @@
]
}
},
"DefaultMaterial": {
"SurfaceType": "Default_1"
},
"MaterialLibrary": {
"assetId": {
"guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}"

@ -119,9 +119,6 @@
]
}
},
"DefaultMaterial": {
"SurfaceType": "Default_1"
},
"MaterialLibrary": {
"assetId": {
"guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}"

@ -119,9 +119,6 @@
]
}
},
"DefaultMaterial": {
"SurfaceType": "Default_1"
},
"MaterialLibrary": {
"assetId": {
"guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}"

@ -101,9 +101,6 @@
]
}
},
"DefaultMaterial": {
"SurfaceType": "Default_1"
},
"MaterialLibrary": {
"assetId": {
"guid": "{3A055A3F-8CB7-5FEE-B437-EB365FACD0D4}"

@ -9,17 +9,8 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
# Cmake version 3.19 is the minimum version needed for all of Open 3D Engine's supported platforms
cmake_minimum_required(VERSION 3.19)
# CMP0111 introduced in 3.19 has a bug that produces the policy to warn every time there is an
# INTERFACE IMPORTED library. We use this type of libraries for handling 3rdParty. The rest of
# the documentation states that INTERFACE IMPORTED libraries do not require to set locations, but
# the policy still warns about it. Issue: https://gitlab.kitware.com/cmake/cmake/-/issues/21470
# The issue was fixed in 3.19.1 so we just disable the policy for 3.19
if(CMAKE_VERSION VERSION_EQUAL 3.19)
cmake_policy(SET CMP0111 OLD)
endif()
# Cmake version 3.20 is the minimum version needed for all of Open 3D Engine's supported platforms
cmake_minimum_required(VERSION 3.20)
include(cmake/LySet.cmake)
include(cmake/Version.cmake)

@ -67,7 +67,7 @@ struct CryPakMock
MOCK_METHOD1(PoolMalloc, void*(size_t size));
MOCK_METHOD1(PoolFree, void(void* p));
MOCK_METHOD3(PoolAllocMemoryBlock, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> (size_t nSize, const char* sUsage, size_t nAlign));
MOCK_METHOD3(FindFirst, AZ::IO::ArchiveFileIterator(AZStd::string_view pDir, uint32_t nFlags, bool bAllOwUseFileSystem));
MOCK_METHOD2(FindFirst, AZ::IO::ArchiveFileIterator(AZStd::string_view pDir, AZ::IO::IArchive::EFileSearchType));
MOCK_METHOD1(FindNext, AZ::IO::ArchiveFileIterator(AZ::IO::ArchiveFileIterator handle));
MOCK_METHOD1(FindClose, bool(AZ::IO::ArchiveFileIterator));
MOCK_METHOD1(GetModificationTime, AZ::IO::IArchive::FileTime(AZ::IO::HandleType f));

@ -306,8 +306,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder)
AZStd::unordered_set<AZStd::string> pakList;
bool allowFileSystem = true;
AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(search.c_str(), 0, allowFileSystem);
AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(search.c_str(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskOnly);
if (handle)
{
@ -320,7 +319,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder)
{
if (AZ::StringFunc::Equal(handle.m_filename.data(), LevelPakName))
{
// level folder contain pak files like 'level.pak'
// level folder contain pak files like 'level.pak'
// which we only want to load during level loading.
continue;
}
@ -351,7 +350,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder)
PopulateLevels(search, folder, pPak, modFolder, false);
// Load levels outside of the bundles to maintain backward compatibility.
PopulateLevels(search, folder, pPak, modFolder, true);
}
void CLevelSystem::PopulateLevels(
@ -360,7 +359,7 @@ void CLevelSystem::PopulateLevels(
{
// allow this find first to actually touch the file system
// (causes small overhead but with minimal amount of levels this should only be around 150ms on actual DVD Emu)
AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(searchPattern.c_str(), 0, fromFileSystemOnly);
AZ::IO::ArchiveFileIterator handle = pPak->FindFirst(searchPattern.c_str(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskOnly);
if (handle)
{
@ -973,7 +972,7 @@ void CLevelSystem::UnloadLevel()
m_lastLevelName.clear();
SAFE_RELEASE(m_pCurrentLevel);
// Force Lua garbage collection (may no longer be needed now the legacy renderer has been removed).
// Normally the GC step is triggered at the end of this method (by the ESYSTEM_EVENT_LEVEL_POST_UNLOAD event).
EBUS_EVENT(AZ::ScriptSystemRequestBus, GarbageCollect);

@ -9,8 +9,6 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
cmake_minimum_required(VERSION 3.0)
ly_add_target(
NAME AzAutoGen HEADERONLY
NAMESPACE AZ

@ -24,7 +24,12 @@
#include <AzCore/Serialization/Json/StringSerializer.h>
#include <AzCore/Serialization/Json/TupleSerializer.h>
#include <AzCore/Serialization/Json/UnorderedSetSerializer.h>
#include <AzCore/Serialization/Json/UnsupportedTypesSerializer.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/any.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/tuple.h>
#include <AzCore/std/utils.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/std/containers/forward_list.h>
@ -33,12 +38,11 @@
#include <AzCore/std/containers/set.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/variant.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/intrusive_ptr.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/tuple.h>
#include <AzCore/std/utils.h>
namespace AZ
{
@ -98,6 +102,13 @@ namespace AZ
jsonContext->Serializer<JsonArraySerializer>()
->HandlesType<AZStd::array>();
jsonContext->Serializer<JsonAnySerializer>()
->HandlesType<AZStd::any>();
jsonContext->Serializer<JsonVariantSerializer>()
->HandlesType<AZStd::variant>();
jsonContext->Serializer<JsonOptionalSerializer>()
->HandlesType<AZStd::optional>();
MathReflect(jsonContext);
}
else if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(reflectContext))

@ -33,34 +33,31 @@ namespace AZ
, m_serializerIter(serializerMapIter)
{}
JsonRegistrationContext::SerializerBuilder* JsonRegistrationContext::SerializerBuilder::HandlesTypeId(const Uuid& uuid)
JsonRegistrationContext::SerializerBuilder* JsonRegistrationContext::SerializerBuilder::HandlesTypeId(
const Uuid& uuid, bool overwriteExisting)
{
if (!m_context->IsRemovingReflection())
{
auto serializer = m_serializerIter->second.get();
if (uuid.IsNull())
{
AZ_Error("Serialization", false,
"Could not register Json serializer %s. Its Uuid is null.",
serializer->RTTI_GetTypeName()
);
AZ_Assert(false, "Could not register Json serializer %s. Its Uuid is null.", serializer->RTTI_GetTypeName());
return this;
}
auto serializerIter = m_context->m_handledTypesMap.find(uuid);
if (serializerIter == m_context->m_handledTypesMap.end())
if (!overwriteExisting)
{
m_context->m_handledTypesMap.emplace(uuid, serializer);
return this;
auto emplaceResult = m_context->m_handledTypesMap.try_emplace(uuid, serializer);
AZ_Assert(
emplaceResult.second,
"Couldn't register Json serializer %s. Another serializer (%s) has already been registered for the same Uuid (%s).",
serializer->RTTI_GetTypeName(), emplaceResult.first->second->RTTI_GetTypeName(),
uuid.ToString<AZStd::string>().c_str());
}
else
{
m_context->m_handledTypesMap.insert_or_assign(uuid, serializer);
}
AZ_Error("Serialization", false,
"Couldn't register Json serializer %s. Another serializer (%s) has already been registered for the same Uuid (%s).",
serializer->RTTI_GetTypeName(),
serializerIter->second->RTTI_GetTypeName(),
serializerIter->first.ToString<OSString>().c_str()
);
}
else
{

@ -63,52 +63,50 @@ namespace AZ
SerializerBuilder* operator->();
template <typename T>
SerializerBuilder* HandlesType()
SerializerBuilder* HandlesType(bool overwriteExisting = false)
{
return HandlesTypeId(azrtti_typeid<T>());
return HandlesTypeId(azrtti_typeid<T>(), overwriteExisting);
}
template <template<typename...> class T>
SerializerBuilder* HandlesType()
SerializerBuilder* HandlesType(bool overwriteExisting = false)
{
return HandlesTypeId(azrtti_typeid<T>());
return HandlesTypeId(azrtti_typeid<T>(), overwriteExisting);
}
template<template<AZStd::size_t...> class T>
SerializerBuilder* HandlesType()
SerializerBuilder* HandlesType(bool overwriteExisting = false)
{
return HandlesTypeId(azrtti_typeid<T>());
return HandlesTypeId(azrtti_typeid<T>(), overwriteExisting);
}
template<template<typename, AZStd::size_t> class T>
SerializerBuilder* HandlesType()
SerializerBuilder* HandlesType(bool overwriteExisting = false)
{
return HandlesTypeId(azrtti_typeid<T>());
return HandlesTypeId(azrtti_typeid<T>(), overwriteExisting);
}
template<template<typename, typename, AZStd::size_t> class T>
SerializerBuilder* HandlesType()
SerializerBuilder* HandlesType(bool overwriteExisting = false)
{
return HandlesTypeId(azrtti_typeid<T>());
return HandlesTypeId(azrtti_typeid<T>(), overwriteExisting);
}
template<template<typename, typename, typename, AZStd::size_t> class T>
SerializerBuilder* HandlesType()
SerializerBuilder* HandlesType(bool overwriteExisting = false)
{
return HandlesTypeId(azrtti_typeid<T>());
return HandlesTypeId(azrtti_typeid<T>(), overwriteExisting);
}
template<template<typename, AZStd::size_t, typename> class T>
SerializerBuilder* HandlesType()
SerializerBuilder* HandlesType(bool overwriteExisting = false)
{
return HandlesTypeId(azrtti_typeid<T>());
return HandlesTypeId(azrtti_typeid<T>(), overwriteExisting);
}
protected:
struct Placeholder { AZ_TYPE_INFO(PlaceHolder, "{4425191C-F497-411A-A7C3-52928E720B0A}"); };
SerializerBuilder(JsonRegistrationContext* context, SerializerMap::const_iterator serializerMapIter);
SerializerBuilder* HandlesTypeId(const AZ::Uuid& uuid);
SerializerBuilder* HandlesTypeId(const AZ::Uuid& uuid, bool overwriteExisting);
JsonRegistrationContext* m_context = nullptr;
SerializerMap::const_iterator m_serializerIter;

@ -0,0 +1,56 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Serialization/Json/UnsupportedTypesSerializer.h>
namespace AZ
{
AZ_CLASS_ALLOCATOR_IMPL(JsonUnsupportedTypesSerializer, SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(JsonAnySerializer, SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(JsonVariantSerializer, SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(JsonOptionalSerializer, SystemAllocator, 0);
JsonSerializationResult::Result JsonUnsupportedTypesSerializer::Load(void*, const Uuid&, const rapidjson::Value&,
JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult;
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Invalid, GetMessage());
}
JsonSerializationResult::Result JsonUnsupportedTypesSerializer::Store(rapidjson::Value&, const void*, const void*, const Uuid&,
JsonSerializerContext& context)
{
namespace JSR = JsonSerializationResult;
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::Invalid, GetMessage());
}
AZStd::string_view JsonAnySerializer::GetMessage() const
{
return "The Json Serialization doesn't support AZStd::any by design. The Json Serialization attempts to minimize the use of $type, "
"in particular the guid version, but no way has yet been found to use AZStd::any without explicitly and always requiring "
"one.";
}
AZStd::string_view JsonVariantSerializer::GetMessage() const
{
return "The Json Serialization doesn't support AZStd::variant by design. The Json Serialization attempts to minimize the use of "
"$type, in particular the guid version. While combinations of AZStd::variant can be constructed that don't require a $type, "
"this cannot be guaranteed in general.";
}
AZStd::string_view JsonOptionalSerializer::GetMessage() const
{
return "The Json Serialization doesn't support AZStd::optional by design. No JSON format has yet been found that wasn't deemed too "
"complex or overly verbose.";
}
} // namespace AZ

@ -0,0 +1,72 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Memory/Memory.h>
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
#include <AzCore/std/string/string_view.h>
namespace AZ
{
class JsonUnsupportedTypesSerializer : public BaseJsonSerializer
{
public:
AZ_RTTI(JsonUnsupportedTypesSerializer, "{AFCC76B9-1F28-429D-8B4E-020BFD95ADAC}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
JsonSerializationResult::Result Load(
void* outputValue,
const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(
rapidjson::Value& outputValue,
const void* inputValue,
const void* defaultValue,
const Uuid& valueTypeId,
JsonSerializerContext& context) override;
protected:
virtual AZStd::string_view GetMessage() const = 0;
};
class JsonAnySerializer : public JsonUnsupportedTypesSerializer
{
public:
AZ_RTTI(JsonAnySerializer, "{699A0864-C4E2-4266-8141-99793C76870F}", JsonUnsupportedTypesSerializer);
AZ_CLASS_ALLOCATOR_DECL;
protected:
AZStd::string_view GetMessage() const override;
};
class JsonVariantSerializer : public JsonUnsupportedTypesSerializer
{
public:
AZ_RTTI(JsonVariantSerializer, "{08F8E746-F8A4-4E83-8902-713E90F3F498}", JsonUnsupportedTypesSerializer);
AZ_CLASS_ALLOCATOR_DECL;
protected:
AZStd::string_view GetMessage() const override;
};
class JsonOptionalSerializer : public JsonUnsupportedTypesSerializer
{
public:
AZ_RTTI(JsonOptionalSerializer, "{F8AF1C95-BD1B-44D2-9B4A-F5726133A104}", JsonUnsupportedTypesSerializer);
AZ_CLASS_ALLOCATOR_DECL;
protected:
AZStd::string_view GetMessage() const override;
};
} // namespace AZ

@ -544,6 +544,8 @@ set(FILES
Serialization/Json/TupleSerializer.cpp
Serialization/Json/UnorderedSetSerializer.h
Serialization/Json/UnorderedSetSerializer.cpp
Serialization/Json/UnsupportedTypesSerializer.h
Serialization/Json/UnsupportedTypesSerializer.cpp
Serialization/std/VariantReflection.inl
Settings/CommandLine.cpp
Settings/CommandLine.h

@ -95,6 +95,20 @@ namespace JsonSerializationTests
}
};
class SerializerWithOneDuplicatedTypeWithOverride
: public JsonSerializerTemplatedMock<SerializerWithOneDuplicatedTypeWithOverride>
{
public:
AZ_RTTI(SerializerWithOneDuplicatedTypeWithOverride, "{4218D591-E578-499B-B578-ACA70C9944AB}", BaseJsonSerializer);
~SerializerWithOneDuplicatedTypeWithOverride() override = default;
static void Reflect(AZ::JsonRegistrationContext* context)
{
context->Serializer<SerializerWithOneDuplicatedTypeWithOverride>()
->HandlesType<bool>(true);
}
};
// Attempts to register the same type twice
class SerializerWithTwoSameTypes
: public JsonSerializerTemplatedMock<SerializerWithTwoSameTypes>
@ -271,13 +285,29 @@ namespace JsonSerializationTests
EXPECT_EQ(1, m_jsonRegistrationContext->GetRegisteredSerializers().size());
AZ::BaseJsonSerializer* mockSerializer = m_jsonRegistrationContext->GetSerializerForType(azrtti_typeid<bool>());
EXPECT_NE(mockSerializer, nullptr);
ASSERT_NE(mockSerializer, nullptr);
EXPECT_EQ(AZ::AzTypeInfo<SerializerWithOneType>::Uuid(), mockSerializer->RTTI_GetType());
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
SerializerWithOneDuplicatedType::Unreflect(m_jsonRegistrationContext.get());
}
TEST_F(JsonRegistrationContextTests, OverwriteRegisterSameUuidWithMultipleSerializers_Succeeds)
{
EXPECT_NE(AZ::AzTypeInfo<SerializerWithOneDuplicatedTypeWithOverride>::Uuid(), AZ::AzTypeInfo<SerializerWithOneType>::Uuid());
SerializerWithOneType::Reflect(m_jsonRegistrationContext.get());
SerializerWithOneDuplicatedTypeWithOverride::Reflect(m_jsonRegistrationContext.get());
EXPECT_EQ(1, m_jsonRegistrationContext->GetRegisteredSerializers().size());
AZ::BaseJsonSerializer* mockSerializer = m_jsonRegistrationContext->GetSerializerForType(azrtti_typeid<bool>());
ASSERT_NE(mockSerializer, nullptr);
EXPECT_EQ(AZ::AzTypeInfo<SerializerWithOneDuplicatedTypeWithOverride>::Uuid(), mockSerializer->RTTI_GetType());
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
SerializerWithOneDuplicatedTypeWithOverride::Unreflect(m_jsonRegistrationContext.get());
}
TEST_F(JsonRegistrationContextTests, RegisterSameUuidWithSameSerializers_Fails)
{
AZ_TEST_START_ASSERTTEST;

@ -0,0 +1,142 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Serialization/Json/UnsupportedTypesSerializer.h>
#include <AzCore/std/any.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/containers/variant.h>
#include <Tests/Serialization/Json/BaseJsonSerializerFixture.h>
namespace JsonSerializationTests
{
struct AnyInfo
{
using Type = AZStd::any;
using Serializer = AZ::JsonAnySerializer;
};
struct VariantInfo
{
using Type = AZStd::variant<AZStd::monostate, int, double>;
using Serializer = AZ::JsonVariantSerializer;
};
struct OptionalInfo
{
using Type = AZStd::optional<int>;
using Serializer = AZ::JsonVariantSerializer;
};
template<typename Info>
class JsonUnsupportedTypesSerializerTests : public BaseJsonSerializerFixture
{
public:
using Type = typename Info::Type;
using Serializer = typename Info::Serializer;
void SetUp() override
{
BaseJsonSerializerFixture::SetUp();
this->m_serializer = AZStd::make_unique<Serializer>();
}
void TearDown() override
{
this->m_serializer.reset();
BaseJsonSerializerFixture::TearDown();
}
protected:
AZStd::unique_ptr<Serializer> m_serializer;
Type m_instance{};
};
using UnsupportedTypesTestTypes = ::testing::Types<AnyInfo, VariantInfo, OptionalInfo>;
TYPED_TEST_CASE(JsonUnsupportedTypesSerializerTests, UnsupportedTypesTestTypes);
TYPED_TEST(JsonUnsupportedTypesSerializerTests, Load_CallDirectly_ReportsIssueAndHalts)
{
namespace JSR = AZ::JsonSerializationResult;
bool hasMessage = false;
auto callback = [&hasMessage](AZStd::string_view message, JSR::ResultCode result, AZStd::string_view) -> JSR::ResultCode
{
hasMessage = !message.empty();
return result;
};
this->m_jsonDeserializationContext->PushReporter(AZStd::move(callback));
JSR::Result result = this->m_serializer->Load(
&this->m_instance, azrtti_typeid(this->m_instance), *this->m_jsonDocument, *this->m_jsonDeserializationContext);
this->m_jsonDeserializationContext->PopReporter();
EXPECT_EQ(JSR::Processing::Halted, result.GetResultCode().GetProcessing());
EXPECT_TRUE(hasMessage);
}
TYPED_TEST(JsonUnsupportedTypesSerializerTests, Load_CallThroughFrontEnd_ReportsIssueAndHalts)
{
namespace JSR = AZ::JsonSerializationResult;
bool hasMessage = false;
auto callback = [&hasMessage](AZStd::string_view message, JSR::ResultCode result, AZStd::string_view) -> JSR::ResultCode
{
hasMessage = !message.empty();
return result;
};
this->m_deserializationSettings->m_reporting = AZStd::move(callback);
JSR::ResultCode result = AZ::JsonSerialization::Load(this->m_instance, *this->m_jsonDocument, *this->m_deserializationSettings);
EXPECT_EQ(JSR::Processing::Halted, result.GetProcessing());
EXPECT_TRUE(hasMessage);
}
TYPED_TEST(JsonUnsupportedTypesSerializerTests, Save_CallDirectly_ReportsIssueAndHalts)
{
namespace JSR = AZ::JsonSerializationResult;
bool hasMessage = false;
auto callback = [&hasMessage](AZStd::string_view message, JSR::ResultCode result, AZStd::string_view) -> JSR::ResultCode
{
hasMessage = !message.empty();
return result;
};
this->m_jsonSerializationContext->PushReporter(AZStd::move(callback));
JSR::Result result = this->m_serializer->Store(
*this->m_jsonDocument, &this->m_instance, nullptr, azrtti_typeid(this->m_instance), *this->m_jsonSerializationContext);
this->m_jsonSerializationContext->PopReporter();
EXPECT_EQ(JSR::Processing::Halted, result.GetResultCode().GetProcessing());
EXPECT_TRUE(hasMessage);
}
TYPED_TEST(JsonUnsupportedTypesSerializerTests, Save_CallThroughFrontEnd_ReportsIssueAndHalts)
{
namespace JSR = AZ::JsonSerializationResult;
bool hasMessage = false;
auto callback = [&hasMessage](AZStd::string_view message, JSR::ResultCode result, AZStd::string_view) -> JSR::ResultCode
{
hasMessage = !message.empty();
return result;
};
this->m_serializationSettings->m_reporting = AZStd::move(callback);
JSR::ResultCode result = AZ::JsonSerialization::Store(
*this->m_jsonDocument, this->m_jsonDocument->GetAllocator(), this->m_instance, *this->m_serializationSettings);
EXPECT_EQ(JSR::Processing::Halted, result.GetProcessing());
EXPECT_TRUE(hasMessage);
}
} // namespace JsonSerializationTests

@ -128,6 +128,7 @@ set(FILES
Serialization/Json/TransformSerializerTests.cpp
Serialization/Json/TupleSerializerTests.cpp
Serialization/Json/UnorderedSetSerializerTests.cpp
Serialization/Json/UnsupportedTypesSerializerTests.cpp
Serialization/Json/UuidSerializerTests.cpp
Math/AabbTests.cpp
Math/ColorTests.cpp

@ -1290,7 +1290,7 @@ namespace AZ::IO
//////////////////////////////////////////////////////////////////////////
AZ::IO::ArchiveFileIterator Archive::FindFirst(AZStd::string_view pDir, [[maybe_unused]] uint32_t nPathFlags, bool bAllowUseFileSystem)
AZ::IO::ArchiveFileIterator Archive::FindFirst(AZStd::string_view pDir, EFileSearchType searchType)
{
auto szFullPath = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pDir);
if (!szFullPath)
@ -1299,8 +1299,26 @@ namespace AZ::IO
return {};
}
bool bScanZips{};
bool bAllowUseFileSystem{};
switch (searchType)
{
case IArchive::eFileSearchType_AllowInZipsOnly:
bAllowUseFileSystem = false;
bScanZips = true;
break;
case IArchive::eFileSearchType_AllowOnDiskAndInZips:
bAllowUseFileSystem = true;
bScanZips = true;
break;
case IArchive::eFileSearchType_AllowOnDiskOnly:
bAllowUseFileSystem = true;
bScanZips = false;
break;
}
AZStd::intrusive_ptr<AZ::IO::FindData> pFindData = new AZ::IO::FindData();
pFindData->Scan(this, szFullPath->Native(), bAllowUseFileSystem);
pFindData->Scan(this, szFullPath->Native(), bAllowUseFileSystem, bScanZips);
return pFindData->Fetch();
}
@ -1676,7 +1694,7 @@ namespace AZ::IO
return true;
}
if (AZ::IO::ArchiveFileIterator fileIterator = FindFirst(pWildcardIn, 0, true); fileIterator)
if (AZ::IO::ArchiveFileIterator fileIterator = FindFirst(pWildcardIn, IArchive::eFileSearchType_AllowOnDiskOnly); fileIterator)
{
AZStd::vector<AZStd::string> files;
do

@ -234,7 +234,7 @@ namespace AZ::IO
uint64_t FTell(AZ::IO::HandleType handle) override;
int FFlush(AZ::IO::HandleType handle) override;
int FClose(AZ::IO::HandleType handle) override;
AZ::IO::ArchiveFileIterator FindFirst(AZStd::string_view pDir, uint32_t nPathFlags = 0, bool bAllOwUseFileSystem = false) override;
AZ::IO::ArchiveFileIterator FindFirst(AZStd::string_view pDir, EFileSearchType searchType = eFileSearchType_AllowInZipsOnly) override;
AZ::IO::ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator fileIterator) override;
bool FindClose(AZ::IO::ArchiveFileIterator fileIterator) override;
int FEof(AZ::IO::HandleType handle) override;

@ -77,7 +77,7 @@ namespace AZ::IO
return m_findData && m_lastFetchValid;
}
void FindData::Scan(IArchive* archive, AZStd::string_view szDir, bool bAllowUseFS)
void FindData::Scan(IArchive* archive, AZStd::string_view szDir, bool bAllowUseFS, bool bScanZips)
{
// get the priority into local variable to avoid it changing in the course of
// this function execution
@ -87,12 +87,18 @@ namespace AZ::IO
{
// first, find the file system files
ScanFS(archive, szDir);
ScanZips(archive, szDir);
if (bScanZips)
{
ScanZips(archive, szDir);
}
}
else
{
// first, find the zip files
ScanZips(archive, szDir);
if (bScanZips)
{
ScanZips(archive, szDir);
}
if (bAllowUseFS || nVarPakPriority != ArchiveLocationPriority::ePakPriorityPakOnly)
{
ScanFS(archive, szDir);
@ -111,30 +117,31 @@ namespace AZ::IO
}
AZ::IO::FileIOBase::GetDirectInstance()->FindFiles(searchDirectory.c_str(), pattern.c_str(), [&](const char* filePath) -> bool
{
AZ::IO::FileDesc fileDesc;
AZStd::string filePathEntry{filePath};
AZ::IO::ArchiveFileIterator fileIterator;
fileIterator.m_filename = AZ::IO::PathView(filePath).Filename().Native();
fileIterator.m_fileDesc.nAttrib = {};
if (AZ::IO::FileIOBase::GetDirectInstance()->IsDirectory(filePath))
{
fileDesc.nAttrib = fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::Subdirectory;
fileIterator.m_fileDesc.nAttrib = fileIterator.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::Subdirectory;
m_fileStack.emplace_back(AZStd::move(fileIterator));
}
else
{
if (AZ::IO::FileIOBase::GetDirectInstance()->IsReadOnly(filePath))
{
fileDesc.nAttrib = fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::ReadOnly;
fileIterator.m_fileDesc.nAttrib = fileIterator.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::ReadOnly;
}
AZ::u64 fileSize = 0;
AZ::IO::FileIOBase::GetDirectInstance()->Size(filePath, fileSize);
fileDesc.nSize = fileSize;
fileDesc.tWrite = AZ::IO::FileIOBase::GetDirectInstance()->ModificationTime(filePath);
fileIterator.m_fileDesc.nSize = fileSize;
fileIterator.m_fileDesc.tWrite = AZ::IO::FileIOBase::GetDirectInstance()->ModificationTime(filePath);
// These times are not supported by our file interface
fileDesc.tAccess = fileDesc.tWrite;
fileDesc.tCreate = fileDesc.tWrite;
fileIterator.m_fileDesc.tAccess = fileIterator.m_fileDesc.tWrite;
fileIterator.m_fileDesc.tCreate = fileIterator.m_fileDesc.tWrite;
m_fileStack.emplace_back(AZStd::move(fileIterator));
}
[[maybe_unused]] auto result = m_mapFiles.emplace(AZStd::move(filePathEntry), fileDesc);
AZ_Assert(result.second, "Failed to insert FindData entry for filePath %s", filePath);
return true;
});
}
@ -164,7 +171,7 @@ namespace AZ::IO
fileDesc.nAttrib = AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive;
fileDesc.nSize = fileEntry->desc.lSizeUncompressed;
fileDesc.tWrite = fileEntry->GetModificationTime();
m_mapFiles.emplace(fname, fileDesc);
m_fileStack.emplace_back(AZ::IO::ArchiveFileIterator{ this, fname, fileDesc });
}
ZipDir::FindDir findDirectoryEntry(zipCache);
@ -177,7 +184,7 @@ namespace AZ::IO
}
AZ::IO::FileDesc fileDesc;
fileDesc.nAttrib = AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive | AZ::IO::FileDesc::Attribute::Subdirectory;
m_mapFiles.emplace(fname, fileDesc);
m_fileStack.emplace_back(AZ::IO::ArchiveFileIterator{ this, fname, fileDesc });
}
};
@ -246,7 +253,7 @@ namespace AZ::IO
if (!bindRootIter->empty() && AZStd::wildcard_match(sourcePathRemainder.Native(), bindRootIter->Native()))
{
AZ::IO::FileDesc fileDesc{ AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive | AZ::IO::FileDesc::Attribute::Subdirectory };
m_mapFiles.emplace(bindRootIter->Native(), fileDesc);
m_fileStack.emplace_back(AZ::IO::ArchiveFileIterator{ this, bindRootIter->Native(), fileDesc });
}
}
else
@ -262,22 +269,19 @@ namespace AZ::IO
AZ::IO::ArchiveFileIterator FindData::Fetch()
{
AZ::IO::ArchiveFileIterator fileIterator;
fileIterator.m_findData = this;
if (m_mapFiles.empty())
if (m_fileStack.empty())
{
return fileIterator;
AZ::IO::ArchiveFileIterator emptyFileIterator;
emptyFileIterator.m_lastFetchValid = false;
emptyFileIterator.m_findData = this;
return emptyFileIterator;
}
auto pakFileIter = m_mapFiles.begin();
AZStd::string fullFilePath;
AZ::StringFunc::Path::GetFullFileName(pakFileIter->first.c_str(), fullFilePath);
fileIterator.m_filename = AZStd::move(fullFilePath);
fileIterator.m_fileDesc = pakFileIter->second;
fileIterator.m_lastFetchValid = true;
// Remove Fetched item from the FindData map so that the iteration continues
m_mapFiles.erase(pakFileIter);
AZ::IO::ArchiveFileIterator fileIterator{ m_fileStack.back() };
fileIterator.m_lastFetchValid = true;
fileIterator.m_findData = this;
m_fileStack.pop_back();
return fileIterator;
}
}

@ -15,7 +15,6 @@
#include <AzCore/std/smart_ptr/intrusive_base.h>
#include <AzCore/std/string/fixed_string.h>
namespace AZ::IO
{
struct IArchive;
@ -74,13 +73,13 @@ namespace AZ::IO
AZ_CLASS_ALLOCATOR(FindData, AZ::SystemAllocator, 0);
FindData() = default;
AZ::IO::ArchiveFileIterator Fetch();
void Scan(IArchive* archive, AZStd::string_view path, bool bAllowUseFS = false);
void Scan(IArchive* archive, AZStd::string_view path, bool bAllowUseFS = false, bool bScanZips = true);
protected:
void ScanFS(IArchive* archive, AZStd::string_view path);
void ScanZips(IArchive* archive, AZStd::string_view path);
using FileMap = AZStd::map<AZStd::string, AZ::IO::FileDesc, AZStdStringLessCaseInsensitive>;
FileMap m_mapFiles;
using FileStack = AZStd::vector<ArchiveFileIterator>;
FileStack m_fileStack;
};
}

@ -197,6 +197,13 @@ namespace AZ::IO
eInMemoryPakLocale_PAK,
};
enum EFileSearchType
{
eFileSearchType_AllowInZipsOnly = 0,
eFileSearchType_AllowOnDiskAndInZips,
eFileSearchType_AllowOnDiskOnly
};
using SignedFileSize = int64_t;
virtual ~IArchive() = default;
@ -315,7 +322,7 @@ namespace AZ::IO
// Arguments:
// nFlags is a combination of EPathResolutionRules flags.
virtual ArchiveFileIterator FindFirst(AZStd::string_view pDir, uint32_t nFlags = 0, bool bAllowUseFileSystem = false) = 0;
virtual ArchiveFileIterator FindFirst(AZStd::string_view pDir, EFileSearchType searchType = eFileSearchType_AllowInZipsOnly) = 0;
virtual ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator handle) = 0;
virtual bool FindClose(AZ::IO::ArchiveFileIterator handle) = 0;
//returns file modification time

@ -166,7 +166,7 @@ namespace AzFramework
{
public:
AZ_CLASS_ALLOCATOR(RequestEscalateAsset, AZ::OSAllocator, 0);
AZ_RTTI(RequestAssetStatus, "{E95C5422-5F00-478B-A984-C041DE70484F}", BaseAssetProcessorMessage);
AZ_RTTI(RequestEscalateAsset, "{E95C5422-5F00-478B-A984-C041DE70484F}", BaseAssetProcessorMessage);
static void Reflect(AZ::ReflectContext* context);
static constexpr unsigned int MessageType = AZ_CRC("AssetSystem::RequestEscalateAsset", 0x1894d94e);

@ -46,7 +46,7 @@ namespace AzPhysics
//! Each Physics Scene uses this as a base and will override as needed.
CollisionConfiguration m_collisionConfig;
Physics::MaterialConfiguration m_defaultMaterialConfiguration; //!< Default material parameters for the project.
Physics::DefaultMaterialConfiguration m_defaultMaterialConfiguration; //!< Default material parameters for the project.
AZ::Data::Asset<Physics::MaterialLibraryAsset> m_materialLibraryAsset = AZ::Data::AssetLoadBehavior::NoLoad; //!< Material Library exposed by the system component SystemBus API.
//! Controls whether the Physics System will self register to the TickBus and call StartSimulation / FinishSimulation on each Scene.

@ -83,14 +83,12 @@ namespace Physics
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
AZStd::unordered_set<AZStd::string> forbiddenSurfaceTypeNames;
forbiddenSurfaceTypeNames.insert("Default");
editContext->Class<Physics::MaterialConfiguration>("", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "Physics Material")
->DataElement(MaterialConfiguration::s_configLineEdit, &MaterialConfiguration::m_surfaceType, "Surface type", "Game surface type") // Uses ConfigStringLineEditCtrl in PhysX gem.
->DataElement(MaterialConfiguration::s_configLineEdit, &MaterialConfiguration::m_surfaceType, "Name", "Name of the physics material") // Uses ConfigStringLineEditCtrl in PhysX gem.
->Attribute(AZ::Edit::Attributes::MaxLength, 64)
->Attribute(AZ::Edit::Attributes::ReadOnly, &MaterialConfiguration::IsNameReadOnly)
->Attribute(MaterialConfiguration::s_stringGroup, AZ_CRC("LineEditGroupSurfaceType", 0x6670659e))
->Attribute(MaterialConfiguration::s_forbiddenStringSet, forbiddenSurfaceTypeNames)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialConfiguration::m_staticFriction, "Static friction", "Friction coefficient when object is still")
->Attribute(AZ::Edit::Attributes::Min, 0.f)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialConfiguration::m_dynamicFriction, "Dynamic friction", "Friction coefficient when object is moving")
@ -178,6 +176,23 @@ namespace Physics
//////////////////////////////////////////////////////////////////////////
DefaultMaterialConfiguration::DefaultMaterialConfiguration()
{
m_surfaceType = Physics::DefaultPhysicsMaterialLabel;
}
void DefaultMaterialConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<Physics::DefaultMaterialConfiguration, Physics::MaterialConfiguration>()
->Version(1)
;
}
}
//////////////////////////////////////////////////////////////////////////
void MaterialLibraryAsset::Reflect(AZ::ReflectContext * context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
@ -212,7 +227,7 @@ namespace Physics
if (serializeContext)
{
serializeContext->Class<Physics::MaterialInfoReflectionWrapper>()
->Version(1)
->Version(2)
->Field("DefaultMaterial", &MaterialInfoReflectionWrapper::m_defaultMaterialConfiguration)
->Field("Asset", &MaterialInfoReflectionWrapper::m_materialLibraryAsset)
;
@ -300,7 +315,7 @@ namespace Physics
{
auto foundMaterialConfiguration = AZStd::find_if(m_materialLibrary.begin(), m_materialLibrary.end(), [&materialName](const auto& data)
{
return data.m_configuration.m_surfaceType == materialName;
return AZ::StringFunc::Equal(data.m_configuration.m_surfaceType, materialName, false/*bCaseSensitive*/);
});
if (foundMaterialConfiguration != m_materialLibrary.end())
@ -372,20 +387,28 @@ namespace Physics
serializeContext->Class<Physics::MaterialSelection>()
->Version(3, &ClassConverters::MaterialSelectionConverter)
->EventHandler<MaterialSelectionEventHandler>()
->Field("EditContextMaterialLibrary", &MaterialSelection::m_editContextMaterialLibrary)
->Field("MaterialIds", &MaterialSelection::m_materialIdsAssignedToSlots)
;
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<Physics::MaterialSelection>("Physics Material", "Select physics material library and which materials to use for the object")
editContext->Class<Physics::MaterialSelection>("Physics Materials", "Select which physics materials to use for each element of this object")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_materialIdsAssignedToSlots, "Mesh Surfaces", "Specify which Physics Material to use for each element of this object")
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_editContextMaterialLibrary, "Library", "Physics material library from PhysX Configuration")
->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
->Attribute(AZ_CRC_CE("BrowseButtonEnabled"), false)
->Attribute(AZ_CRC_CE("EditButton"), "")
->Attribute(AZ_CRC_CE("EditDescription"), "Open in Asset Editor")
->Attribute(AZ::Edit::Attributes::DefaultAsset, &MaterialSelection::GetMaterialLibraryId)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_materialIdsAssignedToSlots, "", "")
->ElementAttribute(Attributes::MaterialLibraryAssetId, &MaterialSelection::GetMaterialLibraryId)
->Attribute(AZ::Edit::Attributes::IndexedChildNameLabelOverride, &MaterialSelection::GetMaterialSlotLabel)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->ElementAttribute(AZ::Edit::Attributes::ReadOnly, &MaterialSelection::AreMaterialSlotsReadOnly)
->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
;
}
}

@ -25,6 +25,8 @@ namespace AZ
namespace Physics
{
static constexpr AZStd::string_view DefaultPhysicsMaterialLabel = "<Default Physics Material>";
/// Physics material
/// =========================
/// This is the interface to the wrapper around native material type (such as PxMaterial in PhysX gem)
@ -97,10 +99,13 @@ namespace Physics
class MaterialConfiguration
{
public:
AZ_TYPE_INFO(Physics::MaterialConfiguration, "{8807CAA1-AD08-4238-8FDB-2154ADD084A1}");
AZ_RTTI(Physics::MaterialConfiguration, "{8807CAA1-AD08-4238-8FDB-2154ADD084A1}");
static void Reflect(AZ::ReflectContext* context);
MaterialConfiguration() = default;
virtual ~MaterialConfiguration() = default;
const static AZ::Crc32 s_stringGroup; ///< Edit context data attribute. Identifies a string group instance. String values in the same group are unique.
const static AZ::Crc32 s_forbiddenStringSet; ///< Edit context data attribute. A set of strings that are not acceptable as values to the data element. Can be AZStd::unordered_set<AZStd::string>, AZStd::set<AZStd::string>, AZStd::vector<AZStd::string>
const static AZ::Crc32 s_configLineEdit; ///< Edit context data element handler. Creates custom line edit widget that allows string values to be unique in a group.
@ -121,11 +126,28 @@ namespace Physics
bool operator==(const MaterialConfiguration& other) const;
bool operator!=(const MaterialConfiguration& other) const;
protected:
virtual bool IsNameReadOnly() const { return false; }
private:
static bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
static AZ::Color GenerateDebugColor(const char* materialName);
};
class DefaultMaterialConfiguration
: public MaterialConfiguration
{
public:
AZ_RTTI(Physics::DefaultMaterialConfiguration, "{A1F64C5C-D413-4757-9D42-51DD0EBFC270}", Physics::MaterialConfiguration);
static void Reflect(AZ::ReflectContext* context);
DefaultMaterialConfiguration();
protected:
bool IsNameReadOnly() const override { return true; }
};
namespace Attributes
{
const static AZ::Crc32 MaterialLibraryAssetId = AZ_CRC("MaterialAssetId", 0x4a88a3f5);
@ -237,7 +259,7 @@ namespace Physics
AZ_TYPE_INFO(Physics::MaterialInfoReflectionWrapper, "{02AB8CBC-D35B-4E0F-89BA-A96D94DAD4F9}");
static void Reflect(AZ::ReflectContext* context);
Physics::MaterialConfiguration m_defaultMaterialConfiguration;
Physics::DefaultMaterialConfiguration m_defaultMaterialConfiguration;
AZ::Data::Asset<Physics::MaterialLibraryAsset> m_materialLibraryAsset =
AZ::Data::AssetLoadBehavior::NoLoad;
};
@ -296,6 +318,10 @@ namespace Physics
// EditorContext callbacks
AZStd::string GetMaterialSlotLabel(int index);
// Only used for Edit Context as it requires to have an asset reflected.
// To get the material library use GetMaterialLibraryId()
AZ::Data::Asset<Physics::MaterialLibraryAsset> m_editContextMaterialLibrary{ AZ::Data::AssetLoadBehavior::NoLoad };
};
/// Editor Bus used to assign material to terrain surface id.

@ -62,7 +62,7 @@ namespace Physics
->Attribute(AZ::Edit::Attributes::Step, 0.01f)
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_rotation, "Rotation", "Local rotation relative to the rigid body")
->Attribute(AZ::Edit::Attributes::Visibility, &ColliderConfiguration::GetOffsetVisibility)
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_materialSelection, "Physics Material", "Select physics material library and which materials to use for the shape")
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_materialSelection, "Physics Materials", "Select which physics materials to use for each element of this shape")
->Attribute(AZ::Edit::Attributes::Visibility, &ColliderConfiguration::GetMaterialSelectionVisibility)
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_tag, "Tag", "Tag used to identify colliders from one another")
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_restOffset, "Rest offset",

@ -17,21 +17,6 @@
namespace Physics
{
namespace Internal
{
bool ShapeConfigurationVersionConverter(
[[maybe_unused]] AZ::SerializeContext& context,
AZ::SerializeContext::DataElementNode& classElement)
{
if (classElement.GetVersion() <= 1)
{
classElement.RemoveElementByName(AZ_CRC_CE("UseMaterialsFromAsset"));
}
return true;
}
}
void ShapeConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
@ -181,9 +166,10 @@ namespace Physics
->RegisterGenericType<AZStd::shared_ptr<PhysicsAssetShapeConfiguration>>();
serializeContext->Class<PhysicsAssetShapeConfiguration, ShapeConfiguration>()
->Version(2, &Internal::ShapeConfigurationVersionConverter)
->Version(3)
->Field("PhysicsAsset", &PhysicsAssetShapeConfiguration::m_asset)
->Field("AssetScale", &PhysicsAssetShapeConfiguration::m_assetScale)
->Field("UseMaterialsFromAsset", &PhysicsAssetShapeConfiguration::m_useMaterialsFromAsset)
->Field("SubdivisionLevel", &PhysicsAssetShapeConfiguration::m_subdivisionLevel)
;
@ -196,6 +182,7 @@ namespace Physics
->DataElement(AZ::Edit::UIHandlers::Default, &PhysicsAssetShapeConfiguration::m_assetScale, "Asset Scale", "The scale of the asset shape")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Step, 0.01f)
->DataElement(AZ::Edit::UIHandlers::Default, &PhysicsAssetShapeConfiguration::m_useMaterialsFromAsset, "Physics materials from asset", "Auto-set physics materials using asset's physics material names")
;
}
}

@ -140,7 +140,7 @@ namespace Physics
AZ::Data::Asset<AZ::Data::AssetData> m_asset{ AZ::Data::AssetLoadBehavior::PreLoad };
AZ::Vector3 m_assetScale = AZ::Vector3::CreateOne();
bool m_useMaterialsFromAsset = false; // Not reflected or exposed to the user until there is a way to auto-match mesh's materials with physics materials
bool m_useMaterialsFromAsset = true;
AZ::u8 m_subdivisionLevel = 4; ///< The level of subdivision if a primitive shape is replaced with a convex mesh due to scaling.
};

@ -118,6 +118,7 @@ namespace Physics
AzPhysics::TriggerEvent::Reflect(context);
AzPhysics::SceneConfiguration::Reflect(context);
MaterialConfiguration::Reflect(context);
DefaultMaterialConfiguration::Reflect(context);
MaterialLibraryAsset::Reflect(context);
MaterialInfoReflectionWrapper::Reflect(context);
JointLimitConfiguration::Reflect(context);

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Asset Editor icon</title>
<g id="Asset-Editor-icon" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M15.9860086,2 L21.0547888,7.65093732 L21.0547888,19.8573598 C21.0547888,21.3071072 19.8795363,22.4823598 18.4297888,22.4823598 L18.4297888,22.4823598 L14.4922888,22.4823598 L14.4922888,19.8573598 L18.4297888,19.8573598 L18.4297888,9.39834532 L14.4922888,9.39834532 L14.4922888,4.39834532 L6.61728879,4.39834532 L6.61728879,9.39834532 L3.99228879,9.39834532 L3.99228879,4.625 C3.99228879,3.17525253 5.16754132,2 6.61728879,2 L6.61728879,2 L15.9860086,2 Z M11.9720779,11.5111613 L11.9720779,22.4823598 L3.99403128,22.4823598 C2.89373128,22.4823598 2,21.5871399 2,20.4828299 L2,20.4828299 L2,13.5106911 C2,12.4089803 2.89275834,11.5111613 3.99403128,11.5111613 L3.99403128,11.5111613 L11.9720779,11.5111613 Z M5.17089634,17.6663021 L4.40036064,18.4676192 C4.18355844,18.6930823 4.18043717,19.0553841 4.39605961,19.2796209 L6.20782094,21.1637586 C6.63740095,21.610499 7.33388742,21.610499 7.76346744,21.1637586 L9.57522877,19.2796209 C9.7896555,19.0566276 9.79105801,18.6965433 9.57092774,18.4676192 L8.80039204,17.6663021 C8.58358984,17.4408391 8.23344799,17.4394206 8.01921651,17.6622102 L6.98564419,18.7447072 L5.95207186,17.6622102 C5.73744273,17.439007 5.39102661,17.437378 5.17089634,17.6663021 Z M7.76346744,12.8297629 C7.33388742,12.3830219 6.63740095,12.3830219 6.20782094,12.8297629 L6.20782094,12.8297629 L4.39605961,14.7139006 C4.18043717,14.9381368 4.18355844,15.3004386 4.40036064,15.5259017 L4.40036064,15.5259017 L5.17089634,16.3272188 C5.39102661,16.5561429 5.73744273,16.5545139 5.95207186,16.3313107 L5.95207186,16.3313107 L6.98564419,15.2447072 L8.01921651,16.3313107 C8.23344799,16.5541009 8.58358984,16.5526818 8.80039204,16.3272188 L8.80039204,16.3272188 L9.57092774,15.5259017 C9.79105801,15.2969782 9.7896555,14.9368933 9.57522877,14.7139006 L9.57522877,14.7139006 Z" id="Combined-Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>audio edit </title>
<g id="audio-edit-" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M13.671871,7.50274987 C13.8711246,7.36985092 14.1146568,7.32555128 14.3360497,7.41415057 C16.9554394,8.10079513 18.9091859,8.10079513 22.0258713,7.41415057 C22.249462,7.36489076 22.4907963,7.39200075 22.6679107,7.5248997 C22.845025,7.65779864 22.9557214,7.87929688 23,8.10079513 L23,8.10079513 L22.9572844,18.8481815 C22.9572844,20.1328713 21.7174842,21.173913 20.1013161,21.173913 C18.485148,21.173913 17.2453478,20.1550211 17.2453478,18.8481815 C17.2453478,17.5634917 18.5294265,16.5224499 20.1013161,16.5224499 C20.3669876,16.5224499 20.632659,16.5445998 20.8761912,16.6110492 C21.1861413,16.6996485 21.473952,16.4560005 21.473952,16.1459029 L21.473952,16.1459029 L21.473952,10.6858818 C18.8420026,11.3725264 17.0164667,11.3725264 14.7803985,10.7080316 L14.7803985,10.7080316 L14.7803985,11.8540584 C14.2459139,11.4245036 13.671871,10.911155 13.3192054,10.5828292 C13.3192054,9.943716 13.3334439,9.11637131 13.3619209,8.10079513 C13.3619209,7.85714706 13.4726174,7.63564882 13.671871,7.50274987 Z M13.0212906,15.3812912 C13.4908418,15.7574122 13.9909547,16.3565034 14.4824837,16.7283004 L14.4824837,16.7283004 L14.4824837,18.6657679 C14.4824837,19.9504578 13.2426835,20.9914995 11.6265154,20.9914995 C10.0103472,20.9914995 8.77054704,19.9726076 8.77054704,18.6657679 C8.77054704,17.3810781 10.0546258,16.3400364 11.6486547,16.3621862 C11.9143261,16.3621862 12.1799976,16.384336 12.4235298,16.4507855 C12.7334799,16.5393848 13.0212906,16.2957367 13.0212906,15.9856392 L13.0212906,15.9856392 Z M4.87344064,2.00000241 C6.46039809,2.00000241 7.74688128,3.28766393 7.74688128,4.87607492 C7.74688128,5.24562866 7.67724533,5.59890316 7.55039064,5.92346979 L9.18715351,7.56781732 L9.19255594,7.56260027 L10.6736079,9.04725227 L10.6664504,9.05409316 L17.0762969,15.3611124 L17.0762969,16.1842691 L14.856515,16.1842691 L9.10274173,10.5856018 L7.50474928,12.1508361 C7.66044683,12.5049585 7.74688128,12.8964744 7.74688128,13.3081966 C7.74688128,14.8966076 6.46039809,16.1842691 4.87344064,16.1842691 C3.2864832,16.1842691 2,14.8966076 2,13.3081966 C2,11.7197856 3.2864832,10.4321241 4.87344064,10.4321241 C5.28086048,10.4321241 5.66847595,10.5169943 6.01960261,10.6700351 L7.61573544,9.10677758 L6.01608448,7.51576697 C5.66588747,7.66784294 5.27950023,7.75214743 4.87344064,7.75214743 C3.2864832,7.75214743 2,6.4644859 2,4.87607492 C2,3.28766393 3.2864832,2.00000241 4.87344064,2.00000241 Z M4.87344064,11.902843 C4.09799553,11.902843 3.46937306,12.5320412 3.46937306,13.3081966 C3.46937306,14.084352 4.09799553,14.7135502 4.87344064,14.7135502 C5.64888576,14.7135502 6.27750823,14.084352 6.27750823,13.3081966 C6.27750823,12.5320412 5.64888576,11.902843 4.87344064,11.902843 Z M9.03871317,8.66478868 C8.79573029,8.66478868 8.59875374,8.86185896 8.59875374,9.10495747 C8.59875374,9.34805597 8.79573029,9.54512625 9.03871317,9.54512625 C9.28169606,9.54512625 9.47867261,9.34805597 9.47867261,9.10495747 C9.47867261,8.86185896 9.28169606,8.66478868 9.03871317,8.66478868 Z M17.0763008,2 L17.0763008,2.68379615 L11.1730797,8.47119228 L9.69202779,6.98654029 L14.856519,2 L17.0763008,2 Z M4.87344064,3.4707213 C4.09799553,3.4707213 3.46937306,4.09991955 3.46937306,4.87607492 C3.46937306,5.65223028 4.09799553,6.28142853 4.87344064,6.28142853 C5.64888576,6.28142853 6.27750823,5.65223028 6.27750823,4.87607492 C6.27750823,4.09991955 5.64888576,3.4707213 4.87344064,3.4707213 Z" id="Combined-Shape-Copy-6" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>EMFX header icon</title>
<g id="EMFX-header-icon" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M15.4402141,6.47369367 C15.8338027,6.6892261 16.1509659,7.03456112 16.3277603,7.4597408 L18.6714707,10.7388937 L20.9265998,13.1099302 L19.5883386,14.5962199 L17.1067913,11.9890965 L17.0892351,12 L16.9863785,11.8625904 L16.7718452,11.6372009 L16.7984797,11.6115701 L15.7677139,10.2345359 L14.2649089,13.609891 L16.9897118,16 L17,16 L17,22 L15,22 L15,16.9335637 L12.0517866,14.4145916 L11.7885832,14.2974059 L10.4131448,17.3866912 L9.63999106,19.1359308 L4,15.6954658 L5.05983853,13.9993696 L8.74227212,16.2223461 L9.96149233,13.4839327 L9.94559534,13.4768549 L12.0486395,8.75334034 L10.4719503,9.12452852 L8.88365054,12.0950967 L7.13441112,11.1254774 L9.14411801,7.42096143 L9.13441112,7.3871095 L14.1957216,6.24191249 L14.1971157,6.24907236 L14.2372583,6.24373869 L14.2372583,6.24373869 C14.6690994,6.19071527 15.0856649,6.27953958 15.4402141,6.47369367 Z M16.1344111,2 C17.2389806,2 18.1344111,2.8954305 18.1344111,4 C18.1344111,5.1045695 17.2389806,6 16.1344111,6 C15.0298416,6 14.1344111,5.1045695 14.1344111,4 C14.1344111,2.8954305 15.0298416,2 16.1344111,2 Z" id="Combined-Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Veg editor icon </title>
<g id="Veg-editor-icon-" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M16.5148429,21.0425016 C16.8177068,21.0425016 17.1645673,21.2855646 17.1645673,21.7481048 C17.1645673,22.210645 16.8118623,22.453708 16.5148429,22.453708 L6.15659085,22.453708 C5.85372694,22.453708 5.50764574,22.2526162 5.50764574,21.7481048 C5.50764574,21.2435934 5.85957142,21.0425016 6.15659085,21.0425016 L16.5148429,21.0425016 Z M18.1293894,18.4805694 C18.5248398,18.4805694 18.783369,18.6215806 18.9803251,18.9179529 L21.3973486,21.6296028 C21.4913398,21.7733953 21.4260987,22.1575312 20.8849279,22.3133932 C20.3437571,22.4692552 19.8091487,22.2405042 19.7170269,22.0995716 L17.4957603,19.8222461 L5.51575322,19.8222461 L3.44599506,21.9708965 C3.25806678,22.2076933 2.65210839,22.4704438 2.11187863,22.3133932 C1.57164887,22.1563426 1.52319993,21.8555986 1.69847744,21.6296028 L4.15746187,18.8359298 C4.28086857,18.6216874 4.5612908,18.4805694 4.9475464,18.4805694 L18.1293894,18.4805694 Z M21.3923287,1.54629195 C21.9446135,1.54629195 22.3923287,1.9940072 22.3923287,2.54629195 L22.3923287,16.2168743 C22.3923287,16.7691591 21.9446135,17.2168743 21.3923287,17.2168743 L2.60767126,17.2168743 C2.05538651,17.2168743 1.60767126,16.7691591 1.60767126,16.2168743 L1.60767126,2.54629195 C1.60767126,1.9940072 2.05538651,1.54629195 2.60767126,1.54629195 L21.3923287,1.54629195 Z M8.80560763,15.9289985 C10.9624477,16.6479451 13.9204397,15.7983494 14.9166545,13.4126851 C15.9128692,11.0270207 15.0674988,8.36538278 17.161329,6.13505487 C18.5572159,4.6481696 19.9059881,3.55165429 21.2076457,2.84550893 C16.9581675,2.13086389 13.1202976,2.89580396 9.69403597,5.14032913 C6.83818733,7.01117991 5.46885511,9.11359981 5.50848138,11.7074507 C5.5219145,12.586754 6.05429493,13.5504113 7.10562269,14.5984226 C7.4393775,12.4421149 8.06340659,10.8285044 8.97770995,9.75759115 C9.89201332,8.68667789 11.2404975,7.64534569 13.0231626,6.63359455 C10.5695597,8.98708856 9.15459202,10.979178 8.77825968,12.6098629 C8.40192735,14.2405478 8.41104333,15.3469263 8.80560763,15.9289985 Z" id="Combined-Shape-Copy-2" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Lua icon</title>
<defs>
<path d="M10.3520891,2 L10.3520891,3.79011197 C8.61581127,3.83490503 7.22130691,4.52914353 7.22130691,5.96998687 L7.22130691,8.10833137 C7.22130691,9.71341592 6.37960225,11.5930873 4.69616767,11.8842422 L4.69616767,12.0186207 C6.37960225,12.3097756 7.22130691,13.8829731 7.22130691,15.4880576 L7.22130691,17.9428759 C7.22130691,18.7043579 7.44211284,19.2567973 7.88373133,19.6002108 C8.32534982,19.9436242 9.49904825,20.1713623 10.3520891,20.1862933 L10.3520891,22 C8.91777263,21.985069 7.17196328,21.7433437 6.38686374,21.1087754 C5.60176421,20.4742071 4.78557962,19.4352441 4.78557962,18.2855557 L4.78557962,15.1587647 C4.78557962,14.3823517 4.54778861,13.8280459 4.07219947,13.4958308 C3.59661033,13.1636156 2.90588408,12.9975105 2,12.9975105 L2,10.684195 C2.98137442,10.669264 3.69097297,10.4900945 4.12881694,10.146681 C4.56666091,9.80326759 4.78557962,9.27322436 4.78557962,8.55653545 L4.78557962,5.59798567 C4.78557962,4.4408317 5.61686205,3.68809925 6.43215772,3.04606543 C7.24745339,2.40403161 8.9781649,2 10.3520891,2 Z M13.6479109,2 C15.0218351,2 16.7525466,2.40403161 17.5678423,3.04606543 C18.383138,3.68809925 19.2144204,4.4408317 19.2144204,5.59798567 L19.2144204,5.59798567 L19.2144204,8.55653545 C19.2144204,9.27322436 19.4333391,9.80326759 19.8711831,10.146681 C20.309027,10.4900945 21.0186256,10.669264 22,10.684195 L22,10.684195 L22,12.9975105 C21.0941159,12.9975105 20.4033897,13.1636156 19.9278005,13.4958308 C19.4522114,13.8280459 19.2144204,14.3823517 19.2144204,15.1587647 L19.2144204,15.1587647 L19.2144204,18.2855557 C19.2144204,19.4352441 18.3982358,20.4742071 17.6131363,21.1087754 C16.8280367,21.7433437 15.0822274,21.985069 13.6479109,22 L13.6479109,22 L13.6479109,20.1862933 C14.5009518,20.1713623 15.6746502,19.9436242 16.1162687,19.6002108 C16.5578872,19.2567973 16.7786931,18.7043579 16.7786931,17.9428759 L16.7786931,17.9428759 L16.7786931,15.4880576 C16.7786931,13.8829731 17.6203978,12.3097756 19.3038323,12.0186207 L19.3038323,12.0186207 L19.3038323,11.8842422 C17.6203978,11.5930873 16.7786931,9.71341592 16.7786931,8.10833137 L16.7786931,8.10833137 L16.7786931,5.96998687 C16.7786931,4.52914353 15.3841887,3.83490503 13.6479109,3.79011197 L13.6479109,3.79011197 Z" id="path-1"></path>
</defs>
<g id="Lua-icon" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="Combined-Shape" fill="#FFFFFF" xlink:href="#path-1"></use>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Material editor icon</title>
<g id="Material-editor-icon" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Matieral-editor" transform="translate(2.000000, 2.000000)" fill="#FFFFFF" fill-rule="nonzero">
<path d="M10.0000677,0 C4.45458426,0 0,4.45451651 0,10.0000677 C0,15.5454835 4.45458426,20 10.0000677,20 C15.5455512,20 20.0000677,15.5454835 20.0000677,10.0000677 C20.0000677,4.45451651 15.5455512,0 10.0000677,0 L10.0000677,0 Z M17.3636715,6.36369179 L14.2727451,6.36369179 C14.0000542,4.90911431 13.6364437,3.45453683 13.0909263,2.3636376 C14.9091143,3.1818459 16.4546114,4.54550382 17.3636715,6.36369179 L17.3636715,6.36369179 Z M12.7273158,10.0000677 C12.7273158,10.6363692 12.7273158,11.2727384 12.6363963,11.818188 L7.36367147,11.818188 C7.27275191,11.2727384 7.27275191,10.6363692 7.27275191,10.0000677 C7.27275191,9.36363082 7.27275191,8.72726164 7.36367147,8.18181202 L12.6363963,8.18181202 C12.7273158,8.72726164 12.7273158,9.36363082 12.7273158,10.0000677 L12.7273158,10.0000677 Z M10.0000677,18.181812 C9.18185945,18.181812 8.09096021,16.4545436 7.54551059,13.636376 L12.3637053,13.636376 C11.9091753,16.4545436 10.8182083,18.181812 10.0000677,18.181812 L10.0000677,18.181812 Z M7.54551059,6.36369179 C8.09096021,3.54545639 9.18185945,1.81818798 10.0000677,1.81818798 C10.8182083,1.81818798 11.9091753,3.54545639 12.4545572,6.36369179 L7.54551059,6.36369179 Z M7.00006097,2.3636376 C6.45454361,3.45453683 6.00001355,4.90911431 5.72732262,6.36369179 L2.63639628,6.36369179 C3.54552414,4.54550382 5.09095344,3.1818459 7.00006097,2.3636376 L7.00006097,2.3636376 Z M2.0000271,8.18181202 L5.45456393,8.18181202 L5.45456393,10.0000677 C5.45456393,10.6363692 5.45456393,11.181812 5.54548349,11.818188 L2.0000271,11.818188 C1.90910754,11.2727384 1.81818798,10.6363692 1.81818798,10.0000677 C1.81818798,9.36363082 1.90910754,8.72726164 2.0000271,8.18181202 L2.0000271,8.18181202 Z M2.63639628,13.636376 L5.72732262,13.636376 C6.00001355,15.0908857 6.36369179,16.5454632 6.90914141,17.6363624 C5.09095344,16.8181541 3.54552414,15.4545639 2.63639628,13.636376 L2.63639628,13.636376 Z M13.0000068,17.6363624 C13.5454564,16.5454632 14.0000542,15.181873 14.1818933,13.636376 L17.2727519,13.636376 C16.4546114,15.4545639 14.9091143,16.8181541 13.0000068,17.6363624 L13.0000068,17.6363624 Z M18.0000406,11.818188 L14.5455038,11.818188 C14.6364234,11.181812 14.6364234,10.6363692 14.6364234,10.0000677 C14.6364234,9.36363082 14.6364234,8.8181812 14.5455038,8.18181202 L18.0000406,8.18181202 C18.0909602,8.72726164 18.1818798,9.36363082 18.1818798,10.0000677 C18.1818798,10.6363692 18.0909602,11.2727384 18.0000406,11.818188 L18.0000406,11.818188 Z" id="Fill-4"></path>
<path d="M10.0000677,18.8342378 L10.0000677,9.74336565 L10.0000677,0.652425764 C10.0000677,-2.07482233 20.0000677,4.28880172 20.0000677,9.74336565 C20.0000677,15.1978618 10.0000677,21.5615536 10.0000677,18.8342378" id="Fill-6"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>SC icon</title>
<g id="SC-icon" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M10.6929214,4.51764023 L2.96130718,4.51764023 L2.96130718,7.96706695 C2.96130718,8.28694683 3.20917431,8.54770094 3.51331968,8.54770094 L10.1409089,8.54770094 C10.4487844,8.54770094 10.6929214,8.29023965 10.6929214,7.96706695 L10.6929214,4.51764023 Z M14.2332467,16.4348559 L14.2332467,20.1725393 C14.2332467,20.4924192 14.4811138,20.7531733 14.7852592,20.7531733 L21.4128484,20.7531733 C21.7207239,20.7531733 21.9648609,20.495712 21.9648609,20.1725393 L21.9648609,16.4348559 L14.2332467,16.4348559 Z M13.2835051,19.1726688 C10.2249629,19.1726688 8.77405507,18.054406 8.77405507,16.1545309 C8.77405507,13.0382457 10.3731227,12.1561574 12.1046383,10.771073 C13.1119101,9.96532998 15.7122691,8.72781013 15.4186287,7.15556325 C15.0742932,5.31187844 13.7887132,5.54159503 12.960578,5.54159503 C12.5216884,5.54159503 12.3086302,5.54159503 11.7395986,5.54159503 L11.7395986,8.55852782 C11.7395986,9.19716613 11.2523897,9.71488477 10.6393701,9.71488477 L3.10022852,9.71488477 C2.49258909,9.71488477 2,9.19561124 2,8.55852782 L2,3.15635695 C2,2.51771864 2.48720892,2 3.10022852,2 L10.6393701,2 C11.2470095,2 11.7395986,2.51927353 11.7395986,3.15635695 L11.7395986,4.28718075 C11.8084605,4.30424636 12.8206778,4.28718075 13.2914795,4.28718075 C14.5272025,4.28718075 16.430931,4.71571873 16.7735044,7.15556325 C17.1046429,9.5139666 13.8637795,11.1020402 12.960578,11.7649711 C12.0573765,12.4279019 9.98473571,13.9400026 10.3731227,16.1545309 C10.5917354,17.4010303 11.1401369,17.4935013 13.2835051,17.616248 L13.2835051,15.0963595 C13.2835051,14.4577212 13.770714,13.9400026 14.3837336,13.9400026 L21.8997715,13.9400026 C22.5074109,13.9400026 23,14.4592761 23,15.0963595 L23,20.8436431 C23,21.4822814 22.5127911,22 21.8997715,22 L14.3837336,22 C13.7760942,22 13.2835051,21.4807265 13.2835051,20.8436431 L13.2835051,19.1726688 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>trackview icon</title>
<g id="trackview-icon" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M16.823,12.203 L21.02,17.385 L22.2679088,17.3858875 C22.5440511,17.3858875 22.7679088,17.6097451 22.7679088,17.8858875 L22.7679088,21.8858875 C22.7679088,22.1620299 22.5440511,22.3858875 22.2679088,22.3858875 L18.2679088,22.3858875 C17.9917664,22.3858875 17.7679088,22.1620299 17.7679088,21.8858875 L17.7679088,17.8858875 C17.7679088,17.6097451 17.9917664,17.3858875 18.2679088,17.3858875 L18.446,17.385 L15.6897008,13.9821947 C16.0317886,13.6205494 16.3299516,13.0552321 16.7322326,12.3605204 L16.823,12.203 Z M21.2679088,18.3858875 L19.2679088,18.3858875 C19.0224489,18.3858875 18.8183004,18.5627627 18.7759644,18.7960119 L18.7679088,18.8858875 L18.7679088,20.8858875 C18.7679088,21.1313474 18.9447839,21.3354959 19.1780331,21.3778318 L19.2679088,21.3858875 L21.2679088,21.3858875 C21.5133687,21.3858875 21.7175171,21.2090124 21.7598531,20.9757631 L21.7679088,20.8858875 L21.7679088,18.8858875 C21.7679088,18.6097451 21.5440511,18.3858875 21.2679088,18.3858875 Z M6.5,7.5 C6.77614237,7.5 7,7.72385763 7,8 L7,12 C7,12.0920475 6.97512693,12.1782855 6.93173534,12.2523596 L8.899,14.864 L12.509852,11.9428149 C12.7234766,12.2722403 12.9272144,12.5724038 13.1110529,12.8336411 C13.2808268,13.0748924 13.4477424,13.3538315 13.6273782,13.6099678 L8.64746364,17.6429264 L8.587,17.568 L8.48965074,17.6429264 L4.614,12.5 L2.5,12.5 C2.22385763,12.5 2,12.2761424 2,12 L2,8 C2,7.72385763 2.22385763,7.5 2.5,7.5 L6.5,7.5 Z M14.7503327,2 C16.9804584,2 19.2448408,4.31018839 18.680341,6.52966995 C18.3107266,7.98290763 16.9879633,10.228611 16.3314778,11.3623164 C15.7632532,12.3436012 15.4564503,13 14.7503327,13 C14.1697909,13 13.8202563,12.2587581 13.4539789,11.7382724 C12.5819908,10.499164 11.1465939,8.15794141 10.8399585,6.34792109 C10.456795,4.08616686 12.520207,2 14.7503327,2 Z M5.5,8.5 L3.5,8.5 C3.25454011,8.5 3.05039163,8.67687516 3.00805567,8.91012437 L3,9 L3,11 C3,11.2454599 3.17687516,11.4496084 3.41012437,11.4919443 L3.5,11.5 L5.5,11.5 C5.74545989,11.5 5.94960837,11.3231248 5.99194433,11.0898756 L6,11 L6,9 C6,8.72385763 5.77614237,8.5 5.5,8.5 Z M14.7630032,3.64006918 C13.4587768,3.64006918 12.2520482,4.86010532 12.4761306,6.1828288 C12.6554577,7.24136838 13.4949093,8.61056669 14.0048671,9.33522446 C14.2190741,9.63961592 14.4234896,10.0731104 14.7630032,10.0731104 C15.1759562,10.0731104 15.3553813,9.68923405 15.6876915,9.11535718 C16.0716186,8.45234135 16.8451996,7.13900479 17.0613582,6.28911952 C17.3914901,4.99111802 16.0672296,3.64006918 14.7630032,3.64006918 Z" id="Combined-Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>UI editor icon</title>
<g id="UI-editor-icon" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M17.2586018,20.75 C17.5703793,20.75 17.9274484,21.0083567 17.9274484,21.5 C17.9274484,21.9916433 17.5643628,22.25 17.2586018,22.25 L6.59549278,22.25 C6.28371519,22.25 5.92744838,22.0362554 5.92744838,21.5 C5.92744838,20.9637446 6.28973169,20.75 6.59549278,20.75 L17.2586018,20.75 Z M18.761301,18.25 C19.1702062,18.25 19.4375316,18.3951808 19.6411889,18.7003165 L22.1404492,21.4921472 C22.2376383,21.6401916 22.1701774,22.035686 21.6105938,22.1961567 C21.0510103,22.3566274 20.4982123,22.1211124 20.4029562,21.9760125 L18.1061133,19.6313487 L5.71849881,19.6313487 L3.57831926,21.8435327 C3.38399691,22.0873313 2.7574214,22.3578511 2.19881088,22.1961567 C1.64020036,22.0344623 1.590103,21.7248255 1.77134414,21.4921472 L4.31399299,18.6158681 C4.44159848,18.3952908 4.73156178,18.25 5.13095934,18.25 L18.761301,18.25 Z M21.3225516,1.75 C21.8748364,1.75 22.3225516,2.19771525 22.3225516,2.75 L22.3225516,16.25 C22.3225516,16.8022847 21.8748364,17.25 21.3225516,17.25 L2.82255162,17.25 C2.27026687,17.25 1.82255162,16.8022847 1.82255162,16.25 L1.82255162,2.75 C1.82255162,2.19771525 2.27026687,1.75 2.82255162,1.75 L21.3225516,1.75 Z M6.34022315,10.6954321 L8.86065341,14.1918975 L8.10519831,14.7407678 C8.03763155,14.7755937 7.97006479,14.8104197 7.8973133,14.8299937 L7.78364943,14.8460092 L5.1111786,14.9475958 C4.69666668,14.954558 4.37144365,14.5550509 4.4333144,14.158968 L4.46000935,14.0513382 L5.3853134,11.5175921 C5.41340273,11.4329953 5.45293841,11.364153 5.51491033,11.3030805 L5.58476804,11.2443023 L6.34022315,10.6954321 Z M16.6597247,8.52554056 L9.81373289,13.4994447 L7.28027754,10.0124426 L14.1262693,5.03853841 L16.6597247,8.52554056 Z M18.9647934,6.05595685 C19.168837,6.31373009 19.1620706,6.6550625 18.9646323,6.84607209 L18.8917853,6.90385362 L17.5997791,7.84255106 L15.0663237,4.35554892 L16.3583299,3.41685147 C16.5750186,3.25941794 16.9000316,3.31183746 17.1130757,3.52293254 L17.1872884,3.60943115 L18.9647934,6.05595685 Z" id="Combined-Shape-Copy-9" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -19,5 +19,14 @@
<file alias="resolution.svg">Menu/resolution.svg</file>
<file alias="debug.svg">Menu/debug.svg</file>
<file alias="camera.svg">Menu/camera.svg</file>
<file alias="asset_editor.svg">Menu/asset_editor.svg</file>
<file alias="audio_editor.svg">Menu/audio_editor.svg</file>
<file alias="emfx_editor.svg">Menu/emfx_editor.svg</file>
<file alias="landscape_canvas_editor.svg">Menu/landscape_canvas_editor.svg</file>
<file alias="lua_editor.svg">Menu/lua_editor.svg</file>
<file alias="material_editor.svg">Menu/material_editor.svg</file>
<file alias="script_canvas_editor.svg">Menu/script_canvas_editor.svg</file>
<file alias="trackview_editor.svg">Menu/trackview_editor.svg</file>
<file alias="ui_editor.svg">Menu/ui_editor.svg</file>
</qresource>
</RCC>

@ -22,7 +22,7 @@
#define AZ_TRAIT_DISABLE_FAILED_AP_CONNECTION_TESTS true
#define AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS true
#define AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS true
#define AZ_TRAIT_DISABLE_FAILED_ATOM_RPI_TESTS true
#define AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS true
#define AZ_TRAIT_DISABLE_FAILED_ZERO_COLOR_CONVERSION_TEST true

@ -916,9 +916,6 @@ namespace AzToolsFramework
eECMF_USE_VIEWPORT_CENTER = 0x2,
};
/// Populate global edit-time context menu.
virtual void PopulateEditorGlobalContextMenu(QMenu * /*menu*/, const AZ::Vector2& /*point*/, int /*flags*/) {}
/// Populate slice portion of edit-time context menu
virtual void PopulateEditorGlobalContextMenu_SliceSection(QMenu * /*menu*/, const AZ::Vector2& /*point*/, int /*flags*/) {}

@ -79,7 +79,7 @@ namespace AzToolsFramework
* This is the menu that appears when right clicking the main editor window,
* including the Entity Outliner and the Viewport.
*/
virtual void PopulateEditorGlobalContextMenu(QMenu* menu) const = 0;
virtual void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) = 0;
};
using EditorContextMenuBus = AZ::EBus<EditorContextMenuEvents>;

@ -187,13 +187,14 @@ namespace AzToolsFramework
return removedEntity;
}
void Instance::DetachNestedEntities(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback)
void Instance::DetachAllEntitiesInHierarchy(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback)
{
callback(AZStd::move(DetachContainerEntity()));
DetachEntities(callback);
for (const auto& [instanceAlias, instance] : m_nestedInstances)
{
instance->DetachNestedEntities(callback);
instance->DetachAllEntitiesInHierarchy(callback);
}
}

@ -87,7 +87,15 @@ namespace AzToolsFramework
bool AddEntity(AZ::Entity& entity, EntityAlias entityAlias);
AZStd::unique_ptr<AZ::Entity> DetachEntity(const AZ::EntityId& entityId);
void DetachEntities(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback);
void DetachNestedEntities(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback);
/**
* Detaches all entities in the instance hierarchy.
* Includes all direct entities, all nested entities, and all container entities.
* Note that without container entities the hierarchy that remains cannot be used further without restoring new ones.
* @param callback A user provided callback that can be used to capture ownership and manipulate the detached entities.
*/
void DetachAllEntitiesInHierarchy(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback);
void RemoveNestedEntities(const AZStd::function<bool(const AZStd::unique_ptr<AZ::Entity>&)>& filter);
void Reset();

@ -154,7 +154,7 @@ namespace AzToolsFramework
InstanceOptionalReference owningInstanceReference = m_storingInstance->m_instanceEntityMapper->FindOwningInstance(entityId);
// Start with an empty alias to build out our reference path
// If we can't resolve this id we'll return a random new alias instead of a reference path
// If we can't resolve this id we'll return a new alias based on the entity ID instead of a reference path
AliasPath relativeEntityAliasPath;
if (!owningInstanceReference)
{
@ -162,7 +162,7 @@ namespace AzToolsFramework
"Prefab - EntityIdMapper: Entity with Id %s has no registered owning instance",
entityId.ToString().c_str());
return Instance::GenerateEntityAlias();
return AZStd::string::format("Entity_%s", entityId.ToString().c_str());
}
Instance* owningInstance = &(owningInstanceReference->get());

@ -38,11 +38,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils
// going to be used to create clones of the entities.
{
AzFramework::Spawnable::EntityList& entities = spawnable.GetEntities();
if (instance.HasContainerEntity())
{
entities.emplace_back(AZStd::move(instance.DetachContainerEntity()));
}
instance.DetachNestedEntities(
instance.DetachAllEntitiesInHierarchy(
[&entities](AZStd::unique_ptr<AZ::Entity> entity)
{
entities.emplace_back(AZStd::move(entity));

@ -1206,7 +1206,7 @@ namespace AzToolsFramework
Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::NotPushableOnSliceRoot)->
Attribute(AZ::Edit::Attributes::ReadOnly, &EditorTransform::m_locked)->
DataElement(AZ::Edit::UIHandlers::Default, &EditorTransform::m_rotate, "Rotate", "Local Rotation (Relative to parent) in degrees.")->
Attribute(AZ::Edit::Attributes::Step, 0.1f)->
Attribute(AZ::Edit::Attributes::Step, 1.0f)->
Attribute(AZ::Edit::Attributes::Suffix, " deg")->
Attribute(AZ::Edit::Attributes::ReadOnly, &EditorTransform::m_locked)->
Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::NotPushableOnSliceRoot)->

@ -26,6 +26,7 @@
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Commands/SelectionCommand.h>
#include <AzToolsFramework/Editor/EditorContextMenuBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
@ -544,8 +545,7 @@ namespace AzToolsFramework
QMenu* contextMenu = new QMenu(this);
// Populate global context menu.
EBUS_EVENT(EditorEvents::Bus,
PopulateEditorGlobalContextMenu,
AzToolsFramework::EditorContextMenuBus::Broadcast(&AzToolsFramework::EditorContextMenuEvents::PopulateEditorGlobalContextMenu,
contextMenu,
AZ::Vector2::CreateZero(),
EditorEvents::eECMF_HIDE_ENTITY_CREATION | EditorEvents::eECMF_USE_VIEWPORT_CENTER);

@ -119,7 +119,7 @@ namespace AzToolsFramework
return "Prefabs";
}
void PrefabIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu) const
void PrefabIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags)
{
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(

@ -65,7 +65,7 @@ namespace AzToolsFramework
// EditorContextMenuBus...
int GetMenuPosition() const override;
AZStd::string GetMenuIdentifier() const override;
void PopulateEditorGlobalContextMenu(QMenu* menu) const override;
void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override;
// EntityOutlinerSourceDropHandlingBus...
void HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const override;

@ -1130,6 +1130,16 @@ namespace AzToolsFramework
m_browseEdit->setAttachedButtonIcon(icon);
}
void PropertyAssetCtrl::SetBrowseButtonEnabled(bool enabled)
{
m_browseEdit->setEnabled(enabled);
}
void PropertyAssetCtrl::SetBrowseButtonVisible(bool visible)
{
m_browseEdit->setVisible(visible);
}
const QModelIndex PropertyAssetCtrl::GetSourceIndex(const QModelIndex& index)
{
if (!index.isValid())
@ -1360,6 +1370,22 @@ namespace AzToolsFramework
GUI->SetBrowseButtonIcon(QIcon(iconPath.c_str()));
}
}
else if (attrib == AZ_CRC_CE("BrowseButtonEnabled"))
{
bool enabled = true;
if (attrValue->Read<bool>(enabled))
{
GUI->SetBrowseButtonEnabled(enabled);
}
}
else if (attrib == AZ_CRC_CE("BrowseButtonVisible"))
{
bool visible = true;
if (attrValue->Read<bool>(visible))
{
GUI->SetBrowseButtonVisible(visible);
}
}
else if (attrib == AZ_CRC_CE("Thumbnail"))
{
bool showThumbnail = false;

@ -208,6 +208,8 @@ namespace AzToolsFramework
void SetEditButtonIcon(const QIcon& icon);
void SetEditButtonTooltip(QString tooltip);
void SetBrowseButtonIcon(const QIcon& icon);
void SetBrowseButtonEnabled(bool enabled);
void SetBrowseButtonVisible(bool visible);
void SetClearButtonEnabled(bool enable);
void SetClearButtonVisible(bool visible);

@ -188,7 +188,7 @@ namespace AzToolsFramework
virtual void AddEditMenuAction(QAction* action) = 0;
/// Add an action to the Editor menu.
virtual void AddMenuAction(AZStd::string_view categoryId, QAction* action) = 0;
virtual void AddMenuAction(AZStd::string_view categoryId, QAction* action, bool addToToolsToolbar) = 0;
/// (Re)populate the default EditMenu.
/// Restore the EditMenu to its default state (the options available when first opening a level).

@ -13,6 +13,7 @@
#include "EditorContextMenu.h"
#include "AzToolsFramework/Viewport/ViewportMessages.h"
#include "Editor/EditorContextMenuBus.h"
namespace AzToolsFramework
{
@ -55,8 +56,7 @@ namespace AzToolsFramework
// Populate global context menu.
const int contextMenuFlag = 0;
EditorEvents::Bus::BroadcastReverse(
&EditorEvents::PopulateEditorGlobalContextMenu, contextMenu.m_menu.data(),
AzToolsFramework::EditorContextMenuBus::Broadcast(&AzToolsFramework::EditorContextMenuEvents::PopulateEditorGlobalContextMenu, contextMenu.m_menu.data(),
AzFramework::Vector2FromScreenPoint(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates),
contextMenuFlag);

@ -1018,6 +1018,7 @@ namespace AzToolsFramework
EditorEntityVisibilityNotificationBus::Router::BusRouterConnect();
EditorEntityLockComponentNotificationBus::Router::BusRouterConnect();
EditorManipulatorCommandUndoRedoRequestBus::Handler::BusConnect(entityContextId);
EditorContextMenuBus::Handler::BusConnect();
CreateTransformModeSelectionCluster();
CreateSpaceSelectionCluster();
@ -1038,6 +1039,7 @@ namespace AzToolsFramework
m_pivotOverrideFrame.Reset();
EditorContextMenuBus::Handler::BusConnect();
EditorManipulatorCommandUndoRedoRequestBus::Handler::BusDisconnect();
EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect();
EditorEntityVisibilityNotificationBus::Router::BusRouterDisconnect();
@ -3097,15 +3099,25 @@ namespace AzToolsFramework
}
}
void EditorTransformComponentSelection::PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& /*point*/, const int /*flags*/)
int EditorTransformComponentSelection::GetMenuPosition() const
{
QAction* action = menu->addAction(QObject::tr(s_togglePivotTitleRightClick));
QObject::connect(
action, &QAction::triggered, action,
[this]()
{
ToggleCenterPivotSelection();
});
return aznumeric_cast<int>(EditorContextMenuOrdering::BOTTOM);
}
AZStd::string EditorTransformComponentSelection::GetMenuIdentifier() const
{
return "Transform Component";
}
void EditorTransformComponentSelection::PopulateEditorGlobalContextMenu(QMenu* menu, [[maybe_unused]] const AZ::Vector2& point, [[maybe_unused]] int flags)
{
QAction* action = menu->addAction(QObject::tr(s_togglePivotTitleRightClick));
QObject::connect(
action, &QAction::triggered, action,
[this]()
{
ToggleCenterPivotSelection();
});
}
void EditorTransformComponentSelection::BeforeEntitySelectionChanged()

@ -22,6 +22,7 @@
#include <AzToolsFramework/API/EditorCameraBus.h>
#include <AzToolsFramework/Commands/EntityManipulatorCommand.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/Editor/EditorContextMenuBus.h>
#include <AzToolsFramework/Manipulators/BaseManipulator.h>
#include <AzToolsFramework/ToolsComponents/EditorLockComponentBus.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityBus.h>
@ -126,6 +127,7 @@ namespace AzToolsFramework
//! Provide a suite of functionality for manipulating entities, primarily through their TransformComponent.
class EditorTransformComponentSelection
: public ViewportInteraction::ViewportSelectionRequests
, public EditorContextMenuBus::Handler
, private EditorEventsBus::Handler
, private EditorTransformComponentSelectionRequestBus::Handler
, private ToolsApplicationNotificationBus::Handler
@ -238,8 +240,12 @@ namespace AzToolsFramework
void UndoRedoEntityManipulatorCommand(
AZ::u8 pivotOverride, const AZ::Transform& transform, AZ::EntityId entityId) override;
// EditorContextMenuBus...
void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2 & point, int flags) override;
int GetMenuPosition() const override;
AZStd::string GetMenuIdentifier() const override;
// EditorEventsBus ...
void PopulateEditorGlobalContextMenu(QMenu *menu, const AZ::Vector2& point, int flags) override;
void OnEscape() override;
// ToolsApplicationNotificationBus ...

@ -71,24 +71,29 @@ namespace UnitTest
m_prefabSystemComponent->CreatePrefab({ entitiesCreated[0] }, {}, "test/path1"));
ASSERT_TRUE(firstInstance);
ASSERT_TRUE(firstInstance->HasContainerEntity());
expectedEntityNameSet.insert(firstInstance->GetContainerEntity()->get().GetName());
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> secondInstance(
m_prefabSystemComponent->CreatePrefab({ entitiesCreated[1] }, MakeInstanceList(AZStd::move(firstInstance)), "test/path2"));
ASSERT_TRUE(secondInstance);
ASSERT_TRUE(secondInstance->HasContainerEntity());
expectedEntityNameSet.insert(secondInstance->GetContainerEntity()->get().GetName());
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> thirdInstance(
m_prefabSystemComponent->CreatePrefab({ entitiesCreated[2] }, MakeInstanceList(AZStd::move(secondInstance)), "test/path3"));
ASSERT_TRUE(thirdInstance);
ASSERT_TRUE(thirdInstance->HasContainerEntity());
auto& containerEntity = thirdInstance->GetContainerEntity()->get();
expectedEntityNameSet.insert(containerEntity.GetName());
expectedEntityNameSet.insert(thirdInstance->GetContainerEntity()->get().GetName());
//Create Spawnable
auto& prefabDom = m_prefabSystemComponent->FindTemplateDom(thirdInstance->GetTemplateId());
AzFramework::Spawnable spawnable;
AzToolsFramework::Prefab::SpawnableUtils::CreateSpawnable(spawnable, prefabDom);
EXPECT_EQ(spawnable.GetEntities().size() - 1, normalEntityCount); // 1 for container entity
EXPECT_EQ(spawnable.GetEntities().size(), normalEntityCount + 3); // +1 for each container entity
const auto& spawnableEntities = spawnable.GetEntities();
AZStd::unordered_set<AZStd::string> actualEntityNameSet;
@ -97,6 +102,6 @@ namespace UnitTest
actualEntityNameSet.insert(spawnableEntity->GetName());
}
EXPECT_EQ(expectedEntityNameSet, actualEntityNameSet);
EXPECT_EQ(actualEntityNameSet, expectedEntityNameSet);
}
}

@ -213,7 +213,7 @@ namespace UnitTest
AZStd::unique_ptr<Instance> convertedInstance(aznew Instance());
ASSERT_TRUE(AzToolsFramework::Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(*convertedInstance, m_prefabDom));
convertedInstance->DetachNestedEntities(
convertedInstance->DetachAllEntitiesInHierarchy(
[this](AZStd::unique_ptr<AZ::Entity> entity)
{
m_runtimeEntities.emplace_back(entity.release());

@ -117,6 +117,8 @@ void AssetEditorWindow::RegisterViewClass()
{
AzToolsFramework::ViewPaneOptions options;
options.preferedDockingArea = Qt::LeftDockWidgetArea;
options.showOnToolsToolbar = true;
options.toolbarIcon = ":/Menu/asset_editor.svg";
AzToolsFramework::RegisterViewPane<AssetEditorWindow>(LyViewPane::AssetEditor, LyViewPane::CategoryTools, options);
}

@ -13,8 +13,6 @@ if(NOT PAL_TRAIT_BUILD_HOST_TOOLS)
return()
endif()
include(${CMAKE_CURRENT_SOURCE_DIR}/Platform/${PAL_PLATFORM_NAME}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
ly_add_target(
NAME EditorCore SHARED
NAMESPACE Legacy
@ -161,7 +159,7 @@ ly_add_source_properties(
# Editor
################################################################################
ly_add_target(
NAME Editor ${PAL_TRAIT_BUILD_EDITOR_APPLICATION_TYPE} # Mac Editor is a bare executable on Atom until we can fix packaging of the shader compilers. DO NOT MERGE BACK TO MAINLINE
NAME Editor APPLICATION
NAMESPACE Legacy
AUTORCC
FILES_CMAKE

@ -1292,7 +1292,7 @@ void LevelEditorMenuHandler::AddEditMenuAction(QAction* action)
}
}
void LevelEditorMenuHandler::AddMenuAction(AZStd::string_view categoryId, QAction* action)
void LevelEditorMenuHandler::AddMenuAction(AZStd::string_view categoryId, QAction* action, bool addToToolsToolbar)
{
auto menuWrapper = m_actionManager->FindMenu(categoryId.data());
if (menuWrapper.isNull())
@ -1301,6 +1301,11 @@ void LevelEditorMenuHandler::AddMenuAction(AZStd::string_view categoryId, QActio
return;
}
menuWrapper.Get()->addAction(action);
if (addToToolsToolbar)
{
m_mainWindow->GetToolbarManager()->AddButtonToEditToolbar(action);
}
}
void LevelEditorMenuHandler::RestoreEditMenuToDefault()

@ -98,7 +98,7 @@ private:
// EditorMenuRequestBus
void AddEditMenuAction(QAction* action) override;
void AddMenuAction(AZStd::string_view categoryId, QAction* action) override;
void AddMenuAction(AZStd::string_view categoryId, QAction* action, bool addToToolsToolbar) override;
void RestoreEditMenuToDefault() override;
MainWindow* m_mainWindow;

@ -1139,7 +1139,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
const QString oldLevelPattern = QDir(oldLevelFolder).absoluteFilePath("*.*");
const QString oldLevelName = Path::GetFile(GetLevelPathName());
const QString oldLevelXml = Path::ReplaceExtension(oldLevelName, "xml");
AZ::IO::ArchiveFileIterator findHandle = pIPak->FindFirst(oldLevelPattern.toUtf8().data(), 0, true);
AZ::IO::ArchiveFileIterator findHandle = pIPak->FindFirst(oldLevelPattern.toUtf8().data(), AZ::IO::IArchive::eFileSearchType_AllowOnDiskAndInZips);
if (findHandle)
{
do

@ -2664,6 +2664,18 @@ bool EditorViewportWidget::GetActiveCameraPosition(AZ::Vector3& cameraPos)
return false;
}
bool EditorViewportWidget::GetActiveCameraState(AzFramework::CameraState& cameraState)
{
if (m_pPrimaryViewport == this)
{
cameraState = GetCameraState();
return true;
}
return false;
}
void EditorViewportWidget::OnStartPlayInEditor()
{
if (m_viewEntityId.IsValid())

@ -184,6 +184,7 @@ public:
void SetViewAndMovementLockFromEntityPerspective(const AZ::EntityId& entityId, bool lockCameraMovement) override;
AZ::EntityId GetCurrentViewEntityId() override { return m_viewEntityId; }
bool GetActiveCameraPosition(AZ::Vector3& cameraPos) override;
bool GetActiveCameraState(AzFramework::CameraState& cameraState) override;
// AzToolsFramework::EditorEntityContextNotificationBus (handler moved to cpp to resolve link issues in unity builds)
virtual void OnStartPlayInEditor();

@ -627,6 +627,8 @@ void CGameEngine::SwitchToInGame()
&AzFramework::InputSystemCursorRequests::SetSystemCursorState,
AzFramework::SystemCursorState::ConstrainedAndHidden);
}
Log("Entered game mode");
}
void CGameEngine::SwitchToInEditor()
@ -681,6 +683,8 @@ void CGameEngine::SwitchToInEditor()
AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
&AzFramework::InputSystemCursorRequests::SetSystemCursorState,
AzFramework::SystemCursorState::UnconstrainedAndVisible);
Log("Exited game mode");
}
void CGameEngine::HandleQuitRequest(IConsoleCmdArgs* /*args*/)

@ -833,13 +833,6 @@ void MainWindow::InitActions()
.Connect(&QAction::triggered, []() { SandboxEditor::SetAngleSnapping(!SandboxEditor::AngleSnappingEnabled()); });
// Display actions
am->AddAction(ID_WIREFRAME, tr("&Wireframe"))
.SetShortcut(tr("F3"))
.SetToolTip(tr("Wireframe (F3)"))
.SetCheckable(true)
.SetStatusTip(tr("Render in Wireframe Mode."))
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateWireframe);
am->AddAction(ID_SWITCHCAMERA_DEFAULTCAMERA, tr("Default Camera")).SetCheckable(true)
.RegisterUpdateCallback(cryEdit, &CCryEditApp::OnUpdateSwitchToDefaultCamera);
am->AddAction(ID_SWITCHCAMERA_SEQUENCECAMERA, tr("Sequence Camera")).SetCheckable(true)

@ -1,13 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set (PAL_TRAIT_BUILD_EDITOR_APPLICATION_TYPE APPLICATION)

@ -1,13 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set (PAL_TRAIT_BUILD_EDITOR_APPLICATION_TYPE APPLICATION)

@ -1,13 +0,0 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set (PAL_TRAIT_BUILD_EDITOR_APPLICATION_TYPE APPLICATION)

@ -49,6 +49,7 @@
// AzToolsFramework
#include <AzToolsFramework/API/ComponentEntityObjectBus.h>
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
#include <AzToolsFramework/Editor/EditorContextMenuBus.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
@ -112,20 +113,20 @@ static const char TextCantCreateCameraNoLevel[] = "Cannot create camera when no
class EditorEntityNotifications
: public AzToolsFramework::EditorEntityContextNotificationBus::Handler
, public AzToolsFramework::EditorEvents::Bus::Handler
, public AzToolsFramework::EditorContextMenuBus::Handler
{
public:
EditorEntityNotifications(CRenderViewport& renderViewport)
: m_renderViewport(renderViewport)
{
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect();
AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
AzToolsFramework::EditorContextMenuBus::Handler::BusConnect();
}
~EditorEntityNotifications() override
{
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
AzToolsFramework::EditorContextMenuBus::Handler::BusDisconnect();
}
// AzToolsFramework::EditorEntityContextNotificationBus
@ -138,7 +139,7 @@ public:
m_renderViewport.OnStopPlayInEditor();
}
// AzToolsFramework::EditorEvents::Bus
// AzToolsFramework::EditorContextMenu::Bus
void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override
{
m_renderViewport.PopulateEditorGlobalContextMenu(menu, point, flags);

@ -179,7 +179,7 @@ public:
virtual void OnStartPlayInEditor();
virtual void OnStopPlayInEditor();
// AzToolsFramework::EditorEvents::Bus (handler moved to cpp to resolve link issues in unity builds)
// AzToolsFramework::EditorContextMenu::Bus (handler moved to cpp to resolve link issues in unity builds)
// We use this to determine when the viewport context menu is being displayed so we can exit move mode
void PopulateEditorGlobalContextMenu(QMenu* /*menu*/, const AZ::Vector2& /*point*/, int /*flags*/);

@ -116,6 +116,8 @@ void CTrackViewDialog::RegisterViewClass()
AzToolsFramework::ViewPaneOptions opts;
opts.shortcut = QKeySequence(Qt::Key_T);
opts.isDisabledInSimMode = true;
opts.showOnToolsToolbar = true;
opts.toolbarIcon = ":/Menu/trackview_editor.svg";
AzToolsFramework::RegisterViewPane<CTrackViewDialog>(LyViewPane::TrackView, LyViewPane::CategoryTools, opts);
GetIEditor()->GetSettingsManager()->AddToolName(s_kTrackViewLayoutSection, LyViewPane::TrackView);

@ -154,11 +154,6 @@ void CViewportTitleDlg::SetupCameraDropdownMenu()
QAction* gotoPositionAction = new QAction("Go to position", cameraMenu);
connect(gotoPositionAction, &QAction::triggered, this, &CViewportTitleDlg::OnBnClickedGotoPosition);
cameraMenu->addAction(gotoPositionAction);
m_syncPlayerToCameraAction = new QAction("Sync camera to player", cameraMenu);
m_syncPlayerToCameraAction->setCheckable(true);
connect(m_syncPlayerToCameraAction, &QAction::triggered, this, &CViewportTitleDlg::OnBnClickedSyncplayer);
cameraMenu->addAction(m_syncPlayerToCameraAction);
cameraMenu->addSeparator();
auto cameraSpeedActionWidget = new QWidgetAction(cameraMenu);
@ -238,21 +233,15 @@ void CViewportTitleDlg::SetupOverflowMenu()
overFlowMenu->addAction(m_enableGridSnappingAction);
m_gridSizeActionWidget = new QWidgetAction(overFlowMenu);
auto gridSizeContainer = new QWidget(overFlowMenu);
auto gridSizeLabel = new QLabel(tr("Grid Size"), overFlowMenu);
m_gridSpinBox = new AzQtComponents::DoubleSpinBox();
m_gridSpinBox->setValue(SandboxEditor::GridSnappingSize());
m_gridSpinBox->setMinimum(1e-2f);
m_gridSpinBox->setToolTip(tr("Grid size"));
QObject::connect(
m_gridSpinBox, QOverload<double>::of(&AzQtComponents::DoubleSpinBox::valueChanged), this, &CViewportTitleDlg::OnGridSpinBoxChanged);
QHBoxLayout* gridSizeLayout = new QHBoxLayout;
gridSizeLayout->addWidget(gridSizeLabel);
gridSizeLayout->addWidget(m_gridSpinBox);
gridSizeContainer->setLayout(gridSizeLayout);
m_gridSizeActionWidget->setDefaultWidget(gridSizeContainer);
m_gridSizeActionWidget->setDefaultWidget(m_gridSpinBox);
overFlowMenu->addAction(m_gridSizeActionWidget);
overFlowMenu->addSeparator();
@ -263,22 +252,16 @@ void CViewportTitleDlg::SetupOverflowMenu()
overFlowMenu->addAction(m_enableAngleSnappingAction);
m_angleSizeActionWidget = new QWidgetAction(overFlowMenu);
auto angleSizeContainer = new QWidget(overFlowMenu);
auto angleSizeLabel = new QLabel(tr("Angle Snapping"), overFlowMenu);
m_angleSpinBox = new AzQtComponents::DoubleSpinBox();
m_angleSpinBox->setValue(SandboxEditor::AngleSnappingSize());
m_angleSpinBox->setMinimum(1e-2f);
m_angleSpinBox->setToolTip(tr("Angle Snapping"));
QObject::connect(
m_angleSpinBox, QOverload<double>::of(&AzQtComponents::DoubleSpinBox::valueChanged), this,
&CViewportTitleDlg::OnAngleSpinBoxChanged);
QHBoxLayout* angleSizeLayout = new QHBoxLayout;
angleSizeLayout->addWidget(angleSizeLabel);
angleSizeLayout->addWidget(m_angleSpinBox);
angleSizeContainer->setLayout(angleSizeLayout);
m_angleSizeActionWidget->setDefaultWidget(angleSizeContainer);
m_angleSizeActionWidget->setDefaultWidget(m_angleSpinBox);
overFlowMenu->addAction(m_angleSizeActionWidget);
m_ui->m_overflowBtn->setMenu(overFlowMenu);
@ -850,14 +833,6 @@ bool CViewportTitleDlg::eventFilter(QObject* object, QEvent* event)
return QWidget::eventFilter(object, event) || consumeEvent;
}
void CViewportTitleDlg::OnBnClickedSyncplayer()
{
emit ActionTriggered(ID_GAME_SYNCPLAYER);
bool bSyncPlayer = GetIEditor()->GetGameEngine()->IsSyncPlayerPosition();
m_syncPlayerToCameraAction->setChecked(!bSyncPlayer);
}
void CViewportTitleDlg::OnBnClickedGotoPosition()
{
emit ActionTriggered(ID_DISPLAY_GOTOPOSITION);

@ -115,10 +115,10 @@ protected:
float m_prevMoveSpeed;
// Speed combobox/lineEdit settings
double m_minSpeed = 0.1;
double m_minSpeed = 0.01;
double m_maxSpeed = 100.0;
double m_speedStep = 0.1;
int m_numDecimals = 1;
double m_speedStep = 0.01;
int m_numDecimals = 3;
// Speed presets
float m_speedPresetValues[3] = { 0.1f, 1.0f, 10.0f };
@ -143,7 +143,6 @@ protected:
void SetFullViewportInfo();
void SetCompactViewportInfo();
void OnBnClickedSyncplayer();
void OnBnClickedGotoPosition();
void OnBnClickedMuteAudio();
void OnBnClickedEnableVR();
@ -174,7 +173,6 @@ protected:
QAction* m_fullInformationAction = nullptr;
QAction* m_compactInformationAction = nullptr;
QAction* m_debugHelpersAction = nullptr;
QAction* m_syncPlayerToCameraAction = nullptr;
QAction* m_audioMuteAction = nullptr;
QAction* m_enableVRAction = nullptr;
QAction* m_enableGridSnappingAction = nullptr;

@ -167,7 +167,7 @@ void SandboxIntegrationManager::Setup()
AzToolsFramework::ToolsApplicationEvents::Bus::Handler::BusConnect();
AzToolsFramework::EditorRequests::Bus::Handler::BusConnect();
AzToolsFramework::EditorWindowRequests::Bus::Handler::BusConnect();
AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
AzToolsFramework::EditorContextMenuBus::Handler::BusConnect();
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect();
AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler::BusConnect();
@ -384,7 +384,7 @@ void SandboxIntegrationManager::Teardown()
AzFramework::DisplayContextRequestBus::Handler::BusDisconnect();
AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
AzToolsFramework::EditorContextMenuBus::Handler::BusDisconnect();
AzToolsFramework::EditorWindowRequests::Bus::Handler::BusDisconnect();
AzToolsFramework::EditorRequests::Bus::Handler::BusDisconnect();
AzToolsFramework::ToolsApplicationEvents::Bus::Handler::BusDisconnect();
@ -589,6 +589,11 @@ void SandboxIntegrationManager::OnSaveLevel()
m_unsavedEntities.clear();
}
int SandboxIntegrationManager::GetMenuPosition() const
{
return aznumeric_cast<int>(AzToolsFramework::EditorContextMenuOrdering::TOP);
}
void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags)
{
if (!IsLevelDocumentOpen())
@ -662,13 +667,6 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con
SetupSliceContextMenu(menu);
}
else
{
menu->addSeparator();
// Allow handlers to append menu items to the context menu
AzToolsFramework::EditorContextMenuBus::Broadcast(&AzToolsFramework::EditorContextMenuEvents::PopulateEditorGlobalContextMenu, menu);
}
action = menu->addAction(QObject::tr("Duplicate"));
QObject::connect(action, &QAction::triggered, action, [this] { ContextMenu_Duplicate(); });

@ -23,6 +23,7 @@
#include <AzFramework/Viewport/DisplayContextRequestBus.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Editor/EditorContextMenuBus.h>
#include <AzToolsFramework/ToolsComponents/EditorLayerComponentBus.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzToolsFramework/UI/Layer/LayerUiHandler.h>
@ -97,7 +98,7 @@ class SandboxIntegrationManager
: private AzToolsFramework::ToolsApplicationEvents::Bus::Handler
, private AzToolsFramework::EditorRequests::Bus::Handler
, private AzToolsFramework::EditorPickModeNotificationBus::Handler
, private AzToolsFramework::EditorEvents::Bus::Handler
, private AzToolsFramework::EditorContextMenuBus::Handler
, private AzToolsFramework::EditorWindowRequests::Bus::Handler
, private AzFramework::AssetCatalogEventBus::Handler
, private AzFramework::DisplayContextRequestBus::Handler
@ -184,8 +185,9 @@ private:
void OnEntityPickModeStopped() override;
//////////////////////////////////////////////////////////////////////////
// AzToolsFramework::EditorEvents::Bus::Handler overrides
// AzToolsFramework::EditorContextMenu::Bus::Handler overrides
void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override;
int GetMenuPosition() const;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

@ -35,6 +35,7 @@
#include <AzQtComponents/Utilities/QtViewPaneEffects.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Commands/SelectionCommand.h>
#include <AzToolsFramework/Editor/EditorContextMenuBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
@ -530,8 +531,8 @@ void OutlinerWidget::contextMenuEvent(QContextMenuEvent* event)
QMenu* contextMenu = new QMenu(this);
// Populate global context menu.
EBUS_EVENT(AzToolsFramework::EditorEvents::Bus,
PopulateEditorGlobalContextMenu,
AzToolsFramework::EditorContextMenuBus::Broadcast(&AzToolsFramework::EditorContextMenuEvents::PopulateEditorGlobalContextMenu,
contextMenu,
AZ::Vector2::CreateZero(),
AzToolsFramework::EditorEvents::eECMF_HIDE_ENTITY_CREATION | AzToolsFramework::EditorEvents::eECMF_USE_VIEWPORT_CENTER);

@ -259,8 +259,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_googletest(
NAME AZ::AssetProcessor.Tests
TEST_COMMAND $<TARGET_FILE:AZ::AssetProcessor.Tests> --unittest
TEST_COMMAND $<TARGET_FILE:AZ::AssetProcessor.Tests> --unittest --gtest_filter=-*.SUITE_sandbox*
)
endif()

@ -11,9 +11,127 @@
*/
#include <native/FileWatcher/FileWatcher.h>
#include <QDirIterator>
#include <QHash>
#include <QMutex>
#include <unistd.h>
#include <sys/inotify.h>
static constexpr int s_handleToFolderMapLockTimeout = 1000; // 1 sec timeout for obtaining the handle to folder map lock
static constexpr size_t s_iNotifyMaxEntries = 1024 * 16; // Control the maximum number of entries (from inotify) that can be read at one time
static constexpr size_t s_iNotifyEventSize = sizeof(struct inotify_event);
static constexpr size_t s_iNotifyReadBufferSize = s_iNotifyMaxEntries * s_iNotifyEventSize;
struct FolderRootWatch::PlatformImplementation
{
PlatformImplementation() { }
PlatformImplementation() = default;
int m_iNotifyHandle = -1;
QMutex m_handleToFolderMapLock;
QHash<int, QString> m_handleToFolderMap;
bool Initialize()
{
if (m_iNotifyHandle < 0)
{
m_iNotifyHandle = inotify_init();
}
return (m_iNotifyHandle >= 0);
}
void Finalize()
{
if (m_iNotifyHandle >= 0)
{
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
{
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
return;
}
QHashIterator<int, QString> iter(m_handleToFolderMap);
while (iter.hasNext())
{
iter.next();
int watchHandle = iter.key();
inotify_rm_watch(m_iNotifyHandle, watchHandle);
}
m_handleToFolderMap.clear();
m_handleToFolderMapLock.unlock();
::close(m_iNotifyHandle);
m_iNotifyHandle = -1;
}
}
void AddWatchFolder(QString folder)
{
if (m_iNotifyHandle >= 0)
{
// Clean up the path before accepting it as a watch folder
QString cleanPath = QDir::cleanPath(folder);
// Add the folder to watch and track it
int watchHandle = inotify_add_watch(m_iNotifyHandle,
cleanPath.toUtf8().constData(),
IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY);
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
{
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
return;
}
m_handleToFolderMap[watchHandle] = cleanPath;
m_handleToFolderMapLock.unlock();
// Add all the subfolders to watch and track them
QDirIterator dirIter(folder, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
while (dirIter.hasNext())
{
QString dirName = dirIter.next();
if (dirName.endsWith("/.") || dirName.endsWith("/.."))
{
continue;
}
int watchHandle = inotify_add_watch(m_iNotifyHandle,
dirName.toUtf8().constData(),
IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY);
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
{
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
return;
}
m_handleToFolderMap[watchHandle] = dirName;
m_handleToFolderMapLock.unlock();
}
}
}
void RemoveWatchFolder(int watchHandle)
{
if (m_iNotifyHandle >= 0)
{
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
{
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
return;
}
QHash<int, QString>::iterator handleToRemove = m_handleToFolderMap.find(watchHandle);
if (handleToRemove != m_handleToFolderMap.end())
{
inotify_rm_watch(m_iNotifyHandle, watchHandle);
m_handleToFolderMap.erase(handleToRemove);
}
m_handleToFolderMapLock.unlock();
}
}
};
//////////////////////////////////////////////////////////////////////////////
@ -36,17 +154,86 @@ FolderRootWatch::~FolderRootWatch()
bool FolderRootWatch::Start()
{
// TODO: Implement for Linux
return false;
// inotify will be used by linux to monitor file changes within directories under the root folder
if (!m_platformImpl->Initialize())
{
return false;
}
m_platformImpl->AddWatchFolder(m_root);
m_shutdownThreadSignal = false;
m_thread = std::thread([this]() { WatchFolderLoop(); });
return true;
}
void FolderRootWatch::Stop()
{
// TODO: Implement for Linux
m_shutdownThreadSignal = true;
m_platformImpl->Finalize();
if (m_thread.joinable())
{
m_thread.join(); // wait for the thread to finish
m_thread = std::thread(); //destroy
}
}
void FolderRootWatch::WatchFolderLoop()
{
// TODO: Implement for Linux
char eventBuffer[s_iNotifyReadBufferSize];
while (!m_shutdownThreadSignal)
{
ssize_t bytesRead = ::read(m_platformImpl->m_iNotifyHandle, eventBuffer, s_iNotifyReadBufferSize);
if (bytesRead < 0)
{
// Break out of the loop when the notify handle was closed (outside of this thread)
break;
}
else if (bytesRead > 0)
{
for (size_t index=0; index<bytesRead;)
{
struct inotify_event *event = ( struct inotify_event * ) &eventBuffer[ index ];
const char* eventName = event->name;
if (event->mask & (IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVE ))
{
QString pathStr = QString("%1%2%3").arg(m_platformImpl->m_handleToFolderMap[event->wd], QDir::separator(), event->name);
if (event->mask & (IN_CREATE | IN_MOVED_TO))
{
if ( event->mask & IN_ISDIR )
{
// New Directory, add it to the watch
m_platformImpl->AddWatchFolder(pathStr);
}
else
{
ProcessNewFileEvent(pathStr);
}
}
else if (event->mask & (IN_DELETE | IN_MOVED_FROM))
{
if (event->mask & IN_ISDIR)
{
// Directory Deleted, remove it from the watch
m_platformImpl->RemoveWatchFolder(event->wd);
}
else
{
ProcessDeleteFileEvent(pathStr);
}
}
else if ((event->mask & IN_MODIFY) && ((event->mask & IN_ISDIR) != IN_ISDIR))
{
ProcessModifyFileEvent(pathStr);
}
}
index += s_iNotifyEventSize + event->len;
}
}
}
}

@ -1,134 +0,0 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <native/FileWatcher/FileWatcher.h>
#include <AzCore/PlatformIncl.h>
struct FolderRootWatch::PlatformImplementation
{
PlatformImplementation() : m_directoryHandle(nullptr), m_ioHandle(nullptr) { }
HANDLE m_directoryHandle;
HANDLE m_ioHandle;
};
//////////////////////////////////////////////////////////////////////////////
/// FolderWatchRoot
FolderRootWatch::FolderRootWatch(const QString rootFolder)
: m_root(rootFolder)
, m_shutdownThreadSignal(false)
, m_fileWatcher(nullptr)
, m_platformImpl(new PlatformImplementation())
{
}
FolderRootWatch::~FolderRootWatch()
{
// Destructor is required in here since this file contains the definition of struct PlatformImplementation
Stop();
delete m_platformImpl;
}
bool FolderRootWatch::Start()
{
m_platformImpl->m_directoryHandle = ::CreateFileW(m_root.toStdWString().data(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr);
if (m_platformImpl->m_directoryHandle != INVALID_HANDLE_VALUE)
{
m_platformImpl->m_ioHandle = ::CreateIoCompletionPort(m_platformImpl->m_directoryHandle, nullptr, 1, 0);
if (m_platformImpl->m_ioHandle != INVALID_HANDLE_VALUE)
{
m_shutdownThreadSignal = false;
m_thread = std::thread(std::bind(&FolderRootWatch::WatchFolderLoop, this));
return true;
}
}
return false;
}
void FolderRootWatch::Stop()
{
m_shutdownThreadSignal = true;
CloseHandle(m_platformImpl->m_ioHandle);
m_platformImpl->m_ioHandle = nullptr;
if (m_thread.joinable())
{
m_thread.join(); // wait for the thread to finish
m_thread = std::thread(); //destroy
}
CloseHandle(m_platformImpl->m_directoryHandle);
m_platformImpl->m_directoryHandle = nullptr;
}
void FolderRootWatch::WatchFolderLoop()
{
FILE_NOTIFY_INFORMATION aFileNotifyInformationList[50000];
QString path;
OVERLAPPED aOverlapped;
LPOVERLAPPED pOverlapped;
DWORD dwByteCount;
ULONG_PTR ulKey;
while (!m_shutdownThreadSignal)
{
::memset(aFileNotifyInformationList, 0, sizeof(aFileNotifyInformationList));
::memset(&aOverlapped, 0, sizeof(aOverlapped));
if (::ReadDirectoryChangesW(m_platformImpl->m_directoryHandle, aFileNotifyInformationList, sizeof(aFileNotifyInformationList), true, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_FILE_NAME, nullptr, &aOverlapped, nullptr))
{
//wait for up to a second for I/O to signal
dwByteCount = 0;
if (::GetQueuedCompletionStatus(m_platformImpl->m_ioHandle, &dwByteCount, &ulKey, &pOverlapped, INFINITE))
{
//if we are signaled to shutdown bypass
if (!m_shutdownThreadSignal && ulKey)
{
if (dwByteCount)
{
int offset = 0;
FILE_NOTIFY_INFORMATION* pFileNotifyInformation = aFileNotifyInformationList;
do
{
pFileNotifyInformation = (FILE_NOTIFY_INFORMATION*)((char*)pFileNotifyInformation + offset);
path.clear();
path.append(m_root);
path.append(QString::fromWCharArray(pFileNotifyInformation->FileName, pFileNotifyInformation->FileNameLength / 2));
QString file = QDir::toNativeSeparators(QDir::cleanPath(path));
switch (pFileNotifyInformation->Action)
{
case FILE_ACTION_ADDED:
case FILE_ACTION_RENAMED_NEW_NAME:
ProcessNewFileEvent(file);
break;
case FILE_ACTION_REMOVED:
case FILE_ACTION_RENAMED_OLD_NAME:
ProcessDeleteFileEvent(file);
break;
case FILE_ACTION_MODIFIED:
ProcessModifyFileEvent(file);
break;
}
offset = pFileNotifyInformation->NextEntryOffset;
} while (offset);
}
}
}
}
}
}

@ -445,10 +445,10 @@ namespace AssetProcessor
bool isExcludedDependency = dependencyPathSearch.starts_with(ExcludedDependenciesSymbol);
dependencyPathSearch = isExcludedDependency ? dependencyPathSearch.substr(1) : dependencyPathSearch;
bool isExactDependency = !AzFramework::StringFunc::Replace(dependencyPathSearch, '*', '%');
SanitizeForDatabase(dependencyPathSearch);
if (cleanedupDependency.m_dependencyType == AssetBuilderSDK::ProductPathDependencyType::ProductFile)
{
SanitizeForDatabase(dependencyPathSearch);
AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer productInfoContainer;
QString productNameWithPlatform = QString("%1%2%3").arg(platform.c_str(), AZ_CORRECT_DATABASE_SEPARATOR_STRING, dependencyPathSearch.c_str());
@ -508,6 +508,10 @@ namespace AssetProcessor
}
else
{
// For source assets, the casing of the input path must be maintained. Just fix up the path separators.
AZStd::replace(dependencyPathSearch.begin(), dependencyPathSearch.end(), AZ_WRONG_DATABASE_SEPARATOR, AZ_CORRECT_DATABASE_SEPARATOR);
AzFramework::StringFunc::Replace(dependencyPathSearch, AZ_DOUBLE_CORRECT_DATABASE_SEPARATOR, AZ_CORRECT_DATABASE_SEPARATOR_STRING);
// See if path matches any source files
AzToolsFramework::AssetDatabase::SourceDatabaseEntryContainer sourceInfoContainer;

@ -190,44 +190,55 @@ Please note that only those seed files will get updated that are active for your
void SourceFileRelocator::HandleMetaDataFiles(QStringList pathMatches, QHash<QString, int>& sourceIndexMap, const ScanFolderInfo* scanFolderInfo, SourceFileRelocationContainer& metadataFiles, bool excludeMetaDataFiles) const
{
QSet<QString> metaDataFileEntries;
for (QString file : pathMatches)
// Remove all the metadata files
if (excludeMetaDataFiles)
{
for (int idx = 0; idx < m_platformConfig->MetaDataFileTypesCount(); idx++)
{
QPair<QString, QString> metaInfo = m_platformConfig->GetMetaDataFileTypeAt(idx);
if (file.endsWith("." + metaInfo.first, Qt::CaseInsensitive))
pathMatches.erase(AZStd::remove_if(pathMatches.begin(), pathMatches.end(), [this](const QString& file)
{
//it is a metadata file
if (excludeMetaDataFiles)
{
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Metadata file %s will be ignored because --excludeMetadataFiles was specified in the command line.\n",
file.toUtf8().constData());
break; // don't check it against other metafile entries, we've already ascertained its a metafile.
}
else
for (int idx = 0; idx < m_platformConfig->MetaDataFileTypesCount(); idx++)
{
QString normalizedFilePath = AssetUtilities::NormalizeFilePath(file);
if (metaDataFileEntries.find(normalizedFilePath) == metaDataFileEntries.end())
const auto& [metadataType, extension] = m_platformConfig->GetMetaDataFileTypeAt(idx);
if (file.endsWith("." + metadataType, Qt::CaseInsensitive))
{
SourceFileRelocationInfo metaDataFile(file.toUtf8().data(), scanFolderInfo);
metaDataFile.m_isMetaDataFile = true;
metadataFiles.emplace_back(metaDataFile);
metaDataFileEntries.insert(normalizedFilePath);
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Metadata file %s will be ignored because --excludeMetadataFiles was specified in the command line.\n",
file.toUtf8().constData());
return true;
}
}
return false;
}),
pathMatches.end());
}
for (const QString& file : pathMatches)
{
for (int idx = 0; idx < m_platformConfig->MetaDataFileTypesCount(); idx++)
{
const auto& [metadataType, extension] = m_platformConfig->GetMetaDataFileTypeAt(idx);
if (file.endsWith("." + metadataType, Qt::CaseInsensitive))
{
const QString normalizedFilePath = AssetUtilities::NormalizeFilePath(file);
if (!metaDataFileEntries.contains(normalizedFilePath))
{
SourceFileRelocationInfo metaDataFile(file.toUtf8().data(), scanFolderInfo);
metaDataFile.m_isMetaDataFile = true;
metadataFiles.emplace_back(metaDataFile);
metaDataFileEntries.insert(normalizedFilePath);
}
}
else if (!excludeMetaDataFiles && (file.endsWith("." + metaInfo.second, Qt::CaseInsensitive) || metaInfo.second.isEmpty()))
else if (!excludeMetaDataFiles && (file.endsWith("." + extension, Qt::CaseInsensitive) || extension.isEmpty()))
{
// if we are here it implies that a metadata file might exists for this source file,
// add metadata file only if it exists and is not added already
AZStd::string metadataFilePath(file.toUtf8().data());
if (metaInfo.second.isEmpty())
if (extension.isEmpty())
{
metadataFilePath.append(AZStd::string::format(".%s", metaInfo.first.toUtf8().data()));
metadataFilePath.append(AZStd::string::format(".%s", metadataType.toUtf8().data()));
}
else
{
AZ::StringFunc::Path::ReplaceExtension(metadataFilePath, metaInfo.first.toUtf8().data());
AZ::StringFunc::Path::ReplaceExtension(metadataFilePath, metadataType.toUtf8().data());
};
// The metadata file can have a different case than the source file,

@ -1697,7 +1697,10 @@ namespace AssetProcessor
if(productFileInfo.absoluteDir().entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot).empty())
{
productFileInfo.absoluteDir().rmdir(".");
const QDir productDir = productFileInfo.absoluteDir();
QDir parentDir = productDir;
parentDir.cdUp();
successfullyRemoved &= parentDir.rmdir(productDir.dirName());
}
if (successfullyRemoved)

@ -28,7 +28,7 @@ namespace AssetProcessorMessagesTests
using namespace AssetProcessor;
using namespace AssetBuilderSDK;
static constexpr unsigned short AssetProcessorPort = static_cast<unsigned short>(888u);
static constexpr unsigned short AssetProcessorPort{65535u};
class AssetProcessorMessages;
@ -85,7 +85,6 @@ namespace AssetProcessorMessagesTests
public:
void SetUp() override
{
#if !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
AssetUtilities::ResetGameName();
m_temporarySourceDir = QDir(m_temporaryDir.path());
@ -166,12 +165,10 @@ namespace AssetProcessorMessagesTests
});
#endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
}
void TearDown() override
{
#if !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
QEventLoop eventLoop;
QObject::connect(m_batchApplicationManager->m_connectionManager, &ConnectionManager::ReadyToQuit, &eventLoop, &QEventLoop::quit);
@ -182,7 +179,6 @@ namespace AssetProcessorMessagesTests
m_assetSystemComponent->Deactivate();
m_batchApplicationManager->Destroy();
#endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
}
void RunNetworkRequest(AZStd::function<void()> func) const
@ -207,6 +203,7 @@ namespace AssetProcessorMessagesTests
thread.join();
}
protected:
MockAssetRequestHandler* m_assetRequestHandler{}; // Not owned, AP will delete this pointer
@ -226,11 +223,7 @@ namespace AssetProcessorMessagesTests
AZStd::unique_ptr<AzFramework::AssetSystem::BaseAssetProcessorMessage> m_response;
};
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(AssetProcessorMessages, DISABLED_All)
#else
TEST_F(AssetProcessorMessages, All)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
// Test that we can successfully send network messages and have them arrive for processing
// For messages that have a response, it also verifies the response comes back
@ -311,11 +304,7 @@ namespace AssetProcessorMessagesTests
});
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(AssetProcessorMessages, DISABLED_GetUnresolvedProductReferences_Succeeds)
#else
TEST_F(AssetProcessorMessages, GetUnresolvedProductReferences_Succeeds)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
using namespace AzToolsFramework::AssetDatabase;

@ -112,7 +112,6 @@ namespace UnitTests
SourceDatabaseEntry sourceFile8 = { m_data->m_scanFolder1.m_scanFolderID, "test.txt", AZ::Uuid::CreateRandom(), "AnalysisFingerprint" };
SourceDatabaseEntry sourceFile9 = { m_data->m_scanFolder1.m_scanFolderID, "duplicate/folder/file1.tif", AZ::Uuid::CreateRandom(), "AnalysisFingerprint" };
SourceDatabaseEntry sourceFile10 = { m_data->m_scanFolder1.m_scanFolderID, "folder/file.foo", AZ::Uuid::CreateRandom(), "AnalysisFingerprint" };
SourceDatabaseEntry sourceFile11 = { m_data->m_scanFolder1.m_scanFolderID, "testfolder/file.foo", AZ::Uuid::CreateRandom(), "AnalysisFingerprint" };
ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile1));
ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile2));
ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile3));
@ -123,7 +122,6 @@ namespace UnitTests
ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile8));
ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile9));
ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile10));
ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile11));
SourceFileDependencyEntry dependency1 = { AZ::Uuid::CreateRandom(), "subfolder1/somefile.tif", "subfolder1/otherfile.tif", SourceFileDependencyEntry::TypeOfDependency::DEP_SourceToSource, false };
SourceFileDependencyEntry dependency2 = { AZ::Uuid::CreateRandom(), "subfolder1/otherfile.tif", "otherfile.tif", SourceFileDependencyEntry::TypeOfDependency::DEP_JobToJob, false };
@ -176,8 +174,6 @@ namespace UnitTests
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/dummy/foo.metadataextension")));
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/folder/file.foo")));
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/folder/file.bar")));
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/testfolder/file.foo")));
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/testfolder/File.bar")));
if (AZ::IO::FileIOBase::GetInstance() == nullptr)
{
@ -487,20 +483,12 @@ namespace UnitTests
TestGetSourcesByPath("dev/", { }, false);
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(SourceFileRelocatorTest, DISABLED_GetSources_MultipleScanFolders_Fails)
#else
TEST_F(SourceFileRelocatorTest, GetSources_MultipleScanFolders_Fails)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
TestGetSourcesByPath("*", { }, false);
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(SourceFileRelocatorTest, DISABLED_GetSources_PartialPath_FailsWithNoResults)
#else
TEST_F(SourceFileRelocatorTest, GetSources_PartialPath_FailsWithNoResults)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
TestGetSourcesByPath("older/*", { }, false);
}
@ -547,18 +535,6 @@ namespace UnitTests
TestGetSourcesByPath(filePath.toUtf8().constData(), { "folder/file.foo" }, true, true);
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(SourceFileRelocatorTest, DISABLED_GetSources_HaveMetadataDifferentFileCase_AbsolutePath_Succeeds)
#else
TEST_F(SourceFileRelocatorTest, GetSources_HaveMetadataDifferentFileCase_AbsolutePath_Succeeds)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
QDir tempPath(m_tempDir.path());
auto filePath = QDir(tempPath.absoluteFilePath(m_data->m_scanFolder1.m_scanFolder.c_str())).absoluteFilePath("testfolder/file.foo");
TestGetSourcesByPath(filePath.toUtf8().constData(), { "testfolder/file.foo", "testfolder/File.bar" }, true, false);
}
TEST_F(SourceFileRelocatorTest, GetMetaDataFile_SingleFileWildcard_Succeeds)
{
QDir tempPath(m_tempDir.path());
@ -921,32 +897,46 @@ namespace UnitTests
ASSERT_FALSE(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.toUtf8().constData()));
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(SourceFileRelocatorTest, DISABLED_Delete_Real_Readonly_Fails)
#else
TEST_F(SourceFileRelocatorTest, Delete_Real_Readonly_Fails)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
struct AutoResetDirectoryReadOnlyState
{
AutoResetDirectoryReadOnlyState(QString dirName)
: m_dirName(AZStd::move(dirName))
{
AZ::IO::SystemFile::SetWritable(m_dirName.toUtf8().constData(), false);
}
~AutoResetDirectoryReadOnlyState()
{
AZ::IO::SystemFile::SetWritable(m_dirName.toUtf8().constData(), true);
}
AZ_DISABLE_COPY_MOVE(AutoResetDirectoryReadOnlyState)
private:
QString m_dirName;
};
QDir tempPath(m_tempDir.path());
auto filePath = QDir(tempPath.absoluteFilePath(m_data->m_scanFolder1.m_scanFolder.c_str())).absoluteFilePath("duplicate/file1.tif");
ASSERT_TRUE(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.toUtf8().constData()));
AutoResetDirectoryReadOnlyState readOnlyResetter(QFileInfo(filePath).absoluteDir().absolutePath());
AZ::IO::SystemFile::SetWritable(filePath.toUtf8().constData(), false);
auto result = m_data->m_reporter->Delete(filePath.toUtf8().constData(), false);
ASSERT_TRUE(result.IsSuccess());
EXPECT_TRUE(result.IsSuccess());
RelocationSuccess successResult = result.TakeValue();
ASSERT_EQ(successResult.m_moveSuccessCount, 0);
ASSERT_EQ(successResult.m_moveFailureCount, 1);
ASSERT_EQ(successResult.m_moveTotalCount, 1);
ASSERT_EQ(successResult.m_updateTotalCount, 0);
EXPECT_EQ(successResult.m_moveSuccessCount, 0);
EXPECT_EQ(successResult.m_moveFailureCount, 1);
EXPECT_EQ(successResult.m_moveTotalCount, 1);
EXPECT_EQ(successResult.m_updateTotalCount, 0);
ASSERT_TRUE(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.toUtf8().constData()));
EXPECT_TRUE(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.toUtf8().constData()));
}
TEST_F(SourceFileRelocatorTest, Delete_Real_WithDependencies_Fails)

@ -30,11 +30,7 @@ public:
friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDependencies);
friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDependencies_DeferredResolution);
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, DISABLED_AssetProcessed_Impl_MultiplatformDependencies_SourcePath);
#else
friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDependencies_SourcePath);
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, DeleteFolder_SignalsDeleteOfContainedFiles);
@ -70,19 +66,11 @@ public:
friend class GTEST_TEST_CLASS_NAME_(AbsolutePathProductDependencyTest, UnresolvedProductPathDependency_AssetProcessedTwice_ValidatePathDependenciesMap);
friend class GTEST_TEST_CLASS_NAME_(AbsolutePathProductDependencyTest, UnresolvedSourceFileTypeProductPathDependency_DependencyHasNoProductOutput_ValidatePathDependenciesMap);
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, DISABLED_ModtimeSkipping_FileUnchanged_WithoutModtimeSkipping);
#else
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_FileUnchanged_WithoutModtimeSkipping);
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_FileUnchanged);
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, DISABLED_ModtimeSkipping_EnablePlatform_ShouldProcessFilesForPlatform);
#else
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_EnablePlatform_ShouldProcessFilesForPlatform);
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_ModifyFile);
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_ModifyFile_AndThenRevert_ProcessesAgain);
@ -2362,11 +2350,7 @@ TEST_F(PathDependencyTest, ChangeDependencies_Existing_ResolveCorrectly)
);
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(PathDependencyTest, DISABLED_MixedPathDependencies_Existing_ResolveCorrectly)
#else
TEST_F(PathDependencyTest, MixedPathDependencies_Existing_ResolveCorrectly)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
using namespace AssetProcessor;
using namespace AssetBuilderSDK;
@ -2661,11 +2645,7 @@ TEST_F(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDepende
ASSERT_NE(SearchDependencies(dependencyContainer, asset1.m_products[0]), SearchDependencies(dependencyContainer, asset1.m_products[1]));
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(MultiplatformPathDependencyTest, DISABLED_AssetProcessed_Impl_MultiplatformDependencies_SourcePath)
#else
TEST_F(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDependencies_SourcePath)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
// One product will be pc, one will be console (order is non-deterministic)
TestAsset asset1("testAsset1");
@ -3894,7 +3874,7 @@ void ModtimeScanningTest::ProcessAssetJobs()
for (const auto& processResult : m_data->m_processResults)
{
auto file = QDir(processResult.m_destinationPath).absoluteFilePath(processResult.m_jobEntry.m_databaseSourceName + ".arc1");
auto file = QDir(processResult.m_destinationPath).absoluteFilePath(processResult.m_jobEntry.m_databaseSourceName.toLower() + ".arc1");
m_data->m_productPaths.emplace(
QDir(processResult.m_jobEntry.m_watchFolderPath)
.absoluteFilePath(processResult.m_jobEntry.m_databaseSourceName)
@ -3943,11 +3923,11 @@ void ModtimeScanningTest::ExpectWork(int createJobs, int processJobs)
{
ASSERT_TRUE(BlockUntilIdle(5000));
ASSERT_EQ(m_data->m_mockBuilderInfoHandler.m_createJobsCount, createJobs);
ASSERT_EQ(m_data->m_processResults.size(), processJobs);
ASSERT_FALSE(m_data->m_processResults[0].m_autoFail);
ASSERT_FALSE(m_data->m_processResults[1].m_autoFail);
ASSERT_EQ(m_data->m_deletedSources.size(), 0);
EXPECT_EQ(m_data->m_mockBuilderInfoHandler.m_createJobsCount, createJobs);
EXPECT_EQ(m_data->m_processResults.size(), processJobs);
EXPECT_FALSE(m_data->m_processResults[0].m_autoFail);
EXPECT_FALSE(m_data->m_processResults[1].m_autoFail);
EXPECT_EQ(m_data->m_deletedSources.size(), 0);
m_isIdling = false;
}
@ -3975,11 +3955,7 @@ void ModtimeScanningTest::SetFileContents(QString filePath, QString contents)
file.close();
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(ModtimeScanningTest, DISABLED_ModtimeSkipping_FileUnchanged_WithoutModtimeSkipping)
#else
TEST_F(ModtimeScanningTest, ModtimeSkipping_FileUnchanged_WithoutModtimeSkipping)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
using namespace AzToolsFramework::AssetSystem;
@ -4008,11 +3984,7 @@ TEST_F(ModtimeScanningTest, ModtimeSkipping_FileUnchanged)
ExpectNoWork();
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(ModtimeScanningTest, DISABLED_ModtimeSkipping_EnablePlatform_ShouldProcessFilesForPlatform)
#else
TEST_F(ModtimeScanningTest, ModtimeSkipping_EnablePlatform_ShouldProcessFilesForPlatform)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
using namespace AzToolsFramework::AssetSystem;
@ -4633,11 +4605,7 @@ TEST_F(AssetProcessorManagerTest, UpdateSourceFileDependenciesDatabase_WildcardM
dependList.clear();
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(AssetProcessorManagerTest, DISABLED_RemoveSource_RemoveCacheFolderIfEmpty_Ok)
#else
TEST_F(AssetProcessorManagerTest, RemoveSource_RemoveCacheFolderIfEmpty_Ok)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
using namespace AssetProcessor;
using namespace AssetBuilderSDK;

@ -14,6 +14,7 @@
#include "native/tests/platformconfiguration/platformconfigurationtests.h"
#include <AzTest/AzTest.h>
#include <gmock/gmock.h>
const char TestAppRoot[] = "@exefolder@/testdata";
const char EmptyDummyProjectName[] = "EmptyDummyProject";
@ -238,33 +239,6 @@ TEST_F(PlatformConfigurationUnitTests_OnePCHostFixture, GetScanFolderForFile_Sub
EXPECT_STREQ(info->GetDisplayName().toUtf8().constData(), "Editor ScanFolder");
}
// note that in the case of GetOverridingFile, this SHOULD return the correct case if an override is found
// because its possible to override a file with another file with different case in a different scan folder
// such a situation is supposed to be very rare, so the cost of correcting the case is mitigated.
TEST_F(PlatformConfigurationUnitTests_OnePCHostFixture, GetOverridingFile_Exists_ReturnsCorrectCase)
{
using namespace AzToolsFramework::AssetSystem;
using namespace AssetProcessor;
// create two scan folders, since its order dependent, the ScanFolder1 is the "winner" in tie breakers (when they both contain same file relpath)
QString scanfolder1Path = m_tempPath.filePath("scanfolder1");
QString scanfolder2Path = m_tempPath.filePath("scanfolder2");
QString caseSensitiveDummyFileName = m_tempPath.absoluteFilePath("scanfolder1/TestCase.tXt");
QString differentCaseDummyFileName = m_tempPath.absoluteFilePath("scanfolder2/testcase.txt");
UnitTestUtils::CreateDummyFile(caseSensitiveDummyFileName, QString("testcase1\n"));
UnitTestUtils::CreateDummyFile(differentCaseDummyFileName, QString("testcase2\n"));
m_config->AddScanFolder(ScanFolderInfo(scanfolder1Path, "ScanFolder1", "sf1", false, true, m_platforms), true);
m_config->AddScanFolder(ScanFolderInfo(scanfolder2Path, "ScanFolder2", "sf2", false, true, m_platforms), true);
// Perform the test by asking it whether anyone overrides "testcase" (lowercase) in scanfolder 2.
QString overrider = m_config->GetOverridingFile("testcase.txt", scanfolder2Path);
ASSERT_FALSE(overrider.isEmpty());
// the result should be the real actual case of the file in scanfolder 1:
EXPECT_STREQ(overrider.toUtf8().constData(), caseSensitiveDummyFileName.toUtf8().constData());
}
TEST_F(PlatformConfigurationUnitTests_OnePCHostFixture, GetOverridingFile_ExistsButNotOverridden_ReturnsEmpty)
{
using namespace AzToolsFramework::AssetSystem;
@ -360,7 +334,7 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularScanfolder)
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
ASSERT_EQ(config.GetScanFolderCount(), 3); // the two, and then the one that has the same data as prior but different identifier.
QString scanName = AssetUtilities::ComputeProjectPath() + " Scan Folder";
QString scanName = AssetUtilities::ComputeProjectPath(true) + " Scan Folder";
ASSERT_EQ(config.GetScanFolderAt(0).GetDisplayName(), scanName);
ASSERT_EQ(config.GetScanFolderAt(0).RecurseSubFolders(), true);
ASSERT_EQ(config.GetScanFolderAt(0).GetOrder(), 0);
@ -445,15 +419,11 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularExcludes)
ASSERT_FALSE(config.IsFileExcluded("blahblah/Levels/blahblahhold/whatever.test"));
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(PlatformConfigurationUnitTests, DISABLED_TestFailReadConfigFile_Recognizers)
#else
TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
using namespace AzToolsFramework::AssetSystem;
using namespace AssetProcessor;
#if defined(AZ_PLATFORM_WINDOWS)
#if defined(AZ_PLATFORM_WINDOWS) || defined(AZ_PLATFORM_LINUX)
const char* platformWhichIsNotCurrentPlatform = "mac";
#else
const char* platformWhichIsNotCurrentPlatform = "pc";
@ -502,31 +472,47 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers)
// the "rend" test makes sure that even if you dont specify 'params' its still there by default for all enabled platforms.
// (but platforms can override it)
ASSERT_TRUE(recogs.contains("rend"));
ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform()));
ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains("android"));
ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains("server"));
ASSERT_FALSE(recogs["rend"].m_platformSpecs.contains(platformWhichIsNotCurrentPlatform)); // this is not an enabled platform and should not be there.
ASSERT_EQ(recogs["rend"].m_platformSpecs.size(), 3);
ASSERT_EQ(recogs["rend"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "rendererparams");
ASSERT_EQ(recogs["rend"].m_platformSpecs["android"].m_extraRCParams, "rendererparams");
ASSERT_EQ(recogs["rend"].m_platformSpecs["server"].m_extraRCParams, ""); // default if not specified is empty string
EXPECT_THAT(
recogs["rend"].m_platformSpecs.keys(),
testing::AllOf(
testing::UnorderedElementsAre(
QString(AzToolsFramework::AssetSystem::GetHostAssetPlatform()),
QString("android"),
QString("server")
),
testing::Not(testing::Contains(platformWhichIsNotCurrentPlatform)) // this is not an enabled platform and should not be there.
)
);
EXPECT_EQ(recogs["rend"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "rendererparams");
EXPECT_EQ(recogs["rend"].m_platformSpecs["android"].m_extraRCParams, "rendererparams");
EXPECT_EQ(recogs["rend"].m_platformSpecs["server"].m_extraRCParams, ""); // default if not specified is empty string
ASSERT_TRUE(recogs.contains("alldefault"));
ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform()));
ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains("android"));
ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains("server"));
ASSERT_FALSE(recogs["alldefault"].m_platformSpecs.contains(platformWhichIsNotCurrentPlatform)); // this is not an enabled platform and should not be there.
ASSERT_EQ(recogs["alldefault"].m_platformSpecs.size(), 3);
ASSERT_EQ(recogs["alldefault"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "");
ASSERT_EQ(recogs["alldefault"].m_platformSpecs["android"].m_extraRCParams, "");
ASSERT_EQ(recogs["alldefault"].m_platformSpecs["server"].m_extraRCParams, "");
EXPECT_THAT(
recogs["alldefault"].m_platformSpecs.keys(),
testing::AllOf(
testing::UnorderedElementsAre(
QString(AzToolsFramework::AssetSystem::GetHostAssetPlatform()),
QString("android"),
QString("server")
),
testing::Not(testing::Contains(platformWhichIsNotCurrentPlatform)) // this is not an enabled platform and should not be there.
)
);
EXPECT_EQ(recogs["alldefault"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "");
EXPECT_EQ(recogs["alldefault"].m_platformSpecs["android"].m_extraRCParams, "");
EXPECT_EQ(recogs["alldefault"].m_platformSpecs["server"].m_extraRCParams, "");
ASSERT_TRUE(recogs.contains("skipallbutone"));
ASSERT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform()));
ASSERT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains("android"));
ASSERT_TRUE(recogs["skipallbutone"].m_platformSpecs.contains("server")); // server is only one enabled (set to copy)
ASSERT_EQ(recogs["skipallbutone"].m_platformSpecs.size(), 1);
ASSERT_EQ(recogs["skipallbutone"].m_platformSpecs["server"].m_extraRCParams, "copy");
EXPECT_THAT(
recogs["skipallbutone"].m_platformSpecs.keys(),
testing::UnorderedElementsAre(
QString("server") // server is only one enabled (set to copy)
)
);
EXPECT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform()));
EXPECT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains("android"));
EXPECT_EQ(recogs["skipallbutone"].m_platformSpecs["server"].m_extraRCParams, "copy");
}

@ -137,10 +137,13 @@ TEST_F(AssetUtilitiesTest, UpdateToCorrectCase_ExistingFile_ReturnsTrue_Corrects
thingsToTry << "SomeFile.TxT";
thingsToTry << "otherfile.txt";
thingsToTry << "subfolder1/otherfile.txt";
#if defined(AZ_PLATFORM_WINDOWS)
thingsToTry << "subfolder2\\otherfile.txt";
thingsToTry << "subFolder3\\somefile.txt";
thingsToTry << "subFolder4\\subfolder6\\somefile.txt";
thingsToTry << "subFolder5\\subfolder7/someFile.txt";
#endif // AZ_PLATFORM_WINDOWS
thingsToTry << "specialFileName[.txt";
thingsToTry << "specialFileName].txt";
thingsToTry << "specialFileName!.txt";

@ -175,6 +175,16 @@ namespace AssetProcessor
UNIT_TEST_EXPECT_FALSE(gameName.isEmpty());
// should create cache folder in the root, and read everything from there.
// There is a sub-case of handling mixed cases, but is only supported on case-insensitive filesystems.
#if defined(AZ_PLATFORM_LINUX)
// Linux is case-sensitive, so 'basefile.txt' will stay the same case as the other subfolder versions
constexpr const char* subfolder3BaseFilePath = "subfolder3/basefile.txt";
constexpr int expectedLegacyAssetIdCount = 1;
#else
constexpr const char* subfolder3BaseFilePath = "subfolder3/BaseFile.txt";
constexpr int expectedLegacyAssetIdCount = 2;
#endif
QSet<QString> expectedFiles;
// set up some interesting files:
expectedFiles << tempPath.absoluteFilePath("rootfile2.txt");
@ -185,7 +195,9 @@ namespace AssetProcessor
expectedFiles << tempPath.absoluteFilePath("subfolder2/aaa/bbb/basefile.txt");
expectedFiles << tempPath.absoluteFilePath("subfolder2/aaa/bbb/ccc/basefile.txt");
expectedFiles << tempPath.absoluteFilePath("subfolder2/aaa/bbb/ccc/ddd/basefile.txt");
expectedFiles << tempPath.absoluteFilePath("subfolder3/BaseFile.txt"); // note the case upper here
expectedFiles << tempPath.absoluteFilePath(subfolder3BaseFilePath);
expectedFiles << tempPath.absoluteFilePath("subfolder8/a/b/c/test.txt");
// subfolder3 is not recursive so none of these should show up in any scan or override check
@ -1521,7 +1533,8 @@ namespace AssetProcessor
// -------------- override test -----------------
// set up by letting it compile basefile.txt from 3:
absolutePath = AssetUtilities::NormalizeFilePath(tempPath.absoluteFilePath("subfolder3/BaseFile.txt"));
absolutePath = AssetUtilities::NormalizeFilePath(tempPath.absoluteFilePath(subfolder3BaseFilePath));
QMetaObject::invokeMethod(&apm, "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, absolutePath));
UNIT_TEST_EXPECT_TRUE(BlockUntil(idling, 5000));
@ -1583,8 +1596,7 @@ namespace AssetProcessor
UNIT_TEST_EXPECT_TRUE(assetMessages.size() == 4);
for (auto element : assetMessages)
{
// because the source asset had UPPER CASE in it, we should have multiple legacy IDs
UNIT_TEST_EXPECT_TRUE(element.m_legacyAssetIds.size() == 2);
UNIT_TEST_EXPECT_TRUE(element.m_legacyAssetIds.size() == expectedLegacyAssetIdCount);
}
// ------------- setup complete, now do the test...
@ -1607,7 +1619,7 @@ namespace AssetProcessor
// delete the highest priority override file and ensure that it generates tasks
// for the next highest priority! Basically, deleting this file should "reveal" the file underneath it in the other subfolder
QString deletedFile = tempPath.absoluteFilePath("subfolder3/BaseFile.txt");
QString deletedFile = tempPath.absoluteFilePath(subfolder3BaseFilePath);
QString expectedReplacementInputFile = AssetUtilities::NormalizeFilePath(tempPath.absoluteFilePath("subfolder2/basefile.txt"));
UNIT_TEST_EXPECT_TRUE(QFile::remove(deletedFile));
@ -1621,6 +1633,11 @@ namespace AssetProcessor
sortAssetToProcessResultList(processResults);
#if defined(AZ_PLATFORM_LINUX)
// On Linux, because of we cannot change the case of the source file, the job fingerprint is not updated due the case-switch so
// there will be actually nothing to process
UNIT_TEST_EXPECT_TRUE(processResults.size() == 0);
#else
// --------- same result as above ----------
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier);
@ -1641,7 +1658,7 @@ namespace AssetProcessor
UNIT_TEST_EXPECT_TRUE(processFile1.startsWith(platformFolder));
UNIT_TEST_EXPECT_TRUE(processResults[checkIdx].m_jobEntry.m_computedFingerprint != 0);
}
#endif // defined(AZ_PLATFORM_LINUX)
relativePathFromWatchFolder = "somefile.xxx";
watchFolderPath = tempPath.absoluteFilePath("subfolder3");
absolutePath = watchFolderPath + "/" + relativePathFromWatchFolder;
@ -2721,7 +2738,12 @@ namespace AssetProcessor
{
AssetBuilderSDK::JobDescriptor secondDescriptor = descriptor;
secondDescriptor.m_jobKey = "yyy";
#if defined(AZ_PLATFORM_WINDOWS)
sourceFileDependency.m_sourceFileDependencyPath = "some\\random/Folders/FILEa.TxT";
#else
sourceFileDependency.m_sourceFileDependencyPath = "some/random/folders/FileA.txt";
#endif // defined(AZ_PLATFORM_WINDOWS)
// ... declare a job dependency on job A ('FileA.txt', 'xxx', platform)
AssetBuilderSDK::JobDependency jobDependency("xxx", platformInfo.m_identifier.c_str(), AssetBuilderSDK::JobDependencyType::Fingerprint, sourceFileDependency);
secondDescriptor.m_jobDependencyList.push_back(jobDependency);
@ -2805,11 +2827,11 @@ namespace AssetProcessor
QDir cacheRoot;
UNIT_TEST_EXPECT_TRUE(AssetUtilities::ComputeProjectCacheRoot(cacheRoot));
QString productFileAPath = cacheRoot.filePath(QString("pc/FileAProduct.txt"));
QString productFileBPath = cacheRoot.filePath(QString("pc/FileBProduct1.txt"));
QString product2FileBPath = cacheRoot.filePath(QString("pc/FileBProduct2.txt"));
QString productFileCPath = cacheRoot.filePath(QString("pc/FileCProduct.txt"));
QString product2FileCPath = cacheRoot.filePath(QString("pc/FileCProduct2.txt"));
QString productFileAPath = cacheRoot.filePath(QString("pc/fileaproduct.txt"));
QString productFileBPath = cacheRoot.filePath(QString("pc/filebproduct1.txt"));
QString product2FileBPath = cacheRoot.filePath(QString("pc/filebproduct2.txt"));
QString productFileCPath = cacheRoot.filePath(QString("pc/filecproduct.txt"));
QString product2FileCPath = cacheRoot.filePath(QString("pc/filecproduct2.txt"));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(sourceFileAPath, ""));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(sourceFileBPath, ""));

@ -106,7 +106,7 @@ void FileWatcherUnitTestRunner::StartTest()
AZ_TracePrintf(AssetProcessor::DebugChannel, "Waiting for remaining notifications: %d \n", outstandingFiles.count());
}
if (outstandingFiles.count() > 0)
if (outstandingFiles.count() > 0)
{
#if defined(AZ_ENABLE_TRACING)
AZ_TracePrintf(AssetProcessor::DebugChannel, "Timed out waiting for file changes: %d / %d missed\n", outstandingFiles.count(), maxFiles);
@ -226,7 +226,9 @@ void FileWatcherUnitTestRunner::StartTest()
UNIT_TEST_EXPECT_TRUE(fileAddCalled);
UNIT_TEST_EXPECT_TRUE(fileRemoveCalled);
UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in
#if defined(AZ_PLATFORM_WINDOWS)
UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in (Only on Windows)
#endif // AZ_PLATFORM_WINDOWS
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileRemoveName).toLower() == QDir::toNativeSeparators(originalName).toLower());
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileAddName).toLower() == QDir::toNativeSeparators(newName1).toLower());
@ -249,7 +251,10 @@ void FileWatcherUnitTestRunner::StartTest()
// the new1 was "removed" and the new2 was "added"
UNIT_TEST_EXPECT_TRUE(fileAddCalled);
UNIT_TEST_EXPECT_TRUE(fileRemoveCalled);
UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in
#if defined(AZ_PLATFORM_WINDOWS)
UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in (Only on Windows)
#endif // AZ_PLATFORM_WINDOWS
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileRemoveName).toLower() == QDir::toNativeSeparators(newName1).toLower());
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileAddName).toLower() == QDir::toNativeSeparators(newName2).toLower());
@ -270,11 +275,15 @@ void FileWatcherUnitTestRunner::StartTest()
// the new1 was "removed" and the new2 was "added"
UNIT_TEST_EXPECT_TRUE(fileAddCalled);
UNIT_TEST_EXPECT_TRUE(fileRemoveCalled);
UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in
#if defined(AZ_PLATFORM_WINDOWS)
UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in (Only on Windows)
#endif // AZ_PLATFORM_WINDOWS
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileRemoveName).toLower() == QDir::toNativeSeparators(newName2).toLower());
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileAddName).toLower() == QDir::toNativeSeparators(newName3).toLower());
// final test... make sure that renaming a DIRECTORY works too
#if !defined(AZ_PLATFORM_LINUX)
// final test... make sure that renaming a DIRECTORY works too.
// Note that linux does not get any callbacks if just the directory is renamed (from inotify)
QDir renamer;
fileAddCalled = false;
fileRemoveCalled = false;
@ -297,7 +306,7 @@ void FileWatcherUnitTestRunner::StartTest()
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileRemoveName).toLower() == QDir::toNativeSeparators(tempDirPath.absoluteFilePath("dir3")).toLower());
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileAddName).toLower() == QDir::toNativeSeparators(tempDirPath.absoluteFilePath("dir4")).toLower());
#endif // AZ_PLATFORM_LINUX
QObject::disconnect(connectionRemove);
QObject::disconnect(connectionAdd);

@ -545,10 +545,13 @@ void RCcontrollerUnitTests::RunRCControllerTests()
rcJob.SetCheckExclusiveLock(true);
rcJob.Start();
#if defined(AZ_PLATFORM_WINDOWS)
// on windows, opening a file for reading locks it
// but on other platforms, this is not the case.
// we only expect work to begin when we can gain an exclusive lock on this file.
UNIT_TEST_EXPECT_FALSE(UnitTestUtils::BlockUntil(beginWork, 5000));
#if defined(AZ_PLATFORM_WINDOWS)
// Once we release the file, it should process normally
lockFileTest.close();
#else

@ -227,8 +227,9 @@ void UtilitiesUnitTests::StartTest()
#else
int handle = open(lockTestFileName.toUtf8().constData(), O_RDONLY | O_EXLOCK | O_NONBLOCK);
#endif // AZ_PLATFORM_WINDOWS
UNIT_TEST_EXPECT_FALSE(AssetUtilities::CheckCanLock(lockTestFileName));
#if defined(AZ_PLATFORM_WINDOWS)
UNIT_TEST_EXPECT_FALSE(AssetUtilities::CheckCanLock(lockTestFileName));
lockTestFile.close();
#else
if (handle != -1)

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save