Merge branch 'development' into profiler_capture_api

Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com>
This commit is contained in:
AMZN-ScottR
2021-10-26 09:02:57 -07:00
96 changed files with 2384 additions and 401 deletions
@@ -65,8 +65,35 @@ namespace AZ
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides - Application is a singleton
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
typedef AZStd::recursive_mutex MutexType;
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
using MutexType = AZStd::recursive_mutex;
static constexpr bool EnableEventQueue = true;
using EventQueueMutexType = AZStd::mutex;
struct PostThreadDispatchInvoker
{
~PostThreadDispatchInvoker();
};
template <typename DispatchMutex>
struct ThreadDispatchLockGuard
{
ThreadDispatchLockGuard(DispatchMutex& contextMutex)
: m_lock{ contextMutex }
{}
ThreadDispatchLockGuard(DispatchMutex& contextMutex, AZStd::adopt_lock_t adopt_lock)
: m_lock{ contextMutex, adopt_lock }
{}
ThreadDispatchLockGuard(const ThreadDispatchLockGuard&) = delete;
ThreadDispatchLockGuard& operator=(const ThreadDispatchLockGuard&) = delete;
private:
PostThreadDispatchInvoker m_threadPolicyInvoker;
using LockType = AZStd::conditional_t<LocklessDispatch, AZ::Internal::NullLockGuard<DispatchMutex>, AZStd::scoped_lock<DispatchMutex>>;
LockType m_lock;
};
template <typename DispatchMutex, bool>
using DispatchLockGuard = ThreadDispatchLockGuard<DispatchMutex>;
//////////////////////////////////////////////////////////////////////////
virtual ~AssetCatalogRequests() = default;
@@ -200,6 +227,17 @@ namespace AZ
using AssetCatalogRequestBus = AZ::EBus<AssetCatalogRequests>;
inline AssetCatalogRequests::PostThreadDispatchInvoker::~PostThreadDispatchInvoker()
{
if (!AssetCatalogRequestBus::IsInDispatchThisThread())
{
if (AssetCatalogRequestBus::QueuedEventCount())
{
AssetCatalogRequestBus::ExecuteQueuedEvents();
}
}
}
/*
* Events that AssetManager listens for
*/
@@ -14,6 +14,7 @@
#include <AzCore/Casting/lossy_cast.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/ComponentApplicationLifecycle.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Debug/LocalFileEventLogger.h>
@@ -44,8 +45,6 @@
#include <AzCore/Module/Module.h>
#include <AzCore/Module/ModuleManager.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/Path/Path_fwd.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Driller/Driller.h>
@@ -506,6 +505,16 @@ namespace AZ
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry);
// The /O3DE/Application/LifecycleEvents array contains a valid set of lifecycle events
// Those lifecycle events are normally read from the <engine-root>/Registry
// which isn't merged until ComponentApplication::Create invokes MergeSettingsToRegistry
// So pre-populate the valid lifecycle even entries
ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "SystemAllocatorCreated");
ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "SettingsRegistryAvailable");
ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "ConsoleAvailable");
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SystemAllocatorCreated", R"({})");
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SettingsRegistryAvailable", R"({})");
// Create the Module Manager
m_moduleManager = AZStd::make_unique<ModuleManager>();
@@ -520,6 +529,7 @@ namespace AZ
m_ownsConsole = true;
m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
m_settingsRegistryConsoleFunctors = AZ::SettingsRegistryConsoleUtils::RegisterAzConsoleCommands(*m_settingsRegistry, *m_console);
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ConsoleAvailable", R"({})");
}
}
@@ -551,6 +561,7 @@ namespace AZ
{
AZ::Interface<AZ::IConsole>::Unregister(m_console);
delete m_console;
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ConsoleUnavailable", R"({})");
}
m_moduleManager.reset();
@@ -558,6 +569,8 @@ namespace AZ
if (AZ::SettingsRegistry::Get() == m_settingsRegistry.get())
{
SettingsRegistry::Unregister(m_settingsRegistry.get());
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SettingsRegistryUnavailable", R"({})");
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SystemAllocatorPendingDestruction", R"({})");
}
m_settingsRegistry.reset();
@@ -672,6 +685,8 @@ namespace AZ
ReflectionEnvironment::GetReflectionManager()->Reflect(azrtti_typeid(this), [this](ReflectContext* context) {Reflect(context); });
RegisterCoreComponents();
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ReflectionManagerAvailable", R"({})");
TickBus::AllowFunctionQueuing(true);
SystemTickBus::AllowFunctionQueuing(true);
@@ -691,6 +706,7 @@ namespace AZ
// Load the actual modules
LoadModules();
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "GemsLoaded", R"({})");
// Execute user.cfg after modules have been loaded but before processing any command-line overrides
AZ::IO::FixedMaxPath platformCachePath;
@@ -756,12 +772,14 @@ namespace AZ
m_entities.rehash(0); // force free all memory
DestroyReflectionManager();
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ReflectionManagerUnavailable", R"({})");
static_cast<SettingsRegistryImpl*>(m_settingsRegistry.get())->ClearNotifiers();
static_cast<SettingsRegistryImpl*>(m_settingsRegistry.get())->ClearMergeEvents();
// Uninit and unload any dynamic modules.
m_moduleManager->UnloadModules();
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "GemsUnloaded", R"({})");
NameDictionary::Destroy();
@@ -175,6 +175,8 @@ namespace AZ
bool m_loadDynamicModules = true;
//! Used by test fixtures to ensure reflection occurs to edit context.
bool m_createEditContext = false;
//! Indicates whether the AssetCatalog.xml should be loaded by default in Application::StartCommon
bool m_loadAssetCatalog = true;
};
ComponentApplication();
@@ -356,7 +358,7 @@ namespace AZ
/// Calculates the root directory of the engine.
void CalculateEngineRoot();
/// Calculates the directory where the bootstrap.cfg file resides.
/// Deprecated: The term "AppRoot" has no meaning
void CalculateAppRoot();
template<typename Iterator>
@@ -0,0 +1,87 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Component/ComponentApplicationLifecycle.h>
#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
namespace AZ::ComponentApplicationLifecycle
{
bool ValidateEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName)
{
using FixedValueString = SettingsRegistryInterface::FixedValueString;
using Type = SettingsRegistryInterface::Type;
FixedValueString eventRegistrationKey{ ApplicationLifecycleEventRegistrationKey };
eventRegistrationKey += '/';
eventRegistrationKey += eventName;
return settingsRegistry.GetType(eventRegistrationKey) == Type::Object;
}
bool SignalEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName, AZStd::string_view eventValue)
{
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
using Format = AZ::SettingsRegistryInterface::Format;
if (!ValidateEvent(settingsRegistry, eventName))
{
AZ_Warning("ComponentApplicationLifecycle", false, R"(Cannot signal event %.*s. Name does is not a field of object "%.*s".)"
R"( Please make sure the entry exists in the '<engine-root>/Registry/application_lifecycle_events.setreg")"
" or in *.setreg within the project", AZ_STRING_ARG(eventName), AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey));
return false;
}
auto eventRegistrationKey = FixedValueString::format("%.*s/%.*s", AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey),
AZ_STRING_ARG(eventName));
return settingsRegistry.MergeSettings(eventValue, Format::JsonMergePatch, eventRegistrationKey);
}
bool RegisterEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName)
{
using FixedValueString = SettingsRegistryInterface::FixedValueString;
using Format = AZ::SettingsRegistryInterface::Format;
if (!ValidateEvent(settingsRegistry, eventName))
{
FixedValueString eventRegistrationKey{ ApplicationLifecycleEventRegistrationKey };
eventRegistrationKey += '/';
eventRegistrationKey += eventName;
return settingsRegistry.MergeSettings(R"({})", Format::JsonMergePatch, eventRegistrationKey);
}
return true;
}
bool RegisterHandler(AZ::SettingsRegistryInterface& settingsRegistry, AZ::SettingsRegistryInterface::NotifyEventHandler& handler,
AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName)
{
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
using Type = AZ::SettingsRegistryInterface::Type;
using NotifyEventHandler = AZ::SettingsRegistryInterface::NotifyEventHandler;
if (!ValidateEvent(settingsRegistry, eventName))
{
AZ_Warning("ComponentApplicationLifecycle", false, R"(Cannot register event %.*s. Name does is not a field of object "%.*s".)"
R"( Please make sure the entry exists in the '<engine-root>/Registry/application_lifecycle_events.setreg")"
" or in *.setreg within the project", AZ_STRING_ARG(eventName), AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey));
return false;
}
auto eventNameRegistrationKey = FixedValueString::format("%.*s/%.*s", AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey),
AZ_STRING_ARG(eventName));
auto lifecycleCallback = [callback = AZStd::move(callback), eventNameRegistrationKey](AZStd::string_view path, Type type)
{
if (path == eventNameRegistrationKey)
{
callback(path, type);
}
};
handler = NotifyEventHandler(AZStd::move(lifecycleCallback));
settingsRegistry.RegisterNotifier(handler);
return true;
}
}
@@ -0,0 +1,54 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/std/string/string_view.h>
namespace AZ::ComponentApplicationLifecycle
{
//! Root Key where lifecycle events should be registered under
inline constexpr AZStd::string_view ApplicationLifecycleEventRegistrationKey = "/O3DE/Runtime/Application/LifecycleEvents";
//! Validates that the event @eventName is stored in the array at ApplicationLifecycleEventRegistrationKey
//! @param settingsRegistry registry where @eventName will be searched
//! @param eventName name of key that validated that exists as an element in the ApplicationLifecycleEventRegistrationKey array
//! @return true if the @eventName was found in the ApplicationLifecycleEventRegistrationKey array
bool ValidateEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName);
//! Wrapper around setting a value underneath the ApplicationLifecycleEventRegistrationKey
//! It validates if the @eventName is is part of the ApplicationLifecycleEventRegistrationKey array
//! It then appends the @eventName to the ApplicationLifecycleEventRegistrationKey merges the @eventValue into
//! the SettingsRegistry at that key
//! NOTE: This function should only be invoked from ComponentApplication and its derived classes
//! @param settingsRegistry registry where eventName should be set
//! @param eventName name of key underneath the ApplicationLifecycleEventRegistrationKey to signal
//! @param eventValue JSON Object that will be merged into the SettingsRegistry at <ApplicationLifecycleEventRootKey>/<eventName>
//! @return true if the eventValue was successfully merged at the <ApplicationLifecycleEventRootKey>/<eventName>
bool SignalEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName, AZStd::string_view eventValue);
//! Register that the event @eventName is stored in the array at ApplicationLifecycleEventRegistrationKey
//! @param settingsRegistry registry where @eventName will be searched
//! @param eventName name of key that will be stored in the ApplicationLifecycleEventRegistrationKey array
//! @return true if the event passed validation or the eventName was stored in the ApplicationLifecycleEventRegistrationKey array
bool RegisterEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName);
//! Wrapper around registering the NotifyEventHandler with the SettingsRegistry for the specified event
//! It validates if the @eventName is is part of the ApplicationLifecycleEventRegistrationKey array and if
//! so moves the @callback into @handler and then registers the handler with the SettingsRegistry NotifyEvent
//! @param settingsRegistry registry where handler will be registered
//! @param handler handler where callback will be moved into and then registered with the SettingsRegistry
//! if the specified @eventName passes validation
//! @param callback will be moved into the handler if the specified @eventName is valid
//! @param eventName name of key underneath the ApplicationLifecycleEventRegistrationKey to register
//! @return true if the handler was registered with the SettingsRegistry NotifyEvent
bool RegisterHandler(AZ::SettingsRegistryInterface& settingsRegistry, AZ::SettingsRegistryInterface::NotifyEventHandler& handler,
AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName);
}
@@ -262,6 +262,6 @@ static constexpr AZ::ThreadSafety ConsoleThreadSafety<_TYPE, std::enable_if_t<st
//! @param _FLAGS a set of AzFramework::ConsoleFunctorFlags used to mutate behaviour
//! @param _DESC a description of the cvar
#define AZ_CONSOLEFREEFUNC_4(_NAME, _FUNCTION, _FLAGS, _DESC) \
inline AZ::ConsoleFunctor<void, false> Functor##_FUNCTION(#_FUNCTION, _DESC, _FLAGS | AZ::ConsoleFunctorFlags::DontDuplicate, AZ::TypeId::CreateNull(), &_FUNCTION)
inline AZ::ConsoleFunctor<void, false> Functor##_FUNCTION(_NAME, _DESC, _FLAGS | AZ::ConsoleFunctorFlags::DontDuplicate, AZ::TypeId::CreateNull(), &_FUNCTION)
#define AZ_CONSOLEFREEFUNC(...) AZ_MACRO_SPECIALIZE(AZ_CONSOLEFREEFUNC_, AZ_VA_NUM_ARGS(__VA_ARGS__), (__VA_ARGS__))
@@ -10,16 +10,13 @@
#include <AzCore/Module/Internal/ModuleManagerSearchPathTool.h>
#include <AzCore/Module/Module.h>
#include <AzCore/RTTI/AttributeReader.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/ComponentApplicationLifecycle.h>
#include <AzCore/NativeUI/NativeUIRequests.h>
#include <AzCore/Script/ScriptSystemBus.h>
#include <AzCore/Script/ScriptContext.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/smart_ptr/make_shared.h>
@@ -221,11 +218,16 @@ namespace AZ
}
}
AZStd::string componentNamesArray = R"({ "SystemComponents":[)";
const char* comma = "";
// For all system components, deactivate
for (auto componentIt = m_systemComponents.rbegin(); componentIt != m_systemComponents.rend(); ++componentIt)
{
ModuleEntity::DeactivateComponent(**componentIt);
componentNamesArray += AZStd::string::format(R"(%s"%s")", comma, (*componentIt)->RTTI_GetTypeName());
comma = ", ";
}
componentNamesArray += R"(]})";
// For all modules that we created an entity for, set them to "Init" (meaning not Activated)
for (auto& moduleData : m_ownedModules)
@@ -239,6 +241,13 @@ namespace AZ
// Since the system components have been deactivated clear out the vector.
m_systemComponents.clear();
// Signal that the System Components have deactivated
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "SystemComponentsDeactivated", componentNamesArray);
}
}
//=========================================================================
@@ -284,7 +293,11 @@ namespace AZ
{
// Split the tag list
AZStd::vector<AZStd::string_view> tagList;
AZStd::tokenize<AZStd::string_view>(tags, ",", tagList);
auto TokenizeTags = [&tagList](AZStd::string_view token)
{
tagList.push_back(token);
};
AZ::StringFunc::TokenizeVisitor(tags, TokenizeTags, ',');
m_systemComponentTags.resize(tagList.size());
AZStd::transform(tagList.begin(), tagList.end(), m_systemComponentTags.begin(), [](const AZStd::string_view& tag)
@@ -737,11 +750,17 @@ namespace AZ
}
}
AZStd::string componentNamesArray = R"({ "SystemComponents":[)";
const char* comma = "";
// Activate the entities in the appropriate order
for (Component* component : componentsToActivate)
{
ModuleEntity::ActivateComponent(*component);
componentNamesArray += AZStd::string::format(R"(%s"%s")", comma, component->RTTI_GetTypeName());
comma = ", ";
}
componentNamesArray += R"(]})";
// Done activating; set state to active
for (auto& moduleData : modulesToInit)
@@ -755,5 +774,12 @@ namespace AZ
// Save the activated components for deactivation later
m_systemComponents.insert(m_systemComponents.end(), componentsToActivate.begin(), componentsToActivate.end());
// Signal that the System Components are activated
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "SystemComponentsActivated",
componentNamesArray);
}
}
} // namespace AZ
@@ -15,20 +15,20 @@
namespace AZ::SettingsRegistryScriptUtils::Internal
{
static void RegisterScriptProxyForNotify(SettingsRegistryScriptProxy& settingsRegistryProxy)
static void RegisterScriptProxyForNotify(SettingsRegistryInterface* settingsRegistry,
SettingsRegistryScriptProxy::NotifyEventProxy* notifyEventProxy)
{
if (settingsRegistryProxy.IsValid())
if (settingsRegistry != nullptr)
{
auto ForwardSettingsUpdateToProxyEvent = [&settingsRegistryProxy](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
auto ForwardSettingsUpdateToProxyEvent = [notifyEventProxy](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
if (settingsRegistryProxy.m_notifyEventProxy)
if (notifyEventProxy)
{
settingsRegistryProxy.m_notifyEventProxy->m_scriptNotifyEvent.Signal(path);
notifyEventProxy->m_scriptNotifyEvent.Signal(path);
}
};
// Register the forwarding function with the BehaviorContext
settingsRegistryProxy.m_notifyEventProxy->m_settingsUpdatedHandler =
settingsRegistryProxy.m_settingsRegistry->RegisterNotifier(ForwardSettingsUpdateToProxyEvent);
notifyEventProxy->m_settingsUpdatedHandler = settingsRegistry->RegisterNotifier(ForwardSettingsUpdateToProxyEvent);
}
}
@@ -37,7 +37,7 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
: m_settingsRegistry(AZStd::move(settingsRegistry))
, m_notifyEventProxy(AZStd::make_shared<NotifyEventProxy>())
{
RegisterScriptProxyForNotify(*this);
RegisterScriptProxyForNotify(m_settingsRegistry.get(), m_notifyEventProxy.get());
}
// Raw AZ::SettingsRegistryInterface pointer is not owned by the proxy, so it's deleter is a no-op
@@ -45,7 +45,7 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
: m_settingsRegistry(settingsRegistry, [](AZ::SettingsRegistryInterface*) {})
, m_notifyEventProxy(AZStd::make_shared<NotifyEventProxy>())
{
RegisterScriptProxyForNotify(*this);
RegisterScriptProxyForNotify(m_settingsRegistry.get(), m_notifyEventProxy.get());
}
// SettingsRegistryScriptProxy function that determines if the SettingsRegistry object is valid
@@ -41,6 +41,8 @@ set(FILES
Component/ComponentApplication.cpp
Component/ComponentApplication.h
Component/ComponentApplicationBus.h
Component/ComponentApplicationLifecycle.cpp
Component/ComponentApplicationLifecycle.h
Component/ComponentBus.cpp
Component/ComponentBus.h
Component/ComponentExport.h
@@ -10,6 +10,7 @@
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/ComponentApplicationLifecycle.h>
#include <AzCore/Component/NonUniformScaleBus.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Memory/MemoryComponent.h>
@@ -120,6 +121,11 @@ namespace AzFramework
m_archiveFileIO = AZStd::make_unique<AZ::IO::ArchiveFileIO>(m_archive.get());
AZ::IO::FileIOBase::SetInstance(m_archiveFileIO.get());
SetFileIOAliases();
// The FileIOAvailable event needs to be registered here as this event is sent out
// before the settings registry has merged the .setreg files from the <engine-root>
// (That happens in MergeSettingsToRegistry
AZ::ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "FileIOAvailable");
AZ::ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "FileIOAvailable", R"({})");
}
if (auto nativeUI = AZ::Interface<AZ::NativeUI::NativeUIRequests>::Get(); nativeUI == nullptr)
@@ -172,6 +178,8 @@ namespace AzFramework
// Archive classes relies on the FileIOBase DirectInstance to close
// files properly
m_directFileIO.reset();
AZ::ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "FileIOUnavailable", R"({})");
}
void Application::Start(const Descriptor& descriptor, const StartupParameters& startupParameters)
@@ -196,7 +204,25 @@ namespace AzFramework
systemEntity->Activate();
AZ_Assert(systemEntity->GetState() == AZ::Entity::State::Active, "System Entity failed to activate.");
m_isStarted = (systemEntity->GetState() == AZ::Entity::State::Active);
if (m_isStarted = (systemEntity->GetState() == AZ::Entity::State::Active); m_isStarted)
{
if (m_startupParameters.m_loadAssetCatalog)
{
// Start Monitoring Asset changes over the network and load the AssetCatalog
auto StartMonitoringAssetsAndLoadCatalog = [this](AZ::Data::AssetCatalogRequests* assetCatalogRequests)
{
if (AZ::IO::FixedMaxPath assetCatalogPath;
m_settingsRegistry->Get(assetCatalogPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
{
assetCatalogPath /= "assetcatalog.xml";
assetCatalogRequests->LoadCatalog(assetCatalogPath.c_str());
}
};
using AssetCatalogBus = AZ::Data::AssetCatalogRequestBus;
AssetCatalogBus::Broadcast(AZStd::move(StartMonitoringAssetsAndLoadCatalog));
}
}
}
void Application::PreModuleLoad()
@@ -210,6 +236,17 @@ namespace AzFramework
{
if (m_isStarted)
{
if (m_startupParameters.m_loadAssetCatalog)
{
// Stop Monitoring Assets changes
auto StopMonitoringAssets = [](AZ::Data::AssetCatalogRequests* assetCatalogRequests)
{
assetCatalogRequests->StopMonitoringAssets();
};
using AssetCatalogBus = AZ::Data::AssetCatalogRequestBus;
AssetCatalogBus::Broadcast(AZStd::move(StopMonitoringAssets));
}
ApplicationLifecycleEvents::Bus::Broadcast(&ApplicationLifecycleEvents::OnApplicationAboutToStop);
m_pimpl.reset();
@@ -565,7 +565,7 @@ namespace AzFramework
if (!bytes.empty())
{
AZStd::shared_ptr < AzFramework::AssetRegistry> prevRegistry;
AZStd::shared_ptr<AzFramework::AssetRegistry> prevRegistry;
if (!m_initialized)
{
// First time initialization may have updates already processed which we want to apply
@@ -589,7 +589,6 @@ namespace AzFramework
AZ_TracePrintf("AssetCatalog", "Loaded registry containing %u assets.\n", m_registry->m_assetIdToInfo.size());
// It's currently possible in tools for us to have received updates from AP which were applied before the catalog was ready to load
// due to CryPak and CrySystem coming online later than our components
if (!m_initialized)
{
ApplyDeltaCatalog(prevRegistry);
@@ -611,12 +610,13 @@ namespace AzFramework
// the mutex. If the listener tries to perform a blocking asset load via GetAsset() / BlockUntilLoadComplete(), the spawned asset
// thread will make a call to the AssetCatalogRequestBus and block on the held mutex. This would cause a deadlock, since the listener
// won't free the mutex until the load is complete.
// So instead, queue the notification until the next tick, so that it doesn't occur within the AssetCatalogRequestBus mutex, and also
// So instead, queue the notification until after the AssetCatalogRequestBus mutex is unlocked for the current thread, and also
// so that the entire AssetCatalog initialization is complete.
AZ::TickBus::QueueFunction([catalogRegistryString = AZStd::string(catalogRegistryFile)]()
{
AssetCatalogEventBus::Broadcast(&AssetCatalogEventBus::Events::OnCatalogLoaded, catalogRegistryString.c_str());
});
auto OnCatalogLoaded = [catalogRegistryString = AZStd::string(catalogRegistryFile)]()
{
AssetCatalogEventBus::Broadcast(&AssetCatalogEventBus::Events::OnCatalogLoaded, catalogRegistryString.c_str());
};
AZ::Data::AssetCatalogRequestBus::QueueFunction(AZStd::move(OnCatalogLoaded));
}
}
@@ -978,6 +978,7 @@ namespace AzFramework
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_registryMutex);
m_registry->Clear();
m_initialized = false;
}
@@ -61,6 +61,7 @@ namespace AzFramework
//=========================================================================
void AssetRegistry::Clear()
{
m_assetDependencies = {};
m_assetIdToInfo = AssetIdToInfoMap();
m_assetPathToId = AssetPathToIdMap();
}
@@ -10,11 +10,11 @@
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/string/wildcard.h>
#include <AzCore/std/string/regex.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/Utils/Utils.h>
#include <AzCore/XML/rapidxml.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Asset/FileTagAsset.h>
@@ -89,19 +89,19 @@ namespace AzFramework
bool FileTagManager::Save(FileTagType fileTagType, const AZStd::string& destinationFilePath = AZStd::string())
{
AzFramework::FileTag::FileTagAsset* fileTagAsset = GetFileTagAsset(fileTagType);
AZStd::string filePathToSave = destinationFilePath;
AZ::IO::Path filePathToSave = destinationFilePath;
if (filePathToSave.empty())
{
filePathToSave = FileTagQueryManager::GetDefaultFileTagFilePath(fileTagType);
}
if (!AzFramework::StringFunc::EndsWith(filePathToSave, AzFramework::FileTag::FileTagAsset::Extension()))
if (!filePathToSave.Extension().Native().ends_with(AzFramework::FileTag::FileTagAsset::Extension()))
{
AZ_Error("FileTag", false, "Unable to save tag file (%s). Invalid file extension, file tag can only have (%s) extension.\n", filePathToSave.c_str(), AzFramework::FileTag::FileTagAsset::Extension());
return false;
}
return AZ::Utils::SaveObjectToFile(filePathToSave, AZ::DataStream::StreamType::ST_XML, fileTagAsset);
return AZ::Utils::SaveObjectToFile(filePathToSave.Native(), AZ::DataStream::StreamType::ST_XML, fileTagAsset);
}
AZ::Outcome<AZStd::string, AZStd::string> FileTagManager::AddTagsInternal(AZStd::string filePath, FileTagType fileTagType, AZStd::vector<AZStd::string> fileTags, AzFramework::FileTag::FilePatternType filePatternType)
@@ -239,17 +239,22 @@ namespace AzFramework
QueryFileTagsEventBus::Handler::BusDisconnect();
}
AZStd::string FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType fileTagType)
AZ::IO::Path FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType fileTagType)
{
auto destinationFilePath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / EngineAssetSourceRelPath;
AZ::IO::Path destinationFilePath;
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
settingsRegistry->Get(destinationFilePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
}
destinationFilePath /= EngineAssetSourceRelPath;
destinationFilePath /= fileTagType == FileTagType::Exclude ? ExcludeFileName : IncludeFileName;
destinationFilePath.ReplaceExtension(AzFramework::FileTag::FileTagAsset::Extension());
return destinationFilePath.String();
return destinationFilePath;
}
bool FileTagQueryManager::Load(const AZStd::string& filePath)
{
AZStd::string fileToLoad = filePath;
AZ::IO::Path fileToLoad = filePath;
if (fileToLoad.empty())
{
fileToLoad = GetDefaultFileTagFilePath(m_fileTagType);
@@ -11,6 +11,7 @@
#include <AzCore/std/containers/map.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/std/string/string.h>
#include <AzCore/IO/Path/Path_fwd.h>
#include <AzFramework/FileTag/FileTagBus.h>
namespace AzFramework
@@ -88,7 +89,7 @@ namespace AzFramework
/////////////////////////////////////////////////////////////////////////
static AZStd::string GetDefaultFileTagFilePath(FileTagType fileTagType);
static AZ::IO::Path GetDefaultFileTagFilePath(FileTagType fileTagType);
protected:
@@ -16,6 +16,7 @@
#include <AzCore/std/string/wildcard.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/XML/rapidxml.h>
namespace AzFramework
@@ -66,7 +67,8 @@ namespace AzFramework
m_excludeFileQueryManager.reset(aznew FileTagQueryManager(FileTagType::Exclude));
if (!m_excludeFileQueryManager.get()->Load())
{
AZ_Error("FileTagQueryComponent", false, "Not able to load default exclude file (%s). Please make sure that it exists on disk.\n", FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType::Exclude).c_str());
AZ_Error("FileTagQueryComponent", false, "Not able to load default exclude file (%s). Please make sure that it exists on disk.\n",
FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType::Exclude).c_str());
}
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
@@ -308,7 +308,9 @@ namespace UnitTest
registry->Set(projectPathKey, "AutomatedTesting");
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
m_app->Start(desc);
AZ::ComponentApplication::StartupParameters startupParameters;
startupParameters.m_loadAssetCatalog = false;
m_app->Start(desc, startupParameters);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
@@ -943,13 +943,15 @@ namespace AzToolsFramework
{
return false;
}
const int count = rowCount(parent);
AZ::EntityId newParentId = GetEntityFromIndex(parent);
AZ::EntityId beforeEntityId = GetEntityFromIndex(index(row, 0, parent));
AZ::EntityId beforeEntityId = (row >= 0 && row < count) ? GetEntityFromIndex(index(row, 0, parent)) : AZ::EntityId();
EntityIdList topLevelEntityIds;
topLevelEntityIds.reserve(entityIdListContainer.m_entityIds.size());
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::FindTopLevelEntityIdsInactive, entityIdListContainer.m_entityIds, topLevelEntityIds);
if (!ReparentEntities(newParentId, topLevelEntityIds, beforeEntityId))
const auto appendActionForInvalid = newParentId.IsValid() && (row >= count) ? AppendEnd : AppendBeginning;
if (!ReparentEntities(newParentId, topLevelEntityIds, beforeEntityId, appendActionForInvalid))
{
return false;
}
@@ -1046,7 +1048,7 @@ namespace AzToolsFramework
return true;
}
bool EntityOutlinerListModel::ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList &selectedEntityIds, const AZ::EntityId& beforeEntityId)
bool EntityOutlinerListModel::ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList &selectedEntityIds, const AZ::EntityId& beforeEntityId, ReparentForInvalid forInvalid)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
if (!CanReparentEntities(newParentId, selectedEntityIds))
@@ -1056,10 +1058,18 @@ namespace AzToolsFramework
m_isFilterDirty = true;
ScopedUndoBatch undo("Reparent Entities");
//capture child entity order before re-parent operation, which will automatically add order info if not present
EntityOrderArray entityOrderArray = GetEntityChildOrder(newParentId);
//search for the insertion entity in the order array
const auto beforeEntityItr = AZStd::find(entityOrderArray.begin(), entityOrderArray.end(), beforeEntityId);
const bool hasInvalidIndex = beforeEntityItr == entityOrderArray.end();
if (hasInvalidIndex && forInvalid == None)
{
return false;
}
ScopedUndoBatch undo("Reparent Entities");
// The new parent is dirty due to sort change(s)
undo.MarkEntityDirty(GetEntityIdForSortInfo(newParentId));
@@ -1088,9 +1098,7 @@ namespace AzToolsFramework
}
}
//search for the insertion entity in the order array
auto beforeEntityItr = AZStd::find(entityOrderArray.begin(), entityOrderArray.end(), beforeEntityId);
//replace order info matching selection with bad values rather than remove to preserve layout
for (auto& id : entityOrderArray)
{
@@ -1100,17 +1108,25 @@ namespace AzToolsFramework
}
}
if (newParentId.IsValid())
//if adding to a valid parent entity, insert at the found entity location or at the head/tail depending on placeAtTail flag
if (hasInvalidIndex)
{
//if adding to a valid parent entity, insert at the found entity location or at the head of the container
auto insertItr = beforeEntityItr != entityOrderArray.end() ? beforeEntityItr : entityOrderArray.begin();
entityOrderArray.insert(insertItr, processedEntityIds.begin(), processedEntityIds.end());
}
else
switch(forInvalid)
{
case AppendEnd:
entityOrderArray.insert(entityOrderArray.end(), processedEntityIds.begin(), processedEntityIds.end());
break;
case AppendBeginning:
entityOrderArray.insert(entityOrderArray.begin(), processedEntityIds.begin(), processedEntityIds.end());
break;
default:
AZ_Assert(false, "Unexpected type for ReparentForInvalid");
break;
}
}
else
{
//if adding to an invalid parent entity (the root), insert at the found entity location or at the tail of the container
auto insertItr = beforeEntityItr != entityOrderArray.end() ? beforeEntityItr : entityOrderArray.end();
entityOrderArray.insert(insertItr, processedEntityIds.begin(), processedEntityIds.end());
entityOrderArray.insert(beforeEntityItr, processedEntityIds.begin(), processedEntityIds.end());
}
//remove placeholder entity ids
@@ -72,6 +72,13 @@ namespace AzToolsFramework
ColumnCount //!< Total number of columns
};
enum ReparentForInvalid
{
None, //!< For an invalid location the entity does not change location
AppendEnd, //!< Append Item to end of target parent list
AppendBeginning, //!< Append Item to the beginning of target parent list
};
// Note: the ColumnSortIndex column isn't shown, hence the -1 and the need for a separate counter.
// A wrong column count number causes refresh issues and hover mismatch on model update.
static const int VisibleColumnCount = ColumnCount - 1;
@@ -162,7 +169,7 @@ namespace AzToolsFramework
// Buffer Processing Slots - These are called using single-shot events when the buffers begin to fill.
bool CanReparentEntities(const AZ::EntityId& newParentId, const EntityIdList& selectedEntityIds) const;
bool ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList& selectedEntityIds, const AZ::EntityId& beforeEntityId = AZ::EntityId());
bool ReparentEntities(const AZ::EntityId& newParentId, const EntityIdList& selectedEntityIds, const AZ::EntityId& beforeEntityId = AZ::EntityId(), ReparentForInvalid forInvalid = None);
//! Use the current filter setting and re-evaluate the filter.
void InvalidateFilter();
+2 -2
View File
@@ -242,7 +242,7 @@ void IDebugCallStack::WriteLineToLog(const char* format, ...)
va_end(ArgList);
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\error.log", AZ::IO::GetOpenModeFromStringMode("a+t"), fileHandle);
AZ::IO::FileIOBase::GetDirectInstance()->Open("@log@\\error.log", AZ::IO::GetOpenModeFromStringMode("a+t"), fileHandle);
if (fileHandle != AZ::IO::InvalidHandle)
{
AZ::IO::FileIOBase::GetDirectInstance()->Write(fileHandle, szBuffer, strlen(szBuffer));
@@ -254,7 +254,7 @@ void IDebugCallStack::WriteLineToLog(const char* format, ...)
//////////////////////////////////////////////////////////////////////////
void IDebugCallStack::StartMemLog()
{
AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\memallocfile.log", AZ::IO::OpenMode::ModeWrite, m_memAllocFileHandle);
AZ::IO::FileIOBase::GetDirectInstance()->Open("@log@\\memallocfile.log", AZ::IO::OpenMode::ModeWrite, m_memAllocFileHandle);
assert(m_memAllocFileHandle != AZ::IO::InvalidHandle);
}
@@ -61,8 +61,12 @@ namespace AssetBundler
{
AZStd::string absolutePath = filePath.toUtf8().data();
if (AZ::IO::FileIOBase::GetInstance()->Exists(absolutePath.c_str()))
{
AZStd::string projectName = pathToProjectNameMap.at(absolutePath);
{
AZStd::string projectName;
if (pathToProjectNameMap.contains(absolutePath))
{
projectName = pathToProjectNameMap.at(absolutePath);
}
// If a project name is already specified, then the associated file is a default file
LoadFile(absolutePath, projectName, !projectName.empty());
@@ -3576,23 +3576,24 @@ namespace AssetProcessor
// Absolute path, just check the 1 scan folder
if (AZ::IO::PathView(encodedFileData.toUtf8().constData()).IsAbsolute())
{
QString scanFolderName;
if (!m_platformConfig->ConvertToRelativePath(encodedFileData, resultDatabaseSourceName, scanFolderName))
auto scanFolderInfo = m_platformConfig->GetScanFolderForFile(encodedFileData);
if (!m_platformConfig->ConvertToRelativePath(encodedFileData, scanFolderInfo, resultDatabaseSourceName))
{
AZ_Warning(
AssetProcessor::ConsoleChannel, false,
"'%s' does not appear to be in any input folder. Use relative paths instead.",
sourceDependency.m_sourceFileDependencyPath.c_str());
}
else
{
// Make an absolute path that is ScanFolderPath + Part of search path before the wildcard
QDir rooted(scanFolderInfo->ScanPath());
QString scanFolderAndKnownSubPath = rooted.absoluteFilePath(knownPathBeforeWildcard);
auto scanFolderInfo = m_platformConfig->GetScanFolderByPath(scanFolderName);
// Make an absolute path that is ScanFolderPath + Part of search path before the wildcard
QDir rooted(scanFolderName);
QString scanFolderAndKnownSubPath = rooted.absoluteFilePath(knownPathBeforeWildcard);
resolvedDependencyList.append(m_platformConfig->FindWildcardMatches(
scanFolderAndKnownSubPath, relativeSearch, false, scanFolderInfo->RecurseSubFolders()));
resolvedDependencyList.append(m_platformConfig->FindWildcardMatches(
scanFolderAndKnownSubPath, relativeSearch, false, scanFolderInfo->RecurseSubFolders()));
}
}
else // Relative path, check every scan folder
{
@@ -88,6 +88,7 @@ public:
friend struct DuplicateProductsTest;
friend struct DuplicateProcessTest;
friend struct AbsolutePathProductDependencyTest;
friend struct WildcardSourceDependencyTest;
explicit AssetProcessorManager_Test(PlatformConfiguration* config, QObject* parent = nullptr);
~AssetProcessorManager_Test() override;
@@ -5308,3 +5309,141 @@ TEST_F(MetadataFileTest, MetadataFile_SourceFileExtensionDifferentCase)
ASSERT_TRUE(BlockUntilIdle(5000));
ASSERT_EQ(jobDetails.m_jobEntry.m_pathRelativeToWatchFolder, relFileName);
}
bool WildcardSourceDependencyTest::Test(
const AZStd::string& dependencyPath, AZStd::vector<AZStd::string>& resolvedPaths)
{
[[maybe_unused]] QString resolvedName;
QStringList stringlistPaths;
AssetBuilderSDK::SourceFileDependency dependency(dependencyPath, AZ::Uuid::CreateNull(), AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Wildcards);
bool result = m_assetProcessorManager->ResolveSourceFileDependencyPath(dependency, resolvedName, stringlistPaths);
// Convert to a vector of AZStd::strings because GTest handles this type better when displaying errors
for (const QString& resolvedPath : stringlistPaths)
{
resolvedPaths.emplace_back(resolvedPath.toUtf8().constData());
}
return result;
}
void WildcardSourceDependencyTest::SetUp()
{
AssetProcessorManagerTest::SetUp();
QDir tempPath(m_tempDir.path());
// Add a non-recursive scan folder. Only files directly inside of this folder should be picked up, subfolders are ignored
m_config->AddScanFolder(ScanFolderInfo(tempPath.filePath("no_recurse"), "no_recurse",
"no_recurse", false, false, m_config->GetEnabledPlatforms(), 1));
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder1/1a.foo"));
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder1/1b.foo"));
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/a.foo"));
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/b.foo"));
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/folder/one/c.foo"));
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("subfolder2/redirected/folder/one/d.foo"));
// Add a file that is not in a scanfolder. Should always be ignored
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("not/a/scanfolder/e.foo"));
// Add a file in the non-recursive scanfolder. Since its not directly in the scan folder, it should always be ignored
UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("no_recurse/one/two/three/f.foo"));
}
TEST_F(WildcardSourceDependencyTest, Relative_Broad)
{
// Expect all files except for the 2 invalid ones (e and f)
AZStd::vector<AZStd::string> resolvedPaths;
ASSERT_TRUE(Test("*.foo", resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("a.foo", "b.foo", "folder/one/c.foo", "folder/one/d.foo", "1a.foo", "1b.foo"));
}
TEST_F(WildcardSourceDependencyTest, Relative_WithFolder)
{
// Make sure we can filter to files under a folder
AZStd::vector<AZStd::string> resolvedPaths;
ASSERT_TRUE(Test("folder/*.foo", resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("folder/one/c.foo", "folder/one/d.foo"));
}
TEST_F(WildcardSourceDependencyTest, Relative_WildcardPath)
{
// Make sure the * wildcard works even if the full filename is given
AZStd::vector<AZStd::string> resolvedPaths;
ASSERT_TRUE(Test("*a.foo", resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("a.foo", "1a.foo"));
}
TEST_F(WildcardSourceDependencyTest, Absolute_WithFolder)
{
// Make sure we can use absolute paths to filter to files under a folder
AZStd::vector<AZStd::string> resolvedPaths;
QDir tempPath(m_tempDir.path());
ASSERT_TRUE(Test(tempPath.absoluteFilePath("subfolder2/redirected/*.foo").toUtf8().constData(), resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre("a.foo", "b.foo", "folder/one/c.foo", "folder/one/d.foo"));
}
TEST_F(WildcardSourceDependencyTest, Absolute_NotInScanfolder)
{
// Files outside a scanfolder should not be returned even with an absolute path
AZStd::vector<AZStd::string> resolvedPaths;
QDir tempPath(m_tempDir.path());
ASSERT_TRUE(Test(tempPath.absoluteFilePath("not/a/scanfolder/*.foo").toUtf8().constData(), resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
}
TEST_F(WildcardSourceDependencyTest, Relative_NotInScanfolder)
{
// Files outside a scanfolder should not be returned
AZStd::vector<AZStd::string> resolvedPaths;
QDir tempPath(m_tempDir.path());
ASSERT_TRUE(Test("*/e.foo", resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
}
TEST_F(WildcardSourceDependencyTest, Relative_InNonRecursiveScanfolder)
{
// Files deep inside non-recursive scanfolders should not be returned
AZStd::vector<AZStd::string> resolvedPaths;
QDir tempPath(m_tempDir.path());
ASSERT_TRUE(Test("*/f.foo", resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
}
TEST_F(WildcardSourceDependencyTest, Absolute_InNonRecursiveScanfolder)
{
// Absolute paths to files deep inside non-recursive scanfolders should not be returned
AZStd::vector<AZStd::string> resolvedPaths;
QDir tempPath(m_tempDir.path());
ASSERT_TRUE(Test(tempPath.absoluteFilePath("one/two/three/*.foo").toUtf8().constData(), resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
}
TEST_F(WildcardSourceDependencyTest, Relative_NoWildcard)
{
// No wildcard results in a failure
AZStd::vector<AZStd::string> resolvedPaths;
QDir tempPath(m_tempDir.path());
ASSERT_FALSE(Test("subfolder1/1a.foo", resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
}
TEST_F(WildcardSourceDependencyTest, Absolute_NoWildcard)
{
// No wildcard results in a failure
AZStd::vector<AZStd::string> resolvedPaths;
QDir tempPath(m_tempDir.path());
ASSERT_FALSE(Test(tempPath.absoluteFilePath("subfolder1/1a.foo").toUtf8().constData(), resolvedPaths));
ASSERT_THAT(resolvedPaths, ::testing::UnorderedElementsAre());
}
@@ -131,6 +131,14 @@ struct MultiplatformPathDependencyTest
void SetUp() override;
};
struct WildcardSourceDependencyTest
: AssetProcessorManagerTest
{
bool Test(const AZStd::string& dependencyPath, AZStd::vector<AZStd::string>& resolvedPaths);
void SetUp() override;
};
struct MockBuilderInfoHandler
: public AssetProcessor::AssetBuilderInfoBus::Handler
{
+1
View File
@@ -34,6 +34,7 @@ ly_add_target(
INCLUDE_DIRECTORIES
PRIVATE
Source
Platform/${PAL_PLATFORM_NAME}
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
@@ -11,4 +11,6 @@ set(FILES
ProjectBuilderWorker_linux.cpp
ProjectUtils_linux.cpp
ProjectManagerDefs_linux.cpp
ProjectManager_Traits_Platform.h
ProjectManager_Traits_Linux.h
)
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR false
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <ProjectManager_Traits_Linux.h>
@@ -11,4 +11,6 @@ set(FILES
ProjectBuilderWorker_mac.cpp
ProjectUtils_mac.cpp
ProjectManagerDefs_mac.cpp
ProjectManager_Traits_Platform.h
ProjectManager_Traits_Mac.h
)
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR false
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <ProjectManager_Traits_Mac.h>
@@ -11,4 +11,6 @@ set(FILES
ProjectBuilderWorker_windows.cpp
ProjectUtils_windows.cpp
ProjectManagerDefs_windows.cpp
ProjectManager_Traits_Platform.h
ProjectManager_Traits_Windows.h
)
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <ProjectManager_Traits_Windows.h>
@@ -0,0 +1,11 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#define AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR true
@@ -9,7 +9,7 @@ QMainWindow {
#ScreensCtrl {
min-width:1200px;
min-height:800px;
min-height:700px;
}
QPushButton:focus {
@@ -16,6 +16,7 @@
#include <AzQtComponents/Utilities/HandleDpiAwareness.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
#include <ProjectManager_Traits_Platform.h>
#include <QApplication>
#include <QDir>
@@ -194,8 +195,12 @@ namespace O3DE::ProjectManager
// set stylesheet after creating the main window or their styles won't get updated
AzQtComponents::StyleManager::setStyleSheet(m_mainWindow.data(), QStringLiteral("style:ProjectManager.qss"));
// the decoration wrapper is intended to remember window positioning and sizing
// the decoration wrapper is intended to remember window positioning and sizing
#if AZ_TRAIT_PROJECT_MANAGER_CUSTOM_TITLEBAR
auto wrapper = new AzQtComponents::WindowDecorationWrapper();
#else
auto wrapper = new AzQtComponents::WindowDecorationWrapper(AzQtComponents::WindowDecorationWrapper::OptionDisabled);
#endif
wrapper->setGuest(m_mainWindow.data());
// show the main window here to apply the stylesheet before restoring geometry or we
@@ -81,8 +81,6 @@ namespace AZ
// Load the asset catalog so that we can find any nested assets successfully. We also need to tick the tick bus
// so that the OnCatalogLoaded event gets processed now, instead of during application shutdown.
AZ::Data::AssetCatalogRequestBus::Broadcast(
&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@products@/assetcatalog.xml");
application.Tick();
AZStd::string logggingScratchBuffer;
@@ -175,8 +175,6 @@ namespace AtomToolsFramework
AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast(
&AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotifications::OnDatabaseInitialized);
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@products@/assetcatalog.xml");
if (!AZ::RPI::RPISystemInterface::Get()->IsInitialized())
{
AZ::RPI::RPISystemInterface::Get()->InitializeSystemAssets();
@@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/EMotionFXAtom">
<file>Camera_category.svg</file>
<file>Visualization.svg</file>
</qresource>
</RCC>
@@ -0,0 +1,9 @@
<?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">
<!-- Generator: Sketch 60 (88103) - https://sketch.com -->
<title>Icons / System / View</title>
<desc>Created with Sketch.</desc>
<g id="Icons-/-System-/-View" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M11.9842667,6 C14.2018891,6 17.5404669,8 22,12 C17.5404669,16 14.2018891,18 11.9842667,18 C9.76664426,18 6.43855537,16 2,12 C6.43855537,8 9.76664426,6 11.9842667,6 Z M12,7.5 C9.51471863,7.5 7.5,9.51471863 7.5,12 C7.5,14.4852814 9.51471863,16.5 12,16.5 C14.4852814,16.5 16.5,14.4852814 16.5,12 C16.5,9.51471863 14.4852814,7.5 12,7.5 Z M12,9 C13.6568542,9 15,10.3431458 15,12 C15,13.6568542 13.6568542,15 12,15 C10.3431458,15 9,13.6568542 9,12 C9,10.3431458 10.3431458,9 12,9 Z" id="Combined-Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 977 B

@@ -0,0 +1,415 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AtomActorDebugDraw.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/AuxGeom/AuxGeomDraw.h>
#include <Atom/RPI.Public/AuxGeom/AuxGeomFeatureProcessorInterface.h>
#include <Integration/Rendering/RenderActorInstance.h>
#include <EMotionFX/Source/ActorInstance.h>
#include <EMotionFX/Source/DebugDraw.h>
#include <EMotionFX/Source/EMotionFXManager.h>
#include <EMotionFX/Source/SubMesh.h>
#include <EMotionFX/Source/TransformData.h>
#include <EMotionFX/Source/Mesh.h>
#include <EMotionFX/Source/Node.h>
namespace AZ::Render
{
AtomActorDebugDraw::AtomActorDebugDraw(AZ::EntityId entityId)
{
m_auxGeomFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity<RPI::AuxGeomFeatureProcessorInterface>(entityId);
}
void AtomActorDebugDraw::DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags, EMotionFX::ActorInstance* instance)
{
if (!m_auxGeomFeatureProcessor || !instance)
{
return;
}
RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
if (!auxGeom)
{
return;
}
// Render aabb
if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_AABB])
{
RenderAABB(instance);
}
// Render skeleton
if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_LINESKELETON])
{
RenderSkeleton(instance);
}
// Render internal EMFX debug lines.
if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_EMFX_DEBUG])
{
RenderEMFXDebugDraw(instance);
}
// Render vertex normal, face normal, tagent and wireframe.
const bool renderVertexNormals = renderFlags[EMotionFX::ActorRenderFlag::RENDER_VERTEXNORMALS];
const bool renderFaceNormals = renderFlags[EMotionFX::ActorRenderFlag::RENDER_FACENORMALS];
const bool renderTangents = renderFlags[EMotionFX::ActorRenderFlag::RENDER_TANGENTS];
const bool renderWireframe = renderFlags[EMotionFX::ActorRenderFlag::RENDER_WIREFRAME];
if (renderVertexNormals || renderFaceNormals || renderTangents || renderWireframe)
{
// Iterate through all enabled nodes
const EMotionFX::Pose* pose = instance->GetTransformData()->GetCurrentPose();
const size_t geomLODLevel = instance->GetLODLevel();
const size_t numEnabled = instance->GetNumEnabledNodes();
for (size_t i = 0; i < numEnabled; ++i)
{
EMotionFX::Node* node = instance->GetActor()->GetSkeleton()->GetNode(instance->GetEnabledNode(i));
EMotionFX::Mesh* mesh = instance->GetActor()->GetMesh(geomLODLevel, node->GetNodeIndex());
const AZ::Transform globalTM = pose->GetWorldSpaceTransform(node->GetNodeIndex()).ToAZTransform();
m_currentMesh = nullptr;
if (!mesh)
{
continue;
}
RenderNormals(mesh, globalTM, renderVertexNormals, renderFaceNormals);
if (renderTangents)
{
RenderTangents(mesh, globalTM);
}
}
}
}
void AtomActorDebugDraw::PrepareForMesh(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM)
{
// Check if we have already prepared for the given mesh
if (m_currentMesh == mesh)
{
return;
}
// Set our new current mesh
m_currentMesh = mesh;
// Get the number of vertices and the data
const uint32 numVertices = m_currentMesh->GetNumVertices();
AZ::Vector3* positions = (AZ::Vector3*)m_currentMesh->FindVertexData(EMotionFX::Mesh::ATTRIB_POSITIONS);
// Check if the vertices fits in our buffer
if (m_worldSpacePositions.size() < numVertices)
{
m_worldSpacePositions.resize(numVertices);
}
// Pre-calculate the world space positions
for (uint32 i = 0; i < numVertices; ++i)
{
m_worldSpacePositions[i] = worldTM.TransformPoint(positions[i]);
}
}
void AtomActorDebugDraw::RenderAABB(EMotionFX::ActorInstance* instance)
{
RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
const AZ::Aabb& aabb = instance->GetAabb();
auxGeom->DrawAabb(aabb, AZ::Color(0.0f, 1.0f, 1.0f, 1.0f), RPI::AuxGeomDraw::DrawStyle::Line);
}
void AtomActorDebugDraw::RenderSkeleton(EMotionFX::ActorInstance* instance)
{
RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
const EMotionFX::TransformData* transformData = instance->GetTransformData();
const EMotionFX::Skeleton* skeleton = instance->GetActor()->GetSkeleton();
const EMotionFX::Pose* pose = transformData->GetCurrentPose();
const size_t lodLevel = instance->GetLODLevel();
const size_t numJoints = skeleton->GetNumNodes();
m_auxVertices.clear();
m_auxVertices.reserve(numJoints * 2);
for (size_t jointIndex = 0; jointIndex < numJoints; ++jointIndex)
{
const EMotionFX::Node* joint = skeleton->GetNode(jointIndex);
if (!joint->GetSkeletalLODStatus(lodLevel))
{
continue;
}
const size_t parentIndex = joint->GetParentIndex();
if (parentIndex == InvalidIndex)
{
continue;
}
const AZ::Vector3 parentPos = pose->GetWorldSpaceTransform(parentIndex).m_position;
m_auxVertices.emplace_back(parentPos);
const AZ::Vector3 bonePos = pose->GetWorldSpaceTransform(jointIndex).m_position;
m_auxVertices.emplace_back(bonePos);
}
const AZ::Color skeletonColor(0.604f, 0.804f, 0.196f, 1.0f);
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast<uint32_t>(m_auxVertices.size());
lineArgs.m_colors = &skeletonColor;
lineArgs.m_colorCount = 1;
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
void AtomActorDebugDraw::RenderEMFXDebugDraw(EMotionFX::ActorInstance* instance)
{
RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
EMotionFX::DebugDraw& debugDraw = EMotionFX::GetDebugDraw();
debugDraw.Lock();
EMotionFX::DebugDraw::ActorInstanceData* actorInstanceData = debugDraw.GetActorInstanceData(instance);
actorInstanceData->Lock();
const AZStd::vector<EMotionFX::DebugDraw::Line>& lines = actorInstanceData->GetLines();
if (lines.empty())
{
actorInstanceData->Unlock();
debugDraw.Unlock();
return;
}
m_auxVertices.clear();
m_auxVertices.reserve(lines.size() * 2);
m_auxColors.clear();
m_auxColors.reserve(m_auxVertices.size());
for (const EMotionFX::DebugDraw::Line& line : actorInstanceData->GetLines())
{
m_auxVertices.emplace_back(line.m_start);
m_auxColors.emplace_back(line.m_startColor);
m_auxVertices.emplace_back(line.m_end);
m_auxColors.emplace_back(line.m_endColor);
}
AZ_Assert(m_auxVertices.size() == m_auxColors.size(), "Number of vertices and number of colors need to match.");
actorInstanceData->Unlock();
debugDraw.Unlock();
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast<uint32_t>(m_auxVertices.size());
lineArgs.m_colors = m_auxColors.data();
lineArgs.m_colorCount = static_cast<uint32_t>(m_auxColors.size());
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
void AtomActorDebugDraw::RenderNormals(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM, bool vertexNormals, bool faceNormals)
{
if (!mesh)
{
return;
}
if (!vertexNormals && !faceNormals)
{
return;
}
RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
if (!auxGeom)
{
return;
}
// TODO: Move line color to a render setting.
const float faceNormalsScale = 0.01f;
const AZ::Color colorFaceNormals = AZ::Colors::Lime;
const float vertexNormalsScale = 0.01f;
const AZ::Color colorVertexNormals = AZ::Colors::Orange;
PrepareForMesh(mesh, worldTM);
AZ::Vector3* normals = (AZ::Vector3*)mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_NORMALS);
// Render face normals
if (faceNormals)
{
const size_t numSubMeshes = mesh->GetNumSubMeshes();
for (uint32 subMeshIndex = 0; subMeshIndex < numSubMeshes; ++subMeshIndex)
{
EMotionFX::SubMesh* subMesh = mesh->GetSubMesh(subMeshIndex);
const uint32 numTriangles = subMesh->GetNumPolygons();
const uint32 startVertex = subMesh->GetStartVertex();
const uint32* indices = subMesh->GetIndices();
m_auxVertices.clear();
m_auxVertices.reserve(numTriangles * 2);
m_auxColors.clear();
m_auxColors.reserve(m_auxVertices.size());
for (uint32 triangleIndex = 0; triangleIndex < numTriangles; ++triangleIndex)
{
const uint32 triangleStartIndex = triangleIndex * 3;
const uint32 indexA = indices[triangleStartIndex + 0] + startVertex;
const uint32 indexB = indices[triangleStartIndex + 1] + startVertex;
const uint32 indexC = indices[triangleStartIndex + 2] + startVertex;
const AZ::Vector3& posA = m_worldSpacePositions[indexA];
const AZ::Vector3& posB = m_worldSpacePositions[indexB];
const AZ::Vector3& posC = m_worldSpacePositions[indexC];
const AZ::Vector3 normalDir = (posB - posA).Cross(posC - posA).GetNormalized();
// Calculate the center pos
const AZ::Vector3 normalPos = (posA + posB + posC) * (1.0f / 3.0f);
m_auxVertices.emplace_back(normalPos);
m_auxColors.emplace_back(colorFaceNormals);
m_auxVertices.emplace_back(normalPos + (normalDir * faceNormalsScale));
m_auxColors.emplace_back(colorFaceNormals);
}
}
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast<uint32_t>(m_auxVertices.size());
lineArgs.m_colors = m_auxColors.data();
lineArgs.m_colorCount = static_cast<uint32_t>(m_auxColors.size());
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
// render vertex normals
if (vertexNormals)
{
const size_t numSubMeshes = mesh->GetNumSubMeshes();
for (uint32 subMeshIndex = 0; subMeshIndex < numSubMeshes; ++subMeshIndex)
{
EMotionFX::SubMesh* subMesh = mesh->GetSubMesh(subMeshIndex);
const uint32 numVertices = subMesh->GetNumVertices();
const uint32 startVertex = subMesh->GetStartVertex();
m_auxVertices.clear();
m_auxVertices.reserve(numVertices * 2);
m_auxColors.clear();
m_auxColors.reserve(m_auxVertices.size());
for (uint32 j = 0; j < numVertices; ++j)
{
const uint32 vertexIndex = j + startVertex;
const AZ::Vector3& position = m_worldSpacePositions[vertexIndex];
const AZ::Vector3 normal = worldTM.TransformVector(normals[vertexIndex]).GetNormalizedSafe() * vertexNormalsScale;
m_auxVertices.emplace_back(position);
m_auxColors.emplace_back(colorFaceNormals);
m_auxVertices.emplace_back(position + normal);
m_auxColors.emplace_back(colorFaceNormals);
}
}
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast<uint32_t>(m_auxVertices.size());
lineArgs.m_colors = m_auxColors.data();
lineArgs.m_colorCount = static_cast<uint32_t>(m_auxColors.size());
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
}
void AtomActorDebugDraw::RenderTangents(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM)
{
if (!mesh)
{
return;
}
RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
if (!auxGeom)
{
return;
}
// TODO: Move line color to a render setting.
const AZ::Color colorTangents = AZ::Colors::Red;
const AZ::Color mirroredBitangentColor = AZ::Colors::Yellow;
const AZ::Color colorBitangents = AZ::Colors::White;
const float scale = 0.01f;
// Get the tangents and check if this mesh actually has tangents
AZ::Vector4* tangents = static_cast<AZ::Vector4*>(mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_TANGENTS));
if (!tangents)
{
return;
}
AZ::Vector3* bitangents = static_cast<AZ::Vector3*>(mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_BITANGENTS));
PrepareForMesh(mesh, worldTM);
AZ::Vector3* normals = (AZ::Vector3*)mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_NORMALS);
const uint32 numVertices = mesh->GetNumVertices();
m_auxVertices.clear();
m_auxVertices.reserve(numVertices * 2);
m_auxColors.clear();
m_auxColors.reserve(m_auxVertices.size());
// Render the tangents and bitangents
AZ::Vector3 orgTangent, tangent, bitangent;
for (uint32 i = 0; i < numVertices; ++i)
{
orgTangent.Set(tangents[i].GetX(), tangents[i].GetY(), tangents[i].GetZ());
tangent = (worldTM.TransformVector(orgTangent)).GetNormalized();
if (bitangents)
{
bitangent = bitangents[i];
}
else
{
bitangent = tangents[i].GetW() * normals[i].Cross(orgTangent);
}
bitangent = (worldTM.TransformVector(bitangent)).GetNormalizedSafe();
m_auxVertices.emplace_back(m_worldSpacePositions[i]);
m_auxColors.emplace_back(colorTangents);
m_auxVertices.emplace_back(m_worldSpacePositions[i] + (tangent * scale));
m_auxColors.emplace_back(colorTangents);
if (tangents[i].GetW() < 0.0f)
{
m_auxVertices.emplace_back(m_worldSpacePositions[i]);
m_auxColors.emplace_back(mirroredBitangentColor);
m_auxVertices.emplace_back(m_worldSpacePositions[i] + (bitangent * scale));
m_auxColors.emplace_back(mirroredBitangentColor);
}
else
{
m_auxVertices.emplace_back(m_worldSpacePositions[i]);
m_auxColors.emplace_back(colorBitangents);
m_auxVertices.emplace_back(m_worldSpacePositions[i] + (bitangent * scale));
m_auxColors.emplace_back(colorBitangents);
}
}
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast<uint32_t>(m_auxVertices.size());
lineArgs.m_colors = m_auxColors.data();
lineArgs.m_colorCount = static_cast<uint32_t>(m_auxColors.size());
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
} // namespace AZ::Render
@@ -0,0 +1,56 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/containers/vector.h>
#include <AzCore/Math/Color.h>
#include <Integration/Rendering/RenderFlag.h>
#include <Integration/Rendering/RenderActorInstance.h>
namespace EMotionFX
{
class Mesh;
class ActorInstance;
}
namespace AZ::RPI
{
class AuxGeomDraw;
class AuxGeomFeatureProcessorInterface;
}
namespace AZ::Render
{
// Ultility class for atom debug render on actor
class AtomActorDebugDraw
{
public:
AtomActorDebugDraw(AZ::EntityId entityId);
void DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags, EMotionFX::ActorInstance* instance);
private:
void PrepareForMesh(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM);
void RenderAABB(EMotionFX::ActorInstance* instance);
void RenderSkeleton(EMotionFX::ActorInstance* instance);
void RenderEMFXDebugDraw(EMotionFX::ActorInstance* instance);
void RenderNormals(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM, bool vertexNormals, bool faceNormals);
void RenderTangents(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM);
EMotionFX::Mesh* m_currentMesh = nullptr; /**< A pointer to the mesh whose world space positions are in the pre-calculated positions buffer.
NULL in case we haven't pre-calculated any positions yet. */
AZStd::vector<AZ::Vector3> m_worldSpacePositions; /**< The buffer used to store world space positions for rendering normals
tangents and the wireframe. */
RPI::AuxGeomFeatureProcessorInterface* m_auxGeomFeatureProcessor = nullptr;
AZStd::vector<AZ::Vector3> m_auxVertices;
AZStd::vector<AZ::Color> m_auxColors;
};
}
@@ -8,6 +8,7 @@
#include <AtomActorInstance.h>
#include <AtomActor.h>
#include <AtomActorDebugDraw.h>
#include <ActorAsset.h>
#include <Atom/Feature/SkinnedMesh/SkinnedMeshInputBuffers.h>
@@ -59,7 +60,7 @@ namespace AZ
AzFramework::BoundsRequestBus::Handler::BusConnect(m_entityId);
}
m_auxGeomFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity<RPI::AuxGeomFeatureProcessorInterface>(m_entityId);
m_atomActorDebugDraw = AZStd::make_unique<AtomActorDebugDraw>(entityId);
}
AtomActorInstance::~AtomActorInstance()
@@ -78,6 +79,11 @@ namespace AZ
UpdateBounds();
}
void AtomActorInstance::DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags)
{
m_atomActorDebugDraw->DebugDraw(renderFlags, m_actorInstance);
}
void AtomActorInstance::UpdateBounds()
{
// Update RenderActorInstance world bounding box
@@ -99,116 +105,6 @@ namespace AZ
AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->RefreshEntityLocalBoundsUnion(m_entityId);
}
void AtomActorInstance::DebugDraw(const DebugOptions& debugOptions)
{
if (m_auxGeomFeatureProcessor)
{
if (RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue())
{
if (debugOptions.m_drawAABB)
{
const AZ::Aabb& aabb = m_actorInstance->GetAabb();
auxGeom->DrawAabb(aabb, AZ::Color(0.0f, 1.0f, 1.0f, 1.0f), RPI::AuxGeomDraw::DrawStyle::Line);
}
if (debugOptions.m_drawSkeleton)
{
RenderSkeleton(auxGeom.get());
}
if (debugOptions.m_emfxDebugDraw)
{
RenderEMFXDebugDraw(auxGeom.get());
}
}
}
}
void AtomActorInstance::RenderSkeleton(RPI::AuxGeomDraw* auxGeom)
{
AZ_Assert(m_actorInstance, "Valid actor instance required.");
const EMotionFX::TransformData* transformData = m_actorInstance->GetTransformData();
const EMotionFX::Skeleton* skeleton = m_actorInstance->GetActor()->GetSkeleton();
const EMotionFX::Pose* pose = transformData->GetCurrentPose();
const size_t lodLevel = m_actorInstance->GetLODLevel();
const size_t numJoints = skeleton->GetNumNodes();
m_auxVertices.clear();
m_auxVertices.reserve(numJoints * 2);
for (size_t jointIndex = 0; jointIndex < numJoints; ++jointIndex)
{
const EMotionFX::Node* joint = skeleton->GetNode(jointIndex);
if (!joint->GetSkeletalLODStatus(lodLevel))
{
continue;
}
const size_t parentIndex = joint->GetParentIndex();
if (parentIndex == InvalidIndex)
{
continue;
}
const AZ::Vector3 parentPos = pose->GetWorldSpaceTransform(parentIndex).m_position;
m_auxVertices.emplace_back(parentPos);
const AZ::Vector3 bonePos = pose->GetWorldSpaceTransform(jointIndex).m_position;
m_auxVertices.emplace_back(bonePos);
}
const AZ::Color skeletonColor(0.604f, 0.804f, 0.196f, 1.0f);
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast<uint32_t>(m_auxVertices.size());
lineArgs.m_colors = &skeletonColor;
lineArgs.m_colorCount = 1;
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
void AtomActorInstance::RenderEMFXDebugDraw(RPI::AuxGeomDraw* auxGeom)
{
EMotionFX::DebugDraw& debugDraw = EMotionFX::GetDebugDraw();
debugDraw.Lock();
EMotionFX::DebugDraw::ActorInstanceData* actorInstanceData = debugDraw.GetActorInstanceData(m_actorInstance);
actorInstanceData->Lock();
const AZStd::vector<EMotionFX::DebugDraw::Line>& lines = actorInstanceData->GetLines();
if (lines.empty())
{
actorInstanceData->Unlock();
debugDraw.Unlock();
return;
}
m_auxVertices.clear();
m_auxVertices.reserve(lines.size() * 2);
m_auxColors.clear();
m_auxColors.reserve(m_auxVertices.size());
for (const EMotionFX::DebugDraw::Line& line : actorInstanceData->GetLines())
{
m_auxVertices.emplace_back(line.m_start);
m_auxColors.emplace_back(line.m_startColor);
m_auxVertices.emplace_back(line.m_end);
m_auxColors.emplace_back(line.m_endColor);
}
AZ_Assert(m_auxVertices.size() == m_auxColors.size(),
"Number of vertices and number of colors need to match.");
actorInstanceData->Unlock();
debugDraw.Unlock();
RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
lineArgs.m_verts = m_auxVertices.data();
lineArgs.m_vertCount = static_cast<uint32_t>(m_auxVertices.size());
lineArgs.m_colors = m_auxColors.data();
lineArgs.m_colorCount = static_cast<uint32_t>(m_auxColors.size());
lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
auxGeom->DrawLines(lineArgs);
}
AZ::Aabb AtomActorInstance::GetWorldBounds()
{
return m_worldAABB;
@@ -53,6 +53,7 @@ namespace AZ
class SkinnedMeshInputBuffers;
class MeshFeatureProcessorInterface;
class AtomActor;
class AtomActorDebugDraw;
//! Render node for managing and rendering actor instances. Each Actor Component
//! creates an ActorRenderNode. The render node is responsible for drawing meshes and
@@ -85,8 +86,8 @@ namespace AZ
// RenderActorInstance overrides ...
void OnTick(float timeDelta) override;
void DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags);
void UpdateBounds() override;
void DebugDraw(const DebugOptions& debugOptions) override;
void SetMaterials(const EMotionFX::Integration::ActorAsset::MaterialList& materialPerLOD) override { AZ_UNUSED(materialPerLOD); };
void SetSkinningMethod(EMotionFX::Integration::SkinningMethod emfxSkinningMethod) override;
SkinningMethod GetAtomSkinningMethod() const;
@@ -184,12 +185,8 @@ namespace AZ
void InitWrinkleMasks();
void UpdateWrinkleMasks();
// Helper and debug geometry rendering
void RenderSkeleton(RPI::AuxGeomDraw* auxGeom);
void RenderEMFXDebugDraw(RPI::AuxGeomDraw* auxGeom);
RPI::AuxGeomFeatureProcessorInterface* m_auxGeomFeatureProcessor = nullptr;
AZStd::vector<AZ::Vector3> m_auxVertices;
AZStd::vector<AZ::Color> m_auxColors;
// Debug geometry rendering
AZStd::unique_ptr<AtomActorDebugDraw> m_atomActorDebugDraw;
AZStd::intrusive_ptr<AZ::Render::SkinnedMeshInputBuffers> m_skinnedMeshInputBuffers = nullptr;
AZStd::intrusive_ptr<SkinnedMeshInstance> m_skinnedMeshInstance;
@@ -206,6 +206,20 @@ namespace EMStudio
return result;
}
void AnimViewportRenderer::UpdateActorRenderFlag(EMotionFX::ActorRenderFlagBitset renderFlags)
{
for (AZ::Entity* entity : m_actorEntities)
{
EMotionFX::Integration::ActorComponent* actorComponent = entity->FindComponent<EMotionFX::Integration::ActorComponent>();
if (!actorComponent)
{
AZ_Assert(false, "Found entity without actor component in the actor entity list.");
continue;
}
actorComponent->SetRenderFlag(renderFlags);
}
}
void AnimViewportRenderer::ResetEnvironment()
{
// Reset environment
@@ -52,6 +52,8 @@ namespace EMStudio
//! Return the center position of the existing objects.
AZ::Vector3 GetCharacterCenter() const;
void UpdateActorRenderFlag(EMotionFX::ActorRenderFlagBitset renderFlags);
private:
// This function resets the light, camera and other environment settings.
@@ -79,10 +81,6 @@ namespace EMStudio
AZ::Entity* m_postProcessEntity = nullptr;
AZ::Entity* m_iblEntity = nullptr;
AZ::Entity* m_cameraEntity = nullptr;
AZ::Component* m_cameraComponent = nullptr;
AZ::Entity* m_modelEntity = nullptr;
AZ::Data::AssetId m_modelAssetId;
AZ::Entity* m_gridEntity = nullptr;
AZStd::vector<AZ::Entity*> m_actorEntities;
@@ -8,7 +8,7 @@
#pragma once
#include <AzCore/EBus/EBus.h>
#include <Integration/Rendering/RenderFlag.h>
namespace EMStudio
{
@@ -35,6 +35,9 @@ namespace EMStudio
//! Set the camera view mode.
virtual void SetCameraViewMode(CameraViewMode mode) = 0;
//! Toggle render option flag
virtual void ToggleRenderFlag(EMotionFX::ActorRenderFlag flag) = 0;
};
using AnimViewportRequestBus = AZ::EBus<AnimViewportRequests>;
@@ -13,7 +13,6 @@
#include <AzCore/std/string/string.h>
#include <AzQtComponents/Components/Widgets/ToolBar.h>
namespace EMStudio
{
AnimViewportToolBar::AnimViewportToolBar(QWidget* parent)
@@ -21,40 +20,107 @@ namespace EMStudio
{
AzQtComponents::ToolBar::addMainToolBarStyle(this);
// Add the camera button
QToolButton* cameraButton = new QToolButton(this);
QMenu* cameraMenu = new QMenu(cameraButton);
// Add the camera option
const AZStd::vector<AZStd::pair<CameraViewMode, AZStd::string>> cameraOptionNames = {
{ CameraViewMode::FRONT, "Front" }, { CameraViewMode::BACK, "Back" }, { CameraViewMode::TOP, "Top" },
{ CameraViewMode::BOTTOM, "Bottom" }, { CameraViewMode::LEFT, "Left" }, { CameraViewMode::RIGHT, "Right" },
};
for (const auto& pair : cameraOptionNames)
// Add the render view options button
QToolButton* renderOptionsButton = new QToolButton(this);
{
CameraViewMode mode = pair.first;
cameraMenu->addAction(
pair.second.c_str(),
[mode]()
{
// Send the reset camera event.
AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::SetCameraViewMode, mode);
});
QMenu* contextMenu = new QMenu(renderOptionsButton);
renderOptionsButton->setText("Render Options");
renderOptionsButton->setMenu(contextMenu);
renderOptionsButton->setPopupMode(QToolButton::InstantPopup);
renderOptionsButton->setVisible(true);
renderOptionsButton->setIcon(QIcon(":/EMotionFXAtom/Visualization.svg"));
addWidget(renderOptionsButton);
CreateViewOptionEntry(contextMenu, "Solid", EMotionFX::ActorRenderFlag::RENDER_SOLID);
CreateViewOptionEntry(contextMenu, "Wireframe", EMotionFX::ActorRenderFlag::RENDER_WIREFRAME);
CreateViewOptionEntry(contextMenu, "Lighting", EMotionFX::ActorRenderFlag::RENDER_LIGHTING);
CreateViewOptionEntry(contextMenu, "Backface Culling", EMotionFX::ActorRenderFlag::RENDER_BACKFACECULLING);
contextMenu->addSeparator();
CreateViewOptionEntry(contextMenu, "Vertex Normals", EMotionFX::ActorRenderFlag::RENDER_VERTEXNORMALS);
CreateViewOptionEntry(contextMenu, "Face Normals", EMotionFX::ActorRenderFlag::RENDER_FACENORMALS);
CreateViewOptionEntry(contextMenu, "Tangents", EMotionFX::ActorRenderFlag::RENDER_TANGENTS);
CreateViewOptionEntry(contextMenu, "Actor Bounding Boxes", EMotionFX::ActorRenderFlag::RENDER_AABB);
contextMenu->addSeparator();
CreateViewOptionEntry(contextMenu, "Line Skeleton", EMotionFX::ActorRenderFlag::RENDER_LINESKELETON);
CreateViewOptionEntry(contextMenu, "Solid Skeleton", EMotionFX::ActorRenderFlag::RENDER_SKELETON);
CreateViewOptionEntry(contextMenu, "Joint Names", EMotionFX::ActorRenderFlag::RENDER_NODENAMES);
CreateViewOptionEntry(contextMenu, "Joint Orientations", EMotionFX::ActorRenderFlag::RENDER_NODEORIENTATION);
CreateViewOptionEntry(contextMenu, "Actor Bind Pose", EMotionFX::ActorRenderFlag::RENDER_ACTORBINDPOSE);
contextMenu->addSeparator();
}
cameraMenu->addSeparator();
cameraMenu->addAction("Reset Camera",
[]()
// Add the camera button
QToolButton* cameraButton = new QToolButton(this);
{
QMenu* cameraMenu = new QMenu(cameraButton);
// Add the camera option
const AZStd::vector<AZStd::pair<CameraViewMode, AZStd::string>> cameraOptionNames = {
{ CameraViewMode::FRONT, "Front" }, { CameraViewMode::BACK, "Back" }, { CameraViewMode::TOP, "Top" },
{ CameraViewMode::BOTTOM, "Bottom" }, { CameraViewMode::LEFT, "Left" }, { CameraViewMode::RIGHT, "Right" },
};
for (const auto& pair : cameraOptionNames)
{
CameraViewMode mode = pair.first;
cameraMenu->addAction(
pair.second.c_str(),
[mode]()
{
// Send the reset camera event.
AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::SetCameraViewMode, mode);
});
}
cameraMenu->addSeparator();
cameraMenu->addAction(
"Reset Camera",
[]()
{
// Send the reset camera event.
AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::ResetCamera);
});
cameraButton->setMenu(cameraMenu);
cameraButton->setText("Camera Option");
cameraButton->setPopupMode(QToolButton::InstantPopup);
cameraButton->setVisible(true);
cameraButton->setIcon(QIcon(":/EMotionFXAtom/Camera_category.svg"));
addWidget(cameraButton);
}
}
void AnimViewportToolBar::CreateViewOptionEntry(
QMenu* menu, const char* menuEntryName, uint32_t actionIndex, bool visible, char* iconFileName)
{
QAction* action = menu->addAction(
menuEntryName,
[actionIndex]()
{
// Send the reset camera event.
AnimViewportRequestBus::Broadcast(&AnimViewportRequestBus::Events::ResetCamera);
AnimViewportRequestBus::Broadcast(
&AnimViewportRequestBus::Events::ToggleRenderFlag, (EMotionFX::ActorRenderFlag)actionIndex);
});
cameraButton->setMenu(cameraMenu);
cameraButton->setText("Camera Option");
cameraButton->setPopupMode(QToolButton::InstantPopup);
cameraButton->setVisible(true);
cameraButton->setIcon(QIcon(":/EMotionFXAtom/Camera_category.svg"));
addWidget(cameraButton);
action->setCheckable(true);
action->setVisible(visible);
if (iconFileName)
{
action->setIcon(QIcon(iconFileName));
}
m_actions[actionIndex] = action;
}
void AnimViewportToolBar::SetRenderFlags(EMotionFX::ActorRenderFlagBitset renderFlags)
{
for (size_t i = 0; i < renderFlags.size(); ++i)
{
QAction* action = m_actions[i];
if (action)
{
action->setChecked(renderFlags[i]);
}
}
}
} // namespace EMStudio
@@ -11,8 +11,11 @@
#if !defined(Q_MOC_RUN)
#include <QAction>
#include <QToolBar>
#include <QMenu>
#endif
#include <Integration/Rendering/RenderFlag.h>
namespace EMStudio
{
class AnimViewportToolBar : public QToolBar
@@ -20,5 +23,13 @@ namespace EMStudio
public:
AnimViewportToolBar(QWidget* parent = nullptr);
~AnimViewportToolBar() = default;
void SetRenderFlags(EMotionFX::ActorRenderFlagBitset renderFlags);
private:
void CreateViewOptionEntry(
QMenu* menu, const char* menuEntryName, uint32_t actionIndex, bool visible = true, char* iconFileName = nullptr);
QAction* m_actions[EMotionFX::ActorRenderFlag::NUM_RENDERFLAGS] = { nullptr };
};
}
@@ -11,6 +11,7 @@
#include <AzFramework/Viewport/CameraInput.h>
#include <Atom/RPI.Public/ViewportContext.h>
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
#include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/EMStudioManager.h>
#include <EMStudio/AnimViewportWidget.h>
#include <EMStudio/AnimViewportRenderer.h>
@@ -32,6 +33,7 @@ namespace EMStudio
m_renderer = AZStd::make_unique<AnimViewportRenderer>(GetViewportContext());
LoadRenderFlags();
SetupCameras();
SetupCameraController();
Reinit();
@@ -41,6 +43,7 @@ namespace EMStudio
AnimViewportWidget::~AnimViewportWidget()
{
SaveRenderFlags();
AnimViewportRequestBus::Handler::BusDisconnect();
}
@@ -50,7 +53,14 @@ namespace EMStudio
{
ResetCamera();
}
m_renderer->Reinit();
m_renderer->UpdateActorRenderFlag(m_renderFlags);
}
EMotionFX::ActorRenderFlagBitset AnimViewportWidget::GetRenderFlags() const
{
return m_renderFlags;
}
void AnimViewportWidget::SetupCameras()
@@ -123,7 +133,7 @@ namespace EMStudio
SetCameraViewMode(CameraViewMode::DEFAULT);
}
void AnimViewportWidget::SetCameraViewMode([[maybe_unused]]CameraViewMode mode)
void AnimViewportWidget::SetCameraViewMode(CameraViewMode mode)
{
// Set the camera view mode.
const AZ::Vector3 targetPosition = m_renderer->GetCharacterCenter();
@@ -155,4 +165,38 @@ namespace EMStudio
}
GetViewportContext()->SetCameraTransform(AZ::Transform::CreateLookAt(cameraPosition, targetPosition));
}
void AnimViewportWidget::ToggleRenderFlag(EMotionFX::ActorRenderFlag flag)
{
m_renderFlags[flag] = !m_renderFlags[flag];
m_renderer->UpdateActorRenderFlag(m_renderFlags);
}
void AnimViewportWidget::LoadRenderFlags()
{
AZStd::string renderFlagsFilename(EMStudioManager::GetInstance()->GetAppDataFolder());
renderFlagsFilename += "AnimViewportRenderFlags.cfg";
QSettings settings(renderFlagsFilename.c_str(), QSettings::IniFormat, this);
for (uint32 i = 0; i < EMotionFX::ActorRenderFlag::NUM_RENDERFLAGS; ++i)
{
QString name = QString(i);
const bool isEnabled = settings.value(name).toBool();
m_renderFlags[i] = isEnabled;
}
m_renderer->UpdateActorRenderFlag(m_renderFlags);
}
void AnimViewportWidget::SaveRenderFlags()
{
AZStd::string renderFlagsFilename(EMStudioManager::GetInstance()->GetAppDataFolder());
renderFlagsFilename += "AnimViewportRenderFlags.cfg";
QSettings settings(renderFlagsFilename.c_str(), QSettings::IniFormat, this);
for (uint32 i = 0; i < EMotionFX::ActorRenderFlag::NUM_RENDERFLAGS; ++i)
{
QString name = QString(i);
settings.setValue(name, (bool)m_renderFlags[i]);
}
}
} // namespace EMStudio
@@ -7,9 +7,11 @@
*/
#pragma once
#include <QSettings>
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
#include <AzFramework/Viewport/CameraInput.h>
#include <EMStudio/AnimViewportRequestBus.h>
#include <Integration/Rendering/RenderFlag.h>
namespace EMStudio
{
@@ -25,14 +27,19 @@ namespace EMStudio
AnimViewportRenderer* GetAnimViewportRenderer() { return m_renderer.get(); }
void Reinit(bool resetCamera = true);
EMotionFX::ActorRenderFlagBitset GetRenderFlags() const;
private:
void SetupCameras();
void SetupCameraController();
void LoadRenderFlags();
void SaveRenderFlags();
// AnimViewportRequestBus::Handler overrides
void ResetCamera();
void SetCameraViewMode(CameraViewMode mode);
void ToggleRenderFlag(EMotionFX::ActorRenderFlag flag);
static constexpr float CameraDistance = 2.0f;
@@ -40,5 +47,6 @@ namespace EMStudio
AZStd::shared_ptr<AzFramework::RotateCameraInput> m_rotateCamera;
AZStd::shared_ptr<AzFramework::TranslateCameraInput> m_translateCamera;
AZStd::shared_ptr<AzFramework::OrbitDollyScrollCameraInput> m_orbitDollyScrollCamera;
EMotionFX::ActorRenderFlagBitset m_renderFlags;
};
}
@@ -90,12 +90,14 @@ namespace EMStudio
verticalLayout->setSpacing(1);
verticalLayout->setMargin(0);
// Add the tool bar
AnimViewportToolBar* toolBar = new AnimViewportToolBar(m_innerWidget);
verticalLayout->addWidget(toolBar);
// Add the viewport widget
m_animViewportWidget = new AnimViewportWidget(m_innerWidget);
// Add the tool bar
AnimViewportToolBar* toolBar = new AnimViewportToolBar(m_innerWidget);
toolBar->SetRenderFlags(m_animViewportWidget->GetRenderFlags());
verticalLayout->addWidget(toolBar);
verticalLayout->addWidget(m_animViewportWidget);
// Register command callbacks.
@@ -17,4 +17,6 @@ set(FILES
Source/AtomActor.cpp
Source/AtomActorInstance.h
Source/AtomActorInstance.cpp
Source/AtomActorDebugDraw.h
Source/AtomActorDebugDraw.cpp
)
@@ -143,6 +143,7 @@ namespace Blast
void BlastSystemComponent::Deactivate()
{
AZ_PROFILE_FUNCTION(Physics);
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
CrySystemEventBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
BlastSystemRequestBus::Handler::BusDisconnect();
@@ -85,9 +85,6 @@ namespace EMotionFX
/// Detach from parent entity, if attached.
virtual void DetachFromEntity() {}
/// Enables debug-drawing of the actor's root.
virtual void DebugDrawRoot(bool /*enable*/) {}
/// Enables rendering of the actor.
virtual bool GetRenderCharacter() const = 0;
virtual void SetRenderCharacter(bool enable) = 0;
@@ -209,7 +209,6 @@ namespace EMotionFX
->Event("GetJointTransform", &ActorComponentRequestBus::Events::GetJointTransform)
->Event("AttachToEntity", &ActorComponentRequestBus::Events::AttachToEntity)
->Event("DetachFromEntity", &ActorComponentRequestBus::Events::DetachFromEntity)
->Event("DebugDrawRoot", &ActorComponentRequestBus::Events::DebugDrawRoot)
->Event("GetRenderCharacter", &ActorComponentRequestBus::Events::GetRenderCharacter)
->Event("SetRenderCharacter", &ActorComponentRequestBus::Events::SetRenderCharacter)
->Event("GetRenderActorVisible", &ActorComponentRequestBus::Events::GetRenderActorVisible)
@@ -238,8 +237,7 @@ namespace EMotionFX
//////////////////////////////////////////////////////////////////////////
ActorComponent::ActorComponent(const Configuration* configuration)
: m_debugDrawRoot(false)
, m_sceneFinishSimHandler([this]([[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
: m_sceneFinishSimHandler([this]([[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
float fixedDeltatime)
{
if (m_actorInstance)
@@ -252,6 +250,8 @@ namespace EMotionFX
{
m_configuration = *configuration;
}
m_debugRenderFlags[RENDER_SOLID] = true;
}
//////////////////////////////////////////////////////////////////////////
@@ -341,12 +341,6 @@ namespace EMotionFX
}
}
//////////////////////////////////////////////////////////////////////////
void ActorComponent::DebugDrawRoot(bool enable)
{
m_debugDrawRoot = enable;
}
//////////////////////////////////////////////////////////////////////////
bool ActorComponent::GetRenderCharacter() const
{
@@ -400,6 +394,11 @@ namespace EMotionFX
return m_sceneFinishSimHandler.IsConnected();
}
void ActorComponent::SetRenderFlag(ActorRenderFlagBitset renderFlags)
{
m_debugRenderFlags = renderFlags;
}
void ActorComponent::CheckActorCreation()
{
// Create actor instance.
@@ -573,13 +572,13 @@ namespace EMotionFX
m_actorInstance->SetIsVisible(isInCameraFrustum && m_configuration.m_renderCharacter);
}
RenderActorInstance::DebugOptions debugOptions;
debugOptions.m_drawAABB = m_configuration.m_renderBounds;
debugOptions.m_drawSkeleton = m_configuration.m_renderSkeleton;
debugOptions.m_drawRootTransform = m_debugDrawRoot;
debugOptions.m_rootWorldTransform = GetEntity()->GetTransform()->GetWorldTM();
debugOptions.m_emfxDebugDraw = true;
m_renderActorInstance->DebugDraw(debugOptions);
m_renderActorInstance->SetIsVisible(m_debugRenderFlags[RENDER_SOLID]);
// The configuration stores some debug option. When that is enabled, we override it on top of the render flags.
m_debugRenderFlags[RENDER_AABB] = m_debugRenderFlags[RENDER_AABB] || m_configuration.m_renderBounds;
m_debugRenderFlags[RENDER_SKELETON] = m_debugRenderFlags[RENDER_SKELETON] || m_configuration.m_renderSkeleton;
m_debugRenderFlags[RENDER_EMFX_DEBUG] = true;
m_renderActorInstance->DebugDraw(m_debugRenderFlags);
}
}
@@ -116,7 +116,6 @@ namespace EMotionFX
ActorInstance* GetActorInstance() override { return m_actorInstance.get(); }
void AttachToEntity(AZ::EntityId targetEntityId, AttachmentType attachmentType) override;
void DetachFromEntity() override;
void DebugDrawRoot(bool enable) override;
bool GetRenderCharacter() const override;
void SetRenderCharacter(bool enable) override;
bool GetRenderActorVisible() const override;
@@ -181,6 +180,8 @@ namespace EMotionFX
bool IsPhysicsSceneSimulationFinishEventConnected() const;
AZ::Data::Asset<ActorAsset> GetActorAsset() const { return m_configuration.m_actorAsset; }
void SetRenderFlag(ActorRenderFlagBitset renderFlags);
private:
// AZ::TransformNotificationBus::MultiHandler
void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
@@ -201,7 +202,7 @@ namespace EMotionFX
AZStd::vector<AZ::EntityId> m_attachments;
AZStd::unique_ptr<RenderActorInstance> m_renderActorInstance;
bool m_debugDrawRoot; ///< Enables drawing of actor root and facing.
ActorRenderFlagBitset m_debugRenderFlags; ///< Actor debug render flag
AzPhysics::SceneEvents::OnSceneSimulationFinishHandler m_sceneFinishSimHandler;
};
@@ -180,6 +180,7 @@ namespace EMotionFX
, m_lodLevel(0)
, m_actorAsset(AZ::Data::AssetLoadBehavior::NoLoad)
{
m_debugRenderFlags[RENDER_SOLID] = true;
}
//////////////////////////////////////////////////////////////////////////
@@ -604,11 +605,10 @@ namespace EMotionFX
m_renderActorInstance->OnTick(deltaTime);
m_renderActorInstance->UpdateBounds();
RenderActorInstance::DebugOptions debugOptions;
debugOptions.m_drawAABB = m_renderBounds;
debugOptions.m_drawSkeleton = m_renderSkeleton;
debugOptions.m_emfxDebugDraw = true;
m_renderActorInstance->DebugDraw(debugOptions);
m_debugRenderFlags[RENDER_AABB] = m_renderBounds;
m_debugRenderFlags[RENDER_SKELETON] = m_renderSkeleton;
m_debugRenderFlags[RENDER_EMFX_DEBUG] = true;
m_renderActorInstance->DebugDraw(m_debugRenderFlags);
}
}
@@ -951,5 +951,10 @@ namespace EMotionFX
LmbrCentral::AttachmentComponentRequestBus::Event(attachment, &LmbrCentral::AttachmentComponentRequestBus::Events::Reattach, true);
}
}
void EditorActorComponent::SetRenderFlag(ActorRenderFlagBitset renderFlags)
{
m_debugRenderFlags = renderFlags;
}
} //namespace Integration
} // namespace EMotionFX
@@ -104,6 +104,8 @@ namespace EMotionFX
ActorComponent::GetRequiredServices(required);
}
void SetRenderFlag(ActorRenderFlagBitset renderFlags);
static void Reflect(AZ::ReflectContext* context);
private:
@@ -162,6 +164,7 @@ namespace EMotionFX
size_t m_lodLevel;
ActorComponent::BoundingBoxConfiguration m_bboxConfig;
bool m_forceUpdateJointsOOV = false;
ActorRenderFlagBitset m_debugRenderFlags; ///< Actor debug render flag
// \todo attachmentTarget node nr
// Note: LOD work in progress. For now we use one material instead of a list of material, because we don't have the support for LOD with multiple scene files.
@@ -16,6 +16,7 @@
#include <Integration/ActorComponentBus.h>
#include <Integration/Assets/ActorAsset.h>
#include <Integration/Rendering/RenderFlag.h>
namespace EMotionFX
{
@@ -33,16 +34,7 @@ namespace EMotionFX
virtual ~RenderActorInstance() = default;
virtual void OnTick(float timeDelta) = 0;
struct DebugOptions
{
bool m_drawAABB = false;
bool m_drawSkeleton = false;
bool m_drawRootTransform = false;
AZ::Transform m_rootWorldTransform = AZ::Transform::CreateIdentity();
bool m_emfxDebugDraw = false;
};
virtual void DebugDraw(const DebugOptions& debugOptions) = 0;
virtual void DebugDraw(const EMotionFX::ActorRenderFlagBitset& renderFlags) = 0;
SkinningMethod GetSkinningMethod() const;
virtual void SetSkinningMethod(SkinningMethod skinningMethod);
@@ -0,0 +1,44 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/containers/bitset.h>
namespace EMotionFX
{
enum ActorRenderFlag
{
RENDER_SOLID = 0,
RENDER_WIREFRAME = 1,
RENDER_LIGHTING = 2,
RENDER_SHADOWS = 3,
RENDER_FACENORMALS = 4,
RENDER_VERTEXNORMALS = 5,
RENDER_TANGENTS = 6,
RENDER_AABB = 7,
RENDER_SKELETON = 8,
RENDER_LINESKELETON = 9,
RENDER_NODEORIENTATION = 10,
RENDER_NODENAMES = 11,
RENDER_GRID = 12,
RENDER_BACKFACECULLING = 13,
RENDER_ACTORBINDPOSE = 14,
RENDER_RAGDOLL_COLLIDERS = 15,
RENDER_RAGDOLL_JOINTLIMITS = 16,
RENDER_HITDETECTION_COLLIDERS = 17,
RENDER_USE_GRADIENTBACKGROUND = 18,
RENDER_MOTIONEXTRACTION = 19,
RENDER_CLOTH_COLLIDERS = 20,
RENDER_SIMULATEDOBJECT_COLLIDERS = 21,
RENDER_SIMULATEJOINTS = 22,
RENDER_EMFX_DEBUG = 23,
NUM_RENDERFLAGS = 24
};
using ActorRenderFlagBitset = AZStd::bitset<ActorRenderFlag::NUM_RENDERFLAGS>;
}
@@ -11,6 +11,7 @@
#include <Integration/Assets/ActorAsset.h>
#include <Integration/Rendering/RenderBackend.h>
#include <Integration/Rendering/RenderActor.h>
#include <Integration/Rendering/RenderFlag.h>
#include <Integration/Rendering/RenderActorInstance.h>
#include <Integration/Rendering/RenderBackendManager.h>
#include <Integration/System/SystemCommon.h>
@@ -64,7 +65,7 @@ namespace EMotionFX
}
MOCK_METHOD1(OnTick, void(float));
MOCK_METHOD1(DebugDraw, void(const DebugOptions&));
MOCK_METHOD1(DebugDraw, void(const EMotionFX::ActorRenderFlagBitset&));
MOCK_CONST_METHOD0(IsVisible, bool());
MOCK_METHOD1(SetIsVisible, void(bool));
MOCK_METHOD1(SetMaterials, void(const ActorAsset::MaterialList&));
@@ -9,43 +9,40 @@
#include "BundlingSystemComponent.h"
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/std/string/string_view.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/Asset/AssetBundleManifest.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <ISystem.h>
#include <IConsole.h>
#include <AzFramework/Archive/IArchive.h>
namespace LmbrCentral
{
const char bundleRoot[] = "@products@";
// Calls the LoadBundles method
static void ConsoleCommandLoadBundles(const AZ::ConsoleCommandContainer& commandArgs);
// Calls the UnloadBundles method
static void ConsoleCommandUnloadBundles(const AZ::ConsoleCommandContainer& commandArgs);
AZ_CONSOLEFREEFUNC("loadbundles", ConsoleCommandLoadBundles, AZ::ConsoleFunctorFlags::Null, "Load Asset Bundles");
AZ_CONSOLEFREEFUNC("unloadbundles", ConsoleCommandUnloadBundles, AZ::ConsoleFunctorFlags::Null, "Unload Asset Bundles");
void BundlingSystemComponent::Activate()
{
BundlingSystemRequestBus::Handler::BusConnect();
CrySystemEventBus::Handler::BusConnect();
AZ::IO::ArchiveNotificationBus::Handler::BusConnect();
}
void BundlingSystemComponent::Deactivate()
{
AZ::IO::ArchiveNotificationBus::Handler::BusDisconnect();
CrySystemEventBus::Handler::BusDisconnect();
BundlingSystemRequestBus::Handler::BusDisconnect();
}
void BundlingSystemComponent::OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams)
{
AZ_UNUSED(systemInitParams);
system.GetIConsole()->AddCommand("loadbundles", ConsoleCommandLoadBundles);
system.GetIConsole()->AddCommand("unloadbundles", ConsoleCommandUnloadBundles);
}
void BundlingSystemComponent::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
@@ -58,7 +55,7 @@ namespace LmbrCentral
AZStd::vector<AZStd::string> BundlingSystemComponent::GetBundleList(const char* bundlePath, const char* bundleExtension) const
{
AZStd::string fileFilter{ AZStd::string::format("*%s",bundleExtension) };
AZStd::string fileFilter{ AZStd::string::format("*%s", bundleExtension) };
AZStd::vector<AZStd::string> bundleList;
AZ::IO::FileIOBase::GetInstance()->FindFiles(bundlePath, fileFilter.c_str(), [&bundleList](const char* foundPath) -> bool
@@ -73,29 +70,28 @@ namespace LmbrCentral
return bundleList;
}
void BundlingSystemComponent::ConsoleCommandLoadBundles(IConsoleCmdArgs* pCmdArgs)
void ConsoleCommandLoadBundles(const AZ::ConsoleCommandContainer& commandArgs)
{
const char defaultBundleFolder[] = "bundles";
const char defaultBundleExtension[] = ".pak";
const char* bundleFolder = pCmdArgs->GetArgCount() > 1 ? pCmdArgs->GetArg(1) : defaultBundleFolder;
const char* bundleExtension = pCmdArgs->GetArgCount() > 2 ? pCmdArgs->GetArg(2) : defaultBundleExtension;
AZ::CVarFixedString bundleFolder = commandArgs.size() > 0 ? AZ::CVarFixedString(commandArgs[0]) : defaultBundleFolder;
AZ::CVarFixedString bundleExtension = commandArgs.size() > 1 ? AZ::CVarFixedString(commandArgs[1]) : defaultBundleExtension;
BundlingSystemRequestBus::Broadcast(&BundlingSystemRequestBus::Events::LoadBundles, bundleFolder, bundleExtension);
BundlingSystemRequestBus::Broadcast(&BundlingSystemRequestBus::Events::LoadBundles, bundleFolder.c_str(), bundleExtension.c_str());
}
void BundlingSystemComponent::ConsoleCommandUnloadBundles(IConsoleCmdArgs* pCmdArgs)
void ConsoleCommandUnloadBundles([[maybe_unused]] const AZ::ConsoleCommandContainer& commandArgs)
{
AZ_UNUSED(pCmdArgs);
BundlingSystemRequestBus::Broadcast(&BundlingSystemRequestBus::Events::UnloadBundles);
}
void BundlingSystemComponent::UnloadBundles()
{
ISystem* crySystem{ GetISystem() };
if (!crySystem)
auto archive = AZ::Interface<AZ::IO::IArchive>::Get();
if (!archive)
{
AZ_Error("BundlingSystem", false, "Couldn't Get ISystem to unload bundles!");
AZ_Error("BundlingSystem", false, "Couldn't Get IArchive to load bundles!");
return;
}
if (!m_bundleModeBundles.size())
@@ -106,7 +102,7 @@ namespace LmbrCentral
AZStd::lock_guard<AZStd::mutex> openBundleLock(m_bundleModeMutex);
for (const auto& thisBundle : m_bundleModeBundles)
{
if (crySystem->GetIPak()->ClosePack(thisBundle.c_str()))
if (archive->ClosePack(thisBundle.c_str()))
{
AZ_TracePrintf("BundlingSystem", "Unloaded %s\n",thisBundle.c_str());
}
@@ -128,15 +124,8 @@ namespace LmbrCentral
return;
}
ISystem* crySystem{ GetISystem() };
if (!crySystem)
{
AZ_Error("BundlingSystem", false, "Couldn't Get ISystem to load bundles!");
return;
}
auto cryPak = crySystem->GetIPak();
if (!cryPak)
auto archive = AZ::Interface<AZ::IO::IArchive>::Get();
if (!archive)
{
AZ_Error("BundlingSystem", false, "Couldn't Get IArchive to load bundles!");
return;
@@ -152,8 +141,8 @@ namespace LmbrCentral
}
}
AZStd::string bundlePath;
AzFramework::StringFunc::Path::Join(bundleRoot, thisBundle.c_str(), bundlePath);
if (cryPak->OpenPack(bundleRoot, thisBundle.c_str()))
AZ::StringFunc::Path::Join(bundleRoot, thisBundle.c_str(), bundlePath);
if (archive->OpenPack(bundleRoot, thisBundle.c_str()))
{
AZ_TracePrintf("BundlingSystem", "Loaded bundle %s\n",bundlePath.c_str());
m_bundleModeBundles.emplace_back(AZStd::move(bundlePath));
@@ -230,28 +219,21 @@ namespace LmbrCentral
void BundlingSystemComponent::OpenDependentBundles(const char* bundleName, AZStd::shared_ptr<AzFramework::AssetBundleManifest> bundleManifest)
{
ISystem* crySystem{ GetISystem() };
if (!crySystem)
{
AZ_Error("BundlingSystem", false, "Couldn't Get ISystem to load dependent bundles for %s", bundleName);
return;
}
auto cryPak{ crySystem->GetIPak() };
if (!cryPak)
auto archive = AZ::Interface<AZ::IO::IArchive>::Get();
if (!archive)
{
AZ_Error("BundlingSystem", false, "Couldn't Get IArchive to load dependent bundles for %s", bundleName);
return;
}
AZStd::string folderPath;
AzFramework::StringFunc::Path::GetFolderPath(bundleName, folderPath);
AZ::StringFunc::Path::GetFolderPath(bundleName, folderPath);
for (const auto& thisBundle : bundleManifest->GetDependentBundleNames())
{
AZStd::string bundlePath;
AzFramework::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath);
AZ::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath);
if (!cryPak->OpenPack(bundleRoot, bundlePath.c_str()))
if (!archive->OpenPack(bundleRoot, bundlePath.c_str()))
{
// We're not bailing here intentionally - try to open the remaining bundles
AZ_Warning("BundlingSystem", false, "Failed to open dependent bundle %s of bundle %s", bundlePath.c_str(), bundleName);
@@ -300,28 +282,21 @@ namespace LmbrCentral
void BundlingSystemComponent::CloseDependentBundles(const char* bundleName, AZStd::shared_ptr<AzFramework::AssetBundleManifest> bundleManifest)
{
ISystem* crySystem{ GetISystem() };
if (!crySystem)
{
AZ_Error("BundlingSystem", false, "Couldn't get ISystem to close dependent bundles for %s", bundleName);
return;
}
auto cryPak{ crySystem->GetIPak() };
if (!cryPak)
auto archive = AZ::Interface<AZ::IO::IArchive>::Get();
if (!archive)
{
AZ_Error("BundlingSystem", false, "Couldn't get IArchive to close dependent bundles for %s", bundleName);
return;
}
AZStd::string folderPath;
AzFramework::StringFunc::Path::GetFolderPath(bundleName, folderPath);
AZ::StringFunc::Path::GetFolderPath(bundleName, folderPath);
for (const auto& thisBundle : bundleManifest->GetDependentBundleNames())
{
AZStd::string bundlePath;
AzFramework::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath);
AZ::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath);
if (!cryPak->ClosePack(bundlePath.c_str()))
if (!archive->ClosePack(bundlePath.c_str()))
{
// We're not bailing here intentionally - try to close the remaining bundles
AZ_Warning("BundlingSystem", false, "Failed to close dependent bundle %s of bundle %s", bundlePath.c_str(), bundleName);
@@ -19,11 +19,8 @@
#include <LmbrCentral/Bundling/BundlingSystemComponentBus.h>
#include <CrySystemBus.h>
#include <AzFramework/Archive/ArchiveBus.h>
struct IConsoleCmdArgs;
namespace AzFramework
{
class AssetBundleManifest;
@@ -42,10 +39,9 @@ namespace LmbrCentral
* System component for managing bundles
*/
class BundlingSystemComponent
: public AZ::Component,
public BundlingSystemRequestBus::Handler,
public CrySystemEventBus::Handler,
public AZ::IO::ArchiveNotificationBus::Handler
: public AZ::Component
, public BundlingSystemRequestBus::Handler
, public AZ::IO::ArchiveNotificationBus::Handler
{
public:
AZ_COMPONENT(BundlingSystemComponent, "{0FB7153D-EE80-4B1C-9584-134270401AAF}");
@@ -70,13 +66,6 @@ namespace LmbrCentral
void BundleOpened(const char* bundleName, AZStd::shared_ptr<AzFramework::AssetBundleManifest> bundleManifest, const char* nextBundle, AZStd::shared_ptr<AzFramework::AssetRegistry> bundleCatalog) override;
void BundleClosed(const char* bundleName) override;
// CrySystemEventBus
void OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) override;
// Calls the LoadBundles method
static void ConsoleCommandLoadBundles(IConsoleCmdArgs* pCmdArgs);
// Calls the UnloadBundles method
static void ConsoleCommandUnloadBundles(IConsoleCmdArgs* pCmdArgs);
AZStd::vector<AZStd::string> GetBundleList(const char* bundlePath, const char* bundleExtension) const;
+1 -42
View File
@@ -84,8 +84,6 @@
namespace LmbrCentral
{
static const char* s_assetCatalogFilename = "assetcatalog.xml";
using LmbrCentralAllocatorScope = AZ::AllocatorScope<AZ::LegacyAllocator>;
// This component boots the required allocators for LmbrCentral everywhere but AssetBuilders
@@ -354,8 +352,7 @@ namespace LmbrCentral
AZ_Assert(AZ::Data::AssetManager::IsReady(), "Asset manager isn't ready!");
// Add asset types and extensions to AssetCatalog. Uses "AssetCatalogService".
auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler();
if (assetCatalog)
if (auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler(); assetCatalog)
{
assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo<AZ::ScriptAsset>::Uuid());
assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo<MaterialAsset>::Uuid());
@@ -376,7 +373,6 @@ namespace LmbrCentral
assetCatalog->AddExtension("cax");
}
CrySystemEventBus::Handler::BusConnect();
AZ::Data::AssetManagerNotificationBus::Handler::BusConnect();
@@ -448,7 +444,6 @@ namespace LmbrCentral
m_unhandledAssetInfo.clear();
AZ::Data::AssetManagerNotificationBus::Handler::BusDisconnect();
CrySystemEventBus::Handler::BusDisconnect();
// AssetHandler's destructor calls Unregister()
m_assetHandlers.clear();
@@ -459,42 +454,6 @@ namespace LmbrCentral
}
m_allocatorShutdowns.clear();
}
void LmbrCentralSystemComponent::OnCrySystemPreInitialize([[maybe_unused]] ISystem& system, [[maybe_unused]] const SSystemInitParams& systemInitParams)
{
EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, StartMonitoringAssets);
}
void LmbrCentralSystemComponent::OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams)
{
#if !defined(AZ_MONOLITHIC_BUILD)
// When module is linked dynamically, we must set our gEnv pointer.
// When module is linked statically, we'll share the application's gEnv pointer.
gEnv = system.GetGlobalEnvironment();
#endif
// Enable catalog now that application's asset root is set.
if (system.GetGlobalEnvironment()->IsEditor())
{
// In the editor, we build the catalog by scanning the disk.
if (systemInitParams.pUserCallback)
{
systemInitParams.pUserCallback->OnInitProgress("Refreshing asset catalog...");
}
}
// load the catalog from disk (supported over VFS).
EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, LoadCatalog, AZStd::string::format("@products@/%s", s_assetCatalogFilename).c_str());
}
void LmbrCentralSystemComponent::OnCrySystemShutdown([[maybe_unused]] ISystem& system)
{
EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, StopMonitoringAssets);
#if !defined(AZ_MONOLITHIC_BUILD)
gEnv = nullptr;
#endif
}
} // namespace LmbrCentral
#if !defined(LMBR_CENTRAL_EDITOR)
@@ -15,8 +15,6 @@
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/functional.h>
#include <CrySystemBus.h>
/*!
* \namespace LmbrCentral
* LmbrCentral ties together systems from CryEngine and systems from the AZ framework.
@@ -49,7 +47,6 @@ namespace LmbrCentral
*/
class LmbrCentralSystemComponent
: public AZ::Component
, private CrySystemEventBus::Handler
, private AZ::Data::AssetManagerNotificationBus::Handler
{
public:
@@ -71,13 +68,6 @@ namespace LmbrCentral
void Deactivate() override;
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// CrySystemEvents
void OnCrySystemPreInitialize(ISystem& system, const SSystemInitParams& systemInitParams) override;
void OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) override;
void OnCrySystemShutdown(ISystem& system) override;
////////////////////////////////////////////////////////////////////////////
AZStd::vector<AZStd::unique_ptr<AZ::Data::AssetHandler> > m_assetHandlers;
AZStd::vector<AZStd::unique_ptr<AZ::AssetTypeInfoBus::Handler> > m_unhandledAssetInfo;
AZStd::vector<AZStd::function<void()>> m_allocatorShutdowns;
@@ -0,0 +1,31 @@
// The Lifecycle events contains the name of the event as a string
// ComponentApplication derived classes
// will set these these keys to a JSON Object indicate an event has occured
// A callback can be registered with the SettingsRegistry
// to be notified when that key is set
// The JSON object that is set will contain any payload data
// related to the event
{
"O3DE" : {
"Runtime": {
"Application": {
"LifecycleEvents": {
"SystemComponentsActivated": {},
"SystemComponentsDeactivated": {},
"ReflectionManagerAvailable": {},
"ReflectionManagerUnavailable": {},
"SystemAllocatorCreated": {},
"SystemAllocatorPendingDestruction": {},
"SettingsRegistryAvailable": {},
"SettingsRegistryUnavailable": {},
"ConsoleAvailable": {},
"ConsoleUnavailable": {},
"GemsLoaded": {},
"GemsUnloaded": {},
"FileIOAvailable": {},
"FileIOUnavailable": {}
}
}
}
}
}
+2
View File
@@ -9,6 +9,8 @@
ly_install_directory(
DIRECTORIES
AssetGem
CustomTool
PythonGem
DefaultGem
DefaultProject
MinimalProject
@@ -0,0 +1,14 @@
# {BEGIN_LICENSE}
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# {END_LICENSE}
set(o3de_gem_path ${CMAKE_CURRENT_LIST_DIR})
set(o3de_gem_json ${o3de_gem_path}/gem.json)
o3de_read_json_key(o3de_gem_name ${o3de_gem_json} "gem_name")
o3de_restricted_path(${o3de_gem_json} o3de_gem_restricted_path)
add_subdirectory(Code)
@@ -0,0 +1,14 @@
# {BEGIN_LICENSE}
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# {END_LICENSE}
set(FILES
Include/${Name}/${Name}Bus.h
Source/${Name}ModuleInterface.h
Source/${Name}EditorSystemComponent.cpp
Source/${Name}EditorSystemComponent.h
)
@@ -0,0 +1,11 @@
# {BEGIN_LICENSE}
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# {END_LICENSE}
set(FILES
Source/${Name}EditorModule.cpp
)
@@ -0,0 +1,11 @@
# {BEGIN_LICENSE}
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# {END_LICENSE}
set(FILES
Tests/${Name}EditorTest.cpp
)
@@ -0,0 +1,76 @@
# {BEGIN_LICENSE}
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# {END_LICENSE}
# Currently we are in the Code folder: ${CMAKE_CURRENT_LIST_DIR}
# Get the platform specific folder ${pal_dir} for the current folder: ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME}
# Note: ly_get_list_relative_pal_filename will take care of the details for us, as this may be a restricted platform
# in which case it will see if that platform is present here or in the restricted folder.
# i.e. It could here in our gem : Gems/${Name}/Code/Platform/<platorm_name> or
# <restricted_folder>/<platform_name>/Gems/${Name}/Code
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${o3de_gem_restricted_path} ${o3de_gem_path} ${o3de_gem_name})
# Now that we have the platform abstraction layer (PAL) folder for this folder, thats where we will find the
# traits for this platform. Traits for a platform are defines for things like whether or not something in this gem
# is supported by this platform.
include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
# If we are on a host platform, we want to add the host tools targets like the ${Name}.Editor target which
# will also depend on ${Name}.Static
if(PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME ${Name}.Editor.Static STATIC
NAMESPACE Gem
FILES_CMAKE
${NameLower}_editor_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
PUBLIC
AZ::AzToolsFramework
)
ly_add_target(
NAME ${Name}.Editor GEM_MODULE
NAMESPACE Gem
AUTOMOC
FILES_CMAKE
${NameLower}_editor_shared_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
PUBLIC
Gem::${Name}.Editor.Static
)
# By default, we will specify that the above target ${Name} would be used by
# Tool and Builder type targets when this gem is enabled. If you don't want it
# active in Tools or Builders by default, delete one of both of the following lines:
ly_create_alias(NAME ${Name}.Tools NAMESPACE Gem TARGETS Gem::${Name}.Editor)
ly_create_alias(NAME ${Name}.Builders NAMESPACE Gem TARGETS Gem::${Name}.Editor)
endif()
################################################################################
# Tests
################################################################################
# See if globally, tests are supported
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
# We globally support tests, see if we support tests on this platform for ${Name}.Static
# If we are a host platform we want to add tools test like editor tests here
if(PAL_TRAIT_BUILD_HOST_TOOLS)
endif()
endif()
@@ -0,0 +1,40 @@
// {BEGIN_LICENSE}
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// {END_LICENSE}
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Interface/Interface.h>
namespace ${SanitizedCppName}
{
class ${SanitizedCppName}Requests
{
public:
AZ_RTTI(${SanitizedCppName}Requests, "{${Random_Uuid}}");
virtual ~${SanitizedCppName}Requests() = default;
// Put your public methods here
};
class ${SanitizedCppName}BusTraits
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
};
using ${SanitizedCppName}RequestBus = AZ::EBus<${SanitizedCppName}Requests, ${SanitizedCppName}BusTraits>;
using ${SanitizedCppName}Interface = AZ::Interface<${SanitizedCppName}Requests>;
} // namespace ${SanitizedCppName}
@@ -0,0 +1,15 @@
# {BEGIN_LICENSE}
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# {END_LICENSE}
# Platform specific files for Linux
# i.e. ../Source/Linux/${Name}Linux.cpp
# ../Source/Linux/${Name}Linux.h
# ../Include/Linux/${Name}Linux.h
set(FILES
)
@@ -0,0 +1,15 @@
# {BEGIN_LICENSE}
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# {END_LICENSE}
# Platform specific files for Linux
# i.e. ../Source/Linux/${Name}Linux.cpp
# ../Source/Linux/${Name}Linux.h
# ../Include/Linux/${Name}Linux.h
set(FILES
)
@@ -0,0 +1,11 @@
# {BEGIN_LICENSE}
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# {END_LICENSE}
set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE)
set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE)
set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE)
@@ -0,0 +1,15 @@
# {BEGIN_LICENSE}
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# {END_LICENSE}
# Platform specific files for Mac
# i.e. ../Source/Mac/${Name}Mac.cpp
# ../Source/Mac/${Name}Mac.h
# ../Include/Mac/${Name}Mac.h
set(FILES
)
@@ -0,0 +1,15 @@
# {BEGIN_LICENSE}
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# {END_LICENSE}
# Platform specific files for Mac
# i.e. ../Source/Mac/${Name}Mac.cpp
# ../Source/Mac/${Name}Mac.h
# ../Include/Mac/${Name}Mac.h
set(FILES
)
@@ -0,0 +1,11 @@
# {BEGIN_LICENSE}
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# {END_LICENSE}
set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE)
set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE)
set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE)
@@ -0,0 +1,15 @@
# {BEGIN_LICENSE}
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# {END_LICENSE}
# Platform specific files for Windows
# i.e. ../Source/Windows/${Name}Windows.cpp
# ../Source/Windows/${Name}Windows.h
# ../Include/Windows/${Name}Windows.h
set(FILES
)
@@ -0,0 +1,15 @@
# {BEGIN_LICENSE}
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# {END_LICENSE}
# Platform specific files for Windows
# i.e. ../Source/Windows/${Name}Windows.cpp
# ../Source/Windows/${Name}Windows.h
# ../Include/Windows/${Name}Windows.h
set(FILES
)
@@ -0,0 +1,11 @@
# {BEGIN_LICENSE}
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
# {END_LICENSE}
set(PAL_TRAIT_${NameUpper}_SUPPORTED TRUE)
set(PAL_TRAIT_${NameUpper}_TEST_SUPPORTED TRUE)
set(PAL_TRAIT_${NameUpper}_EDITOR_TEST_SUPPORTED TRUE)
@@ -0,0 +1,47 @@
// {BEGIN_LICENSE}
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// {END_LICENSE}
#include <${Name}ModuleInterface.h>
#include <${Name}EditorSystemComponent.h>
namespace ${SanitizedCppName}
{
class ${SanitizedCppName}EditorModule
: public ${SanitizedCppName}ModuleInterface
{
public:
AZ_RTTI(${SanitizedCppName}EditorModule, "${ModuleClassId}", ${SanitizedCppName}ModuleInterface);
AZ_CLASS_ALLOCATOR(${SanitizedCppName}EditorModule, AZ::SystemAllocator, 0);
${SanitizedCppName}EditorModule()
{
// Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
// Add ALL components descriptors associated with this gem to m_descriptors.
// This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext.
// This happens through the [MyComponent]::Reflect() function.
m_descriptors.insert(m_descriptors.end(), {
${SanitizedCppName}EditorSystemComponent::CreateDescriptor(),
});
}
/**
* Add required SystemComponents to the SystemEntity.
* Non-SystemComponents should not be added here
*/
AZ::ComponentTypeList GetRequiredSystemComponents() const override
{
return AZ::ComponentTypeList {
azrtti_typeid<${SanitizedCppName}EditorSystemComponent>(),
};
}
};
}// namespace ${SanitizedCppName}
AZ_DECLARE_MODULE_CLASS(Gem_${SanitizedCppName}, ${SanitizedCppName}::${SanitizedCppName}EditorModule)
@@ -0,0 +1,70 @@
// {BEGIN_LICENSE}
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// {END_LICENSE}
#include <AzCore/Serialization/SerializeContext.h>
#include <${Name}EditorSystemComponent.h>
namespace ${SanitizedCppName}
{
void ${SanitizedCppName}EditorSystemComponent::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<${SanitizedCppName}EditorSystemComponent, AZ::Component>();
}
}
${SanitizedCppName}EditorSystemComponent::${SanitizedCppName}EditorSystemComponent()
{
if (${SanitizedCppName}Interface::Get() == nullptr)
{
${SanitizedCppName}Interface::Register(this);
}
}
${SanitizedCppName}EditorSystemComponent::~${SanitizedCppName}EditorSystemComponent()
{
if (${SanitizedCppName}Interface::Get() == this)
{
${SanitizedCppName}Interface::Unregister(this);
}
}
void ${SanitizedCppName}EditorSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService"));
}
void ${SanitizedCppName}EditorSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService"));
}
void ${SanitizedCppName}EditorSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
{
}
void ${SanitizedCppName}EditorSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
}
void ${SanitizedCppName}EditorSystemComponent::Activate()
{
${SanitizedCppName}RequestBus::Handler::BusConnect();
AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
}
void ${SanitizedCppName}EditorSystemComponent::Deactivate()
{
AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
${SanitizedCppName}RequestBus::Handler::BusDisconnect();
}
} // namespace ${SanitizedCppName}
@@ -0,0 +1,42 @@
// {BEGIN_LICENSE}
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// {END_LICENSE}
#pragma once
#include <AzCore/Component/Component.h>
#include <${Name}/${Name}Bus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
namespace ${SanitizedCppName}
{
/// System component for ${SanitizedCppName} editor
class ${SanitizedCppName}EditorSystemComponent
: public ${SanitizedCppName}RequestBus::Handler
, private AzToolsFramework::EditorEvents::Bus::Handler
, public AZ::Component
{
public:
AZ_COMPONENT(${SanitizedCppName}EditorSystemComponent, "${EditorSysCompClassId}");
static void Reflect(AZ::ReflectContext* context);
${SanitizedCppName}EditorSystemComponent();
~${SanitizedCppName}EditorSystemComponent();
private:
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
// AZ::Component
void Activate();
void Deactivate();
};
} // namespace ${SanitizedCppName}
@@ -0,0 +1,36 @@
// {BEGIN_LICENSE}
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// {END_LICENSE}
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Module/Module.h>
namespace ${SanitizedCppName}
{
class ${SanitizedCppName}ModuleInterface
: public AZ::Module
{
public:
AZ_RTTI(${SanitizedCppName}ModuleInterface, "{${Random_Uuid}}", AZ::Module);
AZ_CLASS_ALLOCATOR(${SanitizedCppName}ModuleInterface, AZ::SystemAllocator, 0);
${SanitizedCppName}ModuleInterface()
{
}
/**
* Add required SystemComponents to the SystemEntity.
*/
AZ::ComponentTypeList GetRequiredSystemComponents() const override
{
return AZ::ComponentTypeList{
};
}
};
}// namespace ${SanitizedCppName}
@@ -0,0 +1,13 @@
// {BEGIN_LICENSE}
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// {END_LICENSE}
#include <AzTest/AzTest.h>
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);
@@ -0,0 +1,46 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
# -------------------------------------------------------------------------
"""${SanitizedCppName}\\editor\\scripts\\${SanitizedCppName}_dialog.py
Generated from O3DE PythonGem Template"""
import azlmbr
from shiboken2 import wrapInstance, getCppPointer
from PySide2 import QtCore, QtWidgets, QtGui
from PySide2.QtCore import QEvent, Qt
from PySide2.QtWidgets import QVBoxLayout, QAction, QDialog, QHeaderView, QLabel, QLineEdit, QPushButton, QSplitter, QTreeWidget, QTreeWidgetItem, QWidget, QAbstractButton
# Once PySide2 has been bootstrapped, register our ${SanitizedCppName}Dialog with the Editor
class ${SanitizedCppName}Dialog(QDialog):
def __init__(self, parent=None):
super(${SanitizedCppName}Dialog, self).__init__(parent)
self.setObjectName("${SanitizedCppName}Dialog")
self.setWindowTitle("HelloWorld, ${SanitizedCppName} Dialog")
self.mainLayout = QVBoxLayout(self)
self.introLabel = QLabel("Put your cool stuff here!")
self.mainLayout.addWidget(self.introLabel, 0, Qt.AlignCenter)
self.helpText = str("For help getting started,"
"visit the <a href=\"https://o3de.org/docs/tools-ui/ui-dev-intro/\">UI Development</a> documentation<br/>"
"or come ask a question in the <a href=\"https://discord.gg/R77Wss3kHe\">sig-ui-ux channel</a> on Discord")
self.helpLabel = QLabel()
self.helpLabel.setTextFormat(Qt.RichText)
self.helpLabel.setText(self.helpText)
self.helpLabel.setOpenExternalLinks(True)
self.mainLayout.addWidget(self.helpLabel, 0, Qt.AlignCenter)
self.setLayout(self.mainLayout)
return
@@ -0,0 +1,9 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
# -------------------------------------------------------------------------
__ALL__ = ['bootstrap','${NameLower}_dialog']
@@ -0,0 +1,117 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
# -------------------------------------------------------------------------
"""${SanitizedCppName}\\editor\\scripts\\boostrap.py
Generated from O3DE PythonGem Template"""
import azlmbr
import az_qt_helpers
from PySide2 import QtCore, QtWidgets, QtGui
from PySide2.QtCore import QEvent, Qt
from PySide2.QtWidgets import QMainWindow, QAction, QDialog, QHeaderView, QLabel, QLineEdit, QPushButton, QSplitter, QTreeWidget, QTreeWidgetItem, QWidget, QAbstractButton
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
class SampleUI(QtWidgets.QDialog):
"""Lightweight UI Test Class created a button"""
def __init__(self, parent, title='Not Set'):
super(SampleUI, self).__init__(parent)
self.setWindowTitle(title)
self.initUI()
def initUI(self):
mainLayout = QtWidgets.QHBoxLayout()
testBtn = QtWidgets.QPushButton("I am just a Button man!")
mainLayout.addWidget(testBtn)
self.setLayout(mainLayout)
# -------------------------------------------------------------------------
if __name__ == "__main__":
print("${SanitizedCppName}.boostrap, Generated from O3DE PythonGem Template")
# ---------------------------------------------------------------------
# validate pyside before continuing
try:
azlmbr.qt.QtForPythonRequestBus(azlmbr.bus.Broadcast, 'IsActive')
params = azlmbr.qt.QtForPythonRequestBus(azlmbr.bus.Broadcast, 'GetQtBootstrapParameters')
params is not None and params.mainWindowId is not 0
from PySide2 import QtWidgets
except Exception as e:
_LOGGER.error(f'Pyside not available, exception: {e}')
raise e
# keep going, import the other PySide2 bits we will use
from PySide2 import QtGui
from PySide2.QtCore import Slot
from shiboken2 import wrapInstance, getCppPointer
# Get our Editor main window
_widget_main_window = None
try:
_widget_main_window = az_qt_helpers.get_editor_main_window()
except:
pass # may be booting in the AP?
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
if _widget_main_window:
# creat a custom menu
_tag_str = '${SanitizedCppName}'
# create our own menuBar
${SanitizedCppName}_menu = _widget_main_window.menuBar().addMenu(f"&{_tag_str}")
# nest a menu for util/tool launching
${SanitizedCppName}_launch_menu = ${SanitizedCppName}_menu.addMenu("examples")
else:
print('No O3DE MainWindow')
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
if _widget_main_window:
# (1) add the first SampleUI
action_launch_sample_ui = ${SanitizedCppName}_launch_menu.addAction("O3DE:SampleUI")
@Slot()
def clicked_sample_ui():
while 1: # simple PySide2 test, set to 0 to disable
ui = SampleUI(parent=_widget_main_window, title='O3DE:SampleUI')
ui.show()
break
return
# Add click event to menu bar
action_launch_sample_ui.triggered.connect(clicked_sample_ui)
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
if _widget_main_window:
# (1) and custom external module Qwidget
action_launch_${SanitizedCppName}_dialog = ${SanitizedCppName}_launch_menu.addAction("O3DE:${SanitizedCppName}_dialog")
@Slot()
def clicked_${SanitizedCppName}_dialog():
while 1: # simple PySide2 test, set to 0 to disable
try:
import az_qt_helpers
from ${NameLower}_dialog import ${SanitizedCppName}Dialog
az_qt_helpers.register_view_pane('${SanitizedCppName} Popup', ${SanitizedCppName}Dialog)
except Exception as e:
print(f'Error: {e}')
print('Skipping register our ${SanitizedCppName}Dialog with the Editor.')
${SanitizedCppName}_dialog = ${SanitizedCppName}Dialog(parent=_widget_main_window)
${SanitizedCppName}_dialog.show()
break
return
# Add click event to menu bar
action_launch_${SanitizedCppName}_dialog.triggered.connect(clicked_${SanitizedCppName}_dialog)
# ---------------------------------------------------------------------
# end
+16
View File
@@ -0,0 +1,16 @@
{
"gem_name": "${Name}",
"display_name": "${Name}",
"license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT",
"origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com",
"type": "Code",
"summary": "A short description of ${Name}.",
"canonical_tags": [
"Gem"
],
"user_tags": [
"${Name}"
],
"icon_path": "preview.png",
"requirements": ""
}
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7ac9dd09bde78f389e3725ac49d61eff109857e004840bc0bc3881739df9618d
size 2217
+216
View File
@@ -0,0 +1,216 @@
{
"template_name": "PythonGem",
"restricted_name": "o3de",
"restricted_platform_relative_path": "Templates",
"origin": "The primary repo for PythonGem goes here: i.e. http://www.mydomain.com",
"license": "What license PythonGem uses goes here: i.e. https://opensource.org/licenses/MIT",
"display_name": "PythonGem",
"summary": "A short description of PythonGem.",
"canonical_tags": [],
"user_tags": [
"PythonGem"
],
"icon_path": "preview.png",
"copyFiles": [
{
"file": "CMakeLists.txt",
"origin": "CMakeLists.txt",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/${NameLower}_editor_files.cmake",
"origin": "Code/${NameLower}_editor_files.cmake",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/${NameLower}_editor_shared_files.cmake",
"origin": "Code/${NameLower}_editor_shared_files.cmake",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/${NameLower}_editor_tests_files.cmake",
"origin": "Code/${NameLower}_editor_tests_files.cmake",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/CMakeLists.txt",
"origin": "Code/CMakeLists.txt",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/Include/${Name}/${Name}Bus.h",
"origin": "Code/Include/${Name}/${Name}Bus.h",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/Platform/Linux/${NameLower}_linux_files.cmake",
"origin": "Code/Platform/Linux/${NameLower}_linux_files.cmake",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake",
"origin": "Code/Platform/Linux/${NameLower}_shared_linux_files.cmake",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/Platform/Linux/PAL_linux.cmake",
"origin": "Code/Platform/Linux/PAL_linux.cmake",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/Platform/Mac/${NameLower}_mac_files.cmake",
"origin": "Code/Platform/Mac/${NameLower}_mac_files.cmake",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake",
"origin": "Code/Platform/Mac/${NameLower}_shared_mac_files.cmake",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/Platform/Mac/PAL_mac.cmake",
"origin": "Code/Platform/Mac/PAL_mac.cmake",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake",
"origin": "Code/Platform/Windows/${NameLower}_shared_windows_files.cmake",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/Platform/Windows/${NameLower}_windows_files.cmake",
"origin": "Code/Platform/Windows/${NameLower}_windows_files.cmake",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/Platform/Windows/PAL_windows.cmake",
"origin": "Code/Platform/Windows/PAL_windows.cmake",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/Source/${Name}EditorModule.cpp",
"origin": "Code/Source/${Name}EditorModule.cpp",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/Source/${Name}EditorSystemComponent.cpp",
"origin": "Code/Source/${Name}EditorSystemComponent.cpp",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/Source/${Name}EditorSystemComponent.h",
"origin": "Code/Source/${Name}EditorSystemComponent.h",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/Source/${Name}ModuleInterface.h",
"origin": "Code/Source/${Name}ModuleInterface.h",
"isTemplated": true,
"isOptional": false
},
{
"file": "Code/Tests/${Name}EditorTest.cpp",
"origin": "Code/Tests/${Name}EditorTest.cpp",
"isTemplated": true,
"isOptional": false
},
{
"file": "Editor/Scripts/__init__.py",
"origin": "Editor/Scripts/__init__.py",
"isTemplated": true,
"isOptional": false
},
{
"file": "Editor/Scripts/bootstrap.py",
"origin": "Editor/Scripts/bootstrap.py",
"isTemplated": true,
"isOptional": false
},
{
"file": "Editor/Scripts/${NameLower}_dialog.py",
"origin": "Editor/Scripts/${NameLower}_dialog.py",
"isTemplated": true,
"isOptional": false
},
{
"file": "gem.json",
"origin": "gem.json",
"isTemplated": true,
"isOptional": false
},
{
"file": "preview.png",
"origin": "preview.png",
"isTemplated": false,
"isOptional": false
}
],
"createDirectories": [
{
"dir": "Assets",
"origin": "Assets"
},
{
"dir": "Code",
"origin": "Code"
},
{
"dir": "Editor",
"origin": "Editor"
},
{
"dir": "Editor/Scripts",
"origin": "Editor/Scripts"
},
{
"dir": "Code/Include",
"origin": "Code/Include"
},
{
"dir": "Code/Include/${Name}",
"origin": "Code/Include/${Name}"
},
{
"dir": "Code/Platform",
"origin": "Code/Platform"
},
{
"dir": "Code/Platform/Linux",
"origin": "Code/Platform/Linux"
},
{
"dir": "Code/Platform/Mac",
"origin": "Code/Platform/Mac"
},
{
"dir": "Code/Platform/Windows",
"origin": "Code/Platform/Windows"
},
{
"dir": "Code/Source",
"origin": "Code/Source"
},
{
"dir": "Code/Tests",
"origin": "Code/Tests"
}
]
}
+36
View File
@@ -102,6 +102,10 @@ def IsJobEnabled(branchName, buildTypeMap, pipelineName, platformName) {
}
}
def IsAPLogUpload(branchName, jobName) {
return !IsPullRequest(branchName) && jobName.toLowerCase().contains('asset') && env.AP_LOGS_S3_BUCKET
}
def GetRunningPipelineName(JENKINS_JOB_NAME) {
// If the job name has an underscore
def job_parts = JENKINS_JOB_NAME.tokenize('/')[0].tokenize('_')
@@ -431,6 +435,27 @@ def ExportTestScreenshots(Map options, String branchName, String platformName, S
}
}
def UploadAPLogs(Map options, String branchName, String jobName, String workspace, Map params) {
dir("${workspace}/${ENGINE_REPOSITORY_NAME}") {
projects = params.CMAKE_LY_PROJECTS.split(",")
projects.each{ project ->
def apLogsPath = "${project}/user/log"
def s3UploadScriptPath = "scripts/build/tools/upload_to_s3.py"
if(env.IS_UNIX) {
pythonPath = "${options.PYTHON_DIR}/python.sh"
}
else {
pythonPath = "${options.PYTHON_DIR}/python.cmd"
}
def command = "${pythonPath} -u ${s3UploadScriptPath} --base_dir ${apLogsPath} " +
"--file_regex \".*\" --bucket ${env.AP_LOGS_S3_BUCKET} " +
"--search_subdirectories True --key_prefix ${env.JOB_NAME}/${branchName}/${env.BUILD_NUMBER}/${jobName}" +
"--extra-args {\"ACL\": \"bucket-owner-full-control\"}"
palSh(command, "Uploading AP logs for job ${jobName} for branch ${branchName}", false)
}
}
}
def PostBuildCommonSteps(String workspace, boolean mount = true) {
echo 'Starting post-build common steps...'
@@ -494,6 +519,14 @@ def CreateExportTestScreenshotsStage(Map pipelineConfig, String branchName, Stri
}
}
def CreateUploadAPLogsStage(Map pipelineConfig, String branchName, String jobName, String workspace, Map params) {
return {
stage("${jobName}_upload_ap_logs") {
UploadAPLogs(pipelineConfig, branchName, jobName, workspace, params)
}
}
}
def CreateTeardownStage(Map environmentVars) {
return {
stage('Teardown') {
@@ -543,6 +576,9 @@ def CreateSingleNode(Map pipelineConfig, def platform, def build_job, Map envVar
error "Node disconnected during build: ${e}" // Error raised to retry stage on a new node
}
}
if (IsAPLogUpload(branchName, build_job_name)) {
CreateUploadAPLogsStage(pipelineConfig, branchName, build_job_name, envVars['WORKSPACE'], platform.value.build_types[build_job_name].PARAMETERS).call()
}
// All other errors will be raised outside the retry block
currentResult = envVars['ON_FAILURE_MARK'] ?: 'FAILURE'
currentException = e.toString()
@@ -12,7 +12,12 @@ libxcb-xinerama0 # For Qt plugins at runtime
libxcb-xinput0 # For Qt plugins at runtime
libfontconfig1-dev # For Qt plugins at runtime
libcurl4-openssl-dev # For HttpRequestor
libsdl2-dev # for WWise/Audio
libsdl2-dev # For WWise/Audio
libxcb-xkb-dev # For xcb keyboard input
libxkbcommon-x11-dev # For xcb keyboard input
libxkbcommon-dev # For xcb keyboard input
libxcb-xfixes0-dev # For mouse input
libxcb-xinput-dev # For mouse input
zlib1g-dev
mesa-common-dev
@@ -16,5 +16,7 @@ libsdl2-dev # for WWise/Audio
libxcb-xkb-dev # For xcb keyboard input
libxkbcommon-x11-dev # For xcb keyboard input
libxkbcommon-dev # For xcb keyboard input
libxcb-xfixes0-dev # For mouse input
libxcb-xinput-dev # For mouse input
zlib1g-dev
mesa-common-dev