From 5b148b1f40351098cfdabd89448caa33dfb00394 Mon Sep 17 00:00:00 2001 From: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Date: Thu, 22 Jul 2021 17:37:44 -0500 Subject: [PATCH 01/19] Convert legacy XML handling to rapidxml Updates the Audio Controls Editor code to use rapidxml instead of legacy xml apis. Further makes improvements to path manipulations away from strings towards PathView apis and similar. Fixes some issues encountered with memory management when handling xml data that did not occur previously. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- .../Source/Editor/AudioSystemEditor_wwise.cpp | 205 ++++++--- .../Source/Editor/AudioSystemEditor_wwise.h | 4 +- .../Code/Source/Editor/AudioWwiseLoader.cpp | 21 +- .../Code/Include/Editor/ACETypes.h | 4 + .../Code/Include/Editor/IAudioSystemEditor.h | 8 +- .../Code/Include/Engine/ATLCommon.h | 1 + .../Code/Include/Engine/AudioFileUtils.h | 17 +- .../Code/Source/Editor/AudioControl.h | 53 ++- .../Editor/AudioControlsEditorWindow.cpp | 39 +- .../Source/Editor/AudioControlsLoader.cpp | 407 +++++++++--------- .../Code/Source/Editor/AudioControlsLoader.h | 11 +- .../Source/Editor/AudioControlsWriter.cpp | 183 +++++--- .../Code/Source/Editor/AudioControlsWriter.h | 25 +- .../Source/Editor/QAudioControlEditorIcons.h | 4 +- .../Code/Source/Engine/ATLComponents.cpp | 8 +- 15 files changed, 580 insertions(+), 410 deletions(-) diff --git a/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.cpp b/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.cpp index 4dfafa132a..5bad3ecf1c 100644 --- a/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.cpp +++ b/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.cpp @@ -19,10 +19,7 @@ #include #include -#include -#include -#include -#include +#include void InitWwiseResources() { @@ -217,28 +214,34 @@ namespace AudioControls } //-------------------------------------------------------------------------------------------// - TConnectionPtr CAudioSystemEditor_wwise::CreateConnectionFromXMLNode(XmlNodeRef node, EACEControlType atlControlType) + TConnectionPtr CAudioSystemEditor_wwise::CreateConnectionFromXMLNode(AZ::rapidxml::xml_node* node, EACEControlType atlControlType) { if (node) { - const AZStd::string tag(node->getTag()); - TImplControlType type = TagToType(tag); + AZStd::string_view element(node->name()); + TImplControlType type = TagToType(element); if (type != AUDIO_IMPL_INVALID_TYPE) { - AZStd::string name(node->getAttr(Audio::WwiseXmlTags::WwiseNameAttribute)); - AZStd::string localized(node->getAttr(Audio::WwiseXmlTags::WwiseLocalizedAttribute)); + AZStd::string name; + AZStd::string_view localized; - // Legacy Preload support - if (localized.empty()) + if (auto nameAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute, 0, false); + nameAttr != nullptr) { - localized = node->getAttr(Audio::WwiseXmlTags::Legacy::WwiseLocalizedAttribute); + name = nameAttr->value(); } - bool isLocalized = AZ::StringFunc::Equal(localized.c_str(), "true"); + if (auto localizedAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseLocalizedAttribute, 0, false); + localizedAttr != nullptr) + { + localized = localizedAttr->value(); + } - // If control not found, create a placeholder. - // We want to keep that connection even if it's not in the middleware. - // The user could be using the engine without the wwise project + bool isLocalized = AZ::StringFunc::Equal(localized, "true"); + + // If the control wasn't found, create a placeholder. + // We want to see that connection even if it's not in the middleware. + // User could be viewing the editor without a middleware project. IAudioSystemControl* control = GetControlByName(name, isLocalized); if (!control) { @@ -250,27 +253,26 @@ namespace AudioControls } } - // If it's a switch we actually connect to one of the states within the switch + // If it's a switch we connect to one of the states within the switch if (type == eWCT_WWISE_SWITCH_GROUP || type == eWCT_WWISE_GAME_STATE_GROUP) { - if (node->getChildCount() == 1) + if (auto childNode = node->first_node(); + childNode != nullptr) { - node = node->getChild(0); - if (node) + AZStd::string childName; + if (auto childNameAttr = childNode->first_attribute(Audio::WwiseXmlTags::WwiseNameAttribute, 0, false); + childNameAttr != nullptr) { - AZStd::string childName(node->getAttr(Audio::WwiseXmlTags::WwiseNameAttribute)); - - IAudioSystemControl* childControl = GetControlByName(childName, false, control); - if (!childControl) - { - childControl = CreateControl(SControlDef(childName, type == eWCT_WWISE_SWITCH_GROUP ? eWCT_WWISE_SWITCH : eWCT_WWISE_GAME_STATE, false, control)); - } - control = childControl; + childName = childNameAttr->value(); } - } - else - { - CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_ERROR, "Audio Controls Editor (Wwise): Error reading connection to Wwise control %s", name.c_str()); + + IAudioSystemControl* childControl = GetControlByName(childName, false, control); + if (!childControl) + { + childControl = CreateControl(SControlDef( + childName, type == eWCT_WWISE_SWITCH_GROUP ? eWCT_WWISE_SWITCH : eWCT_WWISE_GAME_STATE, false, control)); + } + control = childControl; } } @@ -289,16 +291,19 @@ namespace AudioControls float mult = 1.0f; float shift = 0.0f; - if (node->haveAttr(Audio::WwiseXmlTags::WwiseMutiplierAttribute)) + + if (auto multAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseMutiplierAttribute, 0, false); + multAttr != nullptr) { - const AZStd::string multProperty(node->getAttr(Audio::WwiseXmlTags::WwiseMutiplierAttribute)); - mult = AZStd::stof(multProperty); + mult = AZStd::stof(AZStd::string(multAttr->value())); } - if (node->haveAttr(Audio::WwiseXmlTags::WwiseShiftAttribute)) + + if (auto shiftAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseShiftAttribute, 0, false); + shiftAttr != nullptr) { - const AZStd::string shiftProperty(node->getAttr(Audio::WwiseXmlTags::WwiseShiftAttribute)); - shift = AZStd::stof(shiftProperty); + shift = AZStd::stof(AZStd::string(shiftAttr->value())); } + connection->m_mult = mult; connection->m_shift = shift; return connection; @@ -308,11 +313,12 @@ namespace AudioControls TStateConnectionPtr connection = AZStd::make_shared(control->GetId()); float value = 0.0f; - if (node->haveAttr(Audio::WwiseXmlTags::WwiseValueAttribute)) + if (auto valueAttr = node->first_attribute(Audio::WwiseXmlTags::WwiseValueAttribute, 0, false); + valueAttr != nullptr) { - const AZStd::string valueProperty(node->getAttr(Audio::WwiseXmlTags::WwiseValueAttribute)); - value = AZStd::stof(valueProperty); + value = AZStd::stof(AZStd::string(valueAttr->value())); } + connection->m_value = value; return connection; } @@ -329,28 +335,50 @@ namespace AudioControls } //-------------------------------------------------------------------------------------------// - XmlNodeRef CAudioSystemEditor_wwise::CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) + AZ::rapidxml::xml_node* CAudioSystemEditor_wwise::CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) { const IAudioSystemControl* control = GetControl(connection->GetID()); if (control) { + XmlAllocator& xmlAllocator(AudioControls::s_xmlAllocator); + switch (control->GetType()) { case AudioControls::eWCT_WWISE_SWITCH: + [[fallthrough]]; case AudioControls::eWCT_WWISE_SWITCH_GROUP: + [[fallthrough]]; case AudioControls::eWCT_WWISE_GAME_STATE: + [[fallthrough]]; case AudioControls::eWCT_WWISE_GAME_STATE_GROUP: { const IAudioSystemControl* parent = control->GetParent(); if (parent) { - XmlNodeRef switchNode = GetISystem()->CreateXmlNode(TypeToTag(parent->GetType()).data()); - switchNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, parent->GetName().c_str()); + AZStd::string_view parentType = TypeToTag(parent->GetType()); + auto switchNode = xmlAllocator.allocate_node( + AZ::rapidxml::node_element, + xmlAllocator.allocate_string(parentType.data()) + ); - XmlNodeRef stateNode = switchNode->createNode(Audio::WwiseXmlTags::WwiseValueTag); - stateNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str()); - switchNode->addChild(stateNode); + auto switchNameAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseNameAttribute, + xmlAllocator.allocate_string(parent->GetName().c_str()) + ); + auto stateNode = xmlAllocator.allocate_node( + AZ::rapidxml::node_element, + Audio::WwiseXmlTags::WwiseValueTag + ); + + auto stateNameAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseNameAttribute, + xmlAllocator.allocate_string(control->GetName().c_str()) + ); + + switchNode->append_attribute(switchNameAttr); + stateNode->append_attribute(stateNameAttr); + switchNode->append_node(stateNode); return switchNode; } break; @@ -358,51 +386,98 @@ namespace AudioControls case AudioControls::eWCT_WWISE_RTPC: { - XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data()); - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str()); + auto connectionNode = xmlAllocator.allocate_node( + AZ::rapidxml::node_element, + xmlAllocator.allocate_string(TypeToTag(control->GetType()).data()) + ); + + auto nameAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseNameAttribute, + xmlAllocator.allocate_string(control->GetName().c_str()) + ); + + connectionNode->append_attribute(nameAttr); if (atlControlType == eACET_RTPC) { AZStd::shared_ptr rtpcConnection = AZStd::static_pointer_cast(connection); - if (rtpcConnection->m_mult != 1.0f) + if (rtpcConnection->m_mult != 1.f) { - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseMutiplierAttribute, rtpcConnection->m_mult); + auto multAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseMutiplierAttribute, + xmlAllocator.allocate_string(AZStd::to_string(rtpcConnection->m_mult).c_str()) + ); + + connectionNode->append_attribute(multAttr); } - if (rtpcConnection->m_shift != 0.0f) + + if (rtpcConnection->m_shift != 0.f) { - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseShiftAttribute, rtpcConnection->m_shift); + auto shiftAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseShiftAttribute, + xmlAllocator.allocate_string(AZStd::to_string(rtpcConnection->m_shift).c_str()) + ); + + connectionNode->append_attribute(shiftAttr); } } else if (atlControlType == eACET_SWITCH_STATE) { AZStd::shared_ptr stateConnection = AZStd::static_pointer_cast(connection); - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseValueAttribute, stateConnection->m_value); + + auto valueAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseValueAttribute, + xmlAllocator.allocate_string(AZStd::to_string(stateConnection->m_value).c_str()) + ); + + connectionNode->append_attribute(valueAttr); } + return connectionNode; } case AudioControls::eWCT_WWISE_EVENT: - { - XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data()); - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str()); - return connectionNode; - } - + [[fallthrough]]; case AudioControls::eWCT_WWISE_AUX_BUS: { - XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data()); - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str()); + auto connectionNode = xmlAllocator.allocate_node( + AZ::rapidxml::node_element, + xmlAllocator.allocate_string(TypeToTag(control->GetType()).data()) + ); + + auto nameAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseNameAttribute, + xmlAllocator.allocate_string(control->GetName().c_str()) + ); + + connectionNode->append_attribute(nameAttr); return connectionNode; } case AudioControls::eWCT_WWISE_SOUND_BANK: { - XmlNodeRef connectionNode = GetISystem()->CreateXmlNode(TypeToTag(control->GetType()).data()); - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseNameAttribute, control->GetName().c_str()); + auto connectionNode = xmlAllocator.allocate_node( + AZ::rapidxml::node_element, + xmlAllocator.allocate_string(TypeToTag(control->GetType()).data()) + ); + + auto nameAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseNameAttribute, + xmlAllocator.allocate_string(control->GetName().c_str()) + ); + + connectionNode->append_attribute(nameAttr); + if (control->IsLocalized()) { - connectionNode->setAttr(Audio::WwiseXmlTags::WwiseLocalizedAttribute, "true"); + auto locAttr = xmlAllocator.allocate_attribute( + Audio::WwiseXmlTags::WwiseLocalizedAttribute, + xmlAllocator.allocate_string("true") + ); + + connectionNode->append_attribute(locAttr); } + return connectionNode; } } diff --git a/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.h b/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.h index 14708a5816..25c621de76 100644 --- a/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.h +++ b/Gems/AudioEngineWwise/Code/Source/Editor/AudioSystemEditor_wwise.h @@ -77,8 +77,8 @@ namespace AudioControls EACEControlType ImplTypeToATLType(TImplControlType type) const override; TImplControlTypeMask GetCompatibleTypes(EACEControlType atlControlType) const override; TConnectionPtr CreateConnectionToControl(EACEControlType atlControlType, IAudioSystemControl* middlewareControl) override; - TConnectionPtr CreateConnectionFromXMLNode(XmlNodeRef node, EACEControlType atlControlType) override; - XmlNodeRef CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) override; + TConnectionPtr CreateConnectionFromXMLNode(AZ::rapidxml::xml_node* node, EACEControlType atlControlType) override; + AZ::rapidxml::xml_node* CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) override; const AZStd::string_view GetTypeIcon(TImplControlType type) const override; const AZStd::string_view GetTypeIconSelected(TImplControlType type) const override; AZStd::string GetName() const override; diff --git a/Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.cpp b/Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.cpp index 30320a567f..1a3ff121f8 100644 --- a/Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.cpp +++ b/Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.cpp @@ -17,12 +17,6 @@ #include #include -#include -#include -#include -#include - -using namespace PathUtil; namespace AudioControls { @@ -68,8 +62,7 @@ namespace AudioControls for (const auto& filePath : foundFiles) { AZ_Assert(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.c_str()), "FindFiles found file '%s' but FileIO says it doesn't exist!", filePath.c_str()); - AZStd::string fileName; - AZ::StringFunc::Path::GetFullFileName(filePath.c_str(), fileName); + AZ::IO::PathView fileName = filePath.Filename(); if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(filePath.c_str())) { @@ -79,15 +72,15 @@ namespace AudioControls // we load only one as all of them should have the // same content (in the future we want to have a // consistency report to highlight if this is not the case) - m_localizationFolder = fileName; + m_localizationFolder.assign(fileName.Native().data(), fileName.Native().size()); LoadSoundBanks(rootFolder, m_localizationFolder, true); isLocalizedLoaded = true; } } - else if (AZ::StringFunc::Find(fileName.c_str(), Audio::Wwise::BankExtension) != AZStd::string::npos - && !AZ::StringFunc::Equal(fileName.c_str(), Audio::Wwise::InitBank)) + else if (fileName.Extension() == Audio::Wwise::BankExtension && !AZ::StringFunc::Equal(fileName.Native(), Audio::Wwise::InitBank)) { - m_audioSystemImpl->CreateControl(SControlDef(fileName, eWCT_WWISE_SOUND_BANK, isLocalized, nullptr, subPath)); + m_audioSystemImpl->CreateControl( + SControlDef(AZStd::string{ fileName.Native() }, eWCT_WWISE_SOUND_BANK, isLocalized, nullptr, subPath)); } } } @@ -103,14 +96,14 @@ namespace AudioControls if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(filePath.c_str())) { - LoadControlsInFolder(filePath); + LoadControlsInFolder(filePath.Native()); } else { // Open the file, read into an xmlDoc, and call LoadControls with the root xml node... AZ_TracePrintf("AudioWwiseLoader", "Loading Xml from '%s'", filePath.c_str()); - Audio::ScopedXmlLoader xmlFileLoader(filePath); + Audio::ScopedXmlLoader xmlFileLoader(filePath.Native()); if (!xmlFileLoader.HasError()) { LoadControl(xmlFileLoader.GetRootNode()); diff --git a/Gems/AudioSystem/Code/Include/Editor/ACETypes.h b/Gems/AudioSystem/Code/Include/Editor/ACETypes.h index cc1e35028c..0bfaa6640c 100644 --- a/Gems/AudioSystem/Code/Include/Editor/ACETypes.h +++ b/Gems/AudioSystem/Code/Include/Editor/ACETypes.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace AudioControls { @@ -39,4 +40,7 @@ namespace AudioControls using FilepathSet = AZStd::set; + using XmlAllocator = AZ::rapidxml::memory_pool<>; + inline static XmlAllocator s_xmlAllocator; + } // namespace AudioControls diff --git a/Gems/AudioSystem/Code/Include/Editor/IAudioSystemEditor.h b/Gems/AudioSystem/Code/Include/Editor/IAudioSystemEditor.h index 5196e2f0bc..5a9c773a32 100644 --- a/Gems/AudioSystem/Code/Include/Editor/IAudioSystemEditor.h +++ b/Gems/AudioSystem/Code/Include/Editor/IAudioSystemEditor.h @@ -12,12 +12,10 @@ #include #include #include +#include #include -#include -#include - namespace AudioControls { class IAudioSystemEditor; @@ -117,14 +115,14 @@ namespace AudioControls //! @param node XML node where the connection is defined. //! @param atlControlType The type of the ATL control you are connecting to. //! @return A pointer to the newly created connection. - virtual TConnectionPtr CreateConnectionFromXMLNode(XmlNodeRef node, EACEControlType atlControlType) = 0; + virtual TConnectionPtr CreateConnectionFromXMLNode(AZ::rapidxml::xml_node* node, EACEControlType atlControlType) = 0; //! When serializing connections between controls this function will be called once per connection to serialize its properties. //! This function should be in sync with CreateConnectionToControl as whatever it's written here will have to be read there. //! @param connection Connection to serialize. //! @param atlControlType Type of the ATL control that has this connection. //! @return XML node with the connection serialized. - virtual XmlNodeRef CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) = 0; + virtual AZ::rapidxml::xml_node* CreateXMLNodeFromConnection(const TConnectionPtr connection, const EACEControlType atlControlType) = 0; //! Whenever a connection is removed from an ATL control this function should be called. //! To keep the system informed of which controls have been connected and which ones haven't. diff --git a/Gems/AudioSystem/Code/Include/Engine/ATLCommon.h b/Gems/AudioSystem/Code/Include/Engine/ATLCommon.h index dae411fd00..45997b1262 100644 --- a/Gems/AudioSystem/Code/Include/Engine/ATLCommon.h +++ b/Gems/AudioSystem/Code/Include/Engine/ATLCommon.h @@ -47,6 +47,7 @@ namespace Audio static constexpr const char* ATLInternalNameAttribute = "atl_internal_name"; static constexpr const char* ATLTypeAttribute = "atl_type"; static constexpr const char* ATLConfigGroupAttribute = "atl_config_group_name"; + static constexpr const char* ATLPathAttribute = "path"; static constexpr const char* ATLDataLoadType = "AutoLoad"; diff --git a/Gems/AudioSystem/Code/Include/Engine/AudioFileUtils.h b/Gems/AudioSystem/Code/Include/Engine/AudioFileUtils.h index 5f071fef63..37952eaa1a 100644 --- a/Gems/AudioSystem/Code/Include/Engine/AudioFileUtils.h +++ b/Gems/AudioSystem/Code/Include/Engine/AudioFileUtils.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include @@ -20,22 +21,26 @@ namespace Audio /*! * FindFilesInPath */ - static AZStd::vector FindFilesInPath(const AZStd::string_view folderPath, const char* filter) + static AZStd::vector FindFilesInPath(const AZStd::string_view folderPath, const char* filter) { - AZStd::vector foundFiles; + AZStd::vector foundFiles; AZ::IO::FileIOBase::FindFilesCallbackType findFilesCallback = [&foundFiles](const char* file) -> bool { - foundFiles.emplace_back(file); + foundFiles.emplace_back(AZ::IO::FixedMaxPath{ file }.LexicallyNormal()); return true; }; - auto fileIO = AZ::IO::FileIOBase::GetInstance(); - if (fileIO) + if (auto fileIO = AZ::IO::FileIOBase::GetInstance(); + fileIO != nullptr) { AZ::IO::Result result = fileIO->FindFiles(folderPath.data(), filter, findFilesCallback); + if (result == AZ::IO::ResultCode::Success) + { + return AZStd::move(foundFiles); + } } - return foundFiles; + return {}; } /*! diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControl.h b/Gems/AudioSystem/Code/Source/Editor/AudioControl.h index f7c856fb46..9335429fd4 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControl.h +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControl.h @@ -11,13 +11,11 @@ #include #include +#include #include #include -#include -#include - namespace AudioControls { class CATLControlsModel; @@ -25,15 +23,52 @@ namespace AudioControls //-------------------------------------------------------------------------------------------// struct SRawConnectionData { - SRawConnectionData(XmlNodeRef node, bool isValid) - : m_xmlNode(node) - , m_isValid(isValid) - {} + SRawConnectionData(AZ::rapidxml::xml_node* node, bool isValid) + { + m_xmlNode = DeepCopyNode(node); + m_isValid = isValid; + } - XmlNodeRef m_xmlNode; + AZ::rapidxml::xml_node* m_xmlNode{ nullptr }; // indicates if the connection is valid for the currently loaded middleware - bool m_isValid; + bool m_isValid{ false }; + + // Rapid XML provides a 'copy_node' utility that will copy an entire node tree, + // but it only copies pointers of any strings in the node names and values. + // This causes problems with storing raw xml nodes as this class does because strings + // will be pointing into the memory pool of an xml document that has gone out of scope. + // This function is a rewritten version of 'copy_node' that does the deep copy of strings + // into the new destination tree. + static AZ::rapidxml::xml_node* DeepCopyNode(AZ::rapidxml::xml_node* srcNode) + { + AZ::rapidxml::xml_node* destNode = nullptr; + if (srcNode) + { + XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator); + destNode = xmlAlloc.allocate_node(srcNode->type()); + + destNode->name(xmlAlloc.allocate_string(srcNode->name(), srcNode->name_size()), srcNode->name_size()); + destNode->value(xmlAlloc.allocate_string(srcNode->value(), srcNode->value_size()), srcNode->value_size()); + + for (AZ::rapidxml::xml_node* child = srcNode->first_node(); child != nullptr; child = child->next_sibling()) + { + destNode->append_node(DeepCopyNode(child)); + } + + for (AZ::rapidxml::xml_attribute* attr = srcNode->first_attribute(); attr != nullptr; attr = attr->next_attribute()) + { + destNode->append_attribute(xmlAlloc.allocate_attribute( + xmlAlloc.allocate_string(attr->name(), attr->name_size()), + xmlAlloc.allocate_string(attr->value(), attr->value_size()), + attr->name_size(), + attr->value_size() + )); + } + } + + return destNode; + } }; using TXmlNodeList = AZStd::vector; diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp index b352534510..d19cfe7adb 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp @@ -10,21 +10,31 @@ #include #include +#include + +#include + #include #include #include #include #include -#include -#include + + #include #include #include #include + + +#include +#include #include -#include #include +#include + + #include #include #include @@ -318,19 +328,24 @@ namespace AudioControls // once we can listen to delete messages from Asset system, this can be changed to an EBus handler. const char* controlsPath = nullptr; Audio::AudioSystemRequestBus::BroadcastResult(controlsPath, &Audio::AudioSystemRequestBus::Events::GetControlsPath); - AZStd::string sControlsPath(Path::GetEditingGameDataFolder()); - AZ::StringFunc::Path::Join(sControlsPath.c_str(), controlsPath, sControlsPath); - Audio::SAudioManagerRequestData oParseGlobalRequestData(sControlsPath.c_str(), Audio::eADS_GLOBAL); + + AZ::IO::FixedMaxPath controlsFolder{ controlsPath }; + + Audio::SAudioManagerRequestData oParseGlobalRequestData(controlsFolder.c_str(), Audio::eADS_GLOBAL); oConfigDataRequest.pData = &oParseGlobalRequestData; Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, oConfigDataRequest); // parse the AudioSystem level-specific config data - AZStd::string levelName{ GetIEditor()->GetLevelName().toUtf8().data() }; - AZ::StringFunc::Path::Join(sControlsPath.c_str(), "levels", sControlsPath); - AZ::StringFunc::Path::Join(sControlsPath.c_str(), levelName.c_str(), sControlsPath); - Audio::SAudioManagerRequestData oParseLevelRequestData(sControlsPath.c_str(), Audio::eADS_LEVEL_SPECIFIC); - oConfigDataRequest.pData = &oParseLevelRequestData; - Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, oConfigDataRequest); + AZStd::string levelName; + AzToolsFramework::EditorRequestBus::BroadcastResult(levelName, &AzToolsFramework::EditorRequests::GetLevelName); + if (!levelName.empty() && levelName != "Untitled") + { + controlsFolder /= "levels"; + controlsFolder /= levelName; + Audio::SAudioManagerRequestData oParseLevelRequestData(controlsFolder.c_str(), Audio::eADS_LEVEL_SPECIFIC); + oConfigDataRequest.pData = &oParseLevelRequestData; + Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, oConfigDataRequest); + } // inform the middleware specific plugin that the data has been saved // to disk (in case it needs to update something) diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp index c247abf855..e0eba868d6 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp @@ -11,26 +11,21 @@ #include #include +#include #include #include #include +#include #include #include #include #include -#include -#include -#include -#include -#include -#include +#include #include -using namespace PathUtil; - namespace AudioControls { //-------------------------------------------------------------------------------------------// @@ -91,100 +86,81 @@ namespace AudioControls { const CUndoSuspend suspendUndo; - // Get the partial path (relative under asset root) where the controls live. + // Get the relative path (under asset root) where the controls live. const char* controlsPath = nullptr; Audio::AudioSystemRequestBus::BroadcastResult(controlsPath, &Audio::AudioSystemRequestBus::Events::GetControlsPath); // Get the full path up to asset root. - AZStd::string controlsFullPath(Path::GetEditingGameDataFolder()); - AZ::StringFunc::Path::Join(controlsFullPath.c_str(), controlsPath, controlsFullPath); + AZ::IO::FixedMaxPath controlsFullPath = AZ::Utils::GetProjectPath(); + controlsFullPath /= controlsPath; // load the global controls - LoadAllLibrariesInFolder(controlsFullPath, ""); + LoadAllLibrariesInFolder(controlsFullPath.Native(), ""); - // load the level specific controls - auto cryPak = gEnv->pCryPak; + AZ::IO::FixedMaxPath searchPath = controlsFullPath / LoaderStrings::LevelsSubFolder; - AZStd::string searchMask; - AZ::StringFunc::Path::Join(controlsFullPath.c_str(), LoaderStrings::LevelsSubFolder, searchMask); - AZ::StringFunc::Path::Join(searchMask.c_str(), "*", searchMask, true, false); - AZ::IO::ArchiveFileIterator handle = cryPak->FindFirst(searchMask.c_str()); - if (handle) + auto foundFiles = Audio::FindFilesInPath(searchPath.Native(), "*"); + + for (const auto& file : foundFiles) { - do + if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(file.c_str())) { - if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory) + AZStd::string levelName{ file.Filename().Native() }; + LoadAllLibrariesInFolder(controlsFullPath.Native(), levelName); + + if (!m_atlControlsModel->ScopeExists(levelName)) { - AZStd::string_view name = handle.m_filename; - if (name != "." && name != "..") - { - LoadAllLibrariesInFolder(controlsFullPath, name); - if (!m_atlControlsModel->ScopeExists(name)) - { - // if the control doesn't exist it - // means it is not a real level in the - // project so it is flagged as LocalOnly - m_atlControlsModel->AddScope(name, true); - } - } + // If the scope doesn't exist it means it is not a real + // level in the project so it's flagged as LocalOnly + m_atlControlsModel->AddScope(levelName, true); } } - while (handle = cryPak->FindNext(handle)); - cryPak->FindClose(handle); } + CreateDefaultControls(); } //-------------------------------------------------------------------------------------------// void CAudioControlsLoader::LoadAllLibrariesInFolder(const AZStd::string_view folderPath, const AZStd::string_view level) { - AZStd::string path(folderPath); - if (path.back() != AZ_CORRECT_FILESYSTEM_SEPARATOR) - { - path.append(AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING); - } + AZ::IO::FixedMaxPath searchPath{ folderPath }; if (!level.empty()) { - path.append(LoaderStrings::LevelsSubFolder); - path.append(GetSlash()); - path.append(level); - path.append(AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING); + searchPath /= LoaderStrings::LevelsSubFolder; + searchPath /= level; } - AZStd::string searchPath = path + "*.xml"; - auto cryPak = gEnv->pCryPak; - AZ::IO::ArchiveFileIterator handle = cryPak->FindFirst(searchPath.c_str()); - if (handle) + auto foundFiles = Audio::FindFilesInPath(searchPath.Native(), "*.xml"); + + for (auto& file : foundFiles) { - do + Audio::ScopedXmlLoader xmlLoader(file.Native()); + if (xmlLoader.HasError()) { - AZStd::string filename = path + AZStd::string{ static_cast(handle.m_filename) }; - AZ::StringFunc::Path::Normalize(filename); - XmlNodeRef root = GetISystem()->LoadXmlFromFile(filename.c_str()); - if (root) + AZ_Warning("AudioControlsLoader", false, "Unable to load the xml file '%s'", file.c_str()); + continue; + } + + auto xmlRootNode = xmlLoader.GetRootNode(); + if (xmlRootNode && azstricmp(xmlRootNode->name(), Audio::ATLXmlTags::RootNodeTag) == 0) + { + AZ::IO::PathView fileName = file.Filename(); + + AZStd::to_lower(file.Native().begin(), file.Native().end()); + m_loadedFilenames.insert(file.c_str()); + + if (auto nameAttr = xmlRootNode->first_attribute(Audio::ATLXmlTags::ATLNameAttribute, 0, false); nameAttr != nullptr) { - AZStd::string tag = root->getTag(); - if (tag == Audio::ATLXmlTags::RootNodeTag) - { - AZStd::to_lower(filename.begin(), filename.end()); - m_loadedFilenames.insert(filename.c_str()); - AZStd::string file = static_cast(handle.m_filename); - if (root->haveAttr(Audio::ATLXmlTags::ATLNameAttribute)) - { - file = root->getAttr(Audio::ATLXmlTags::ATLNameAttribute); - } - AZ::StringFunc::Path::StripExtension(file); - LoadControlsLibrary(root, folderPath, level, file); - } + fileName = nameAttr->value(); } else { - CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_ERROR, "(Audio Controls Editor) Failed parsing ATL Library '%s'", filename.c_str()); + fileName = fileName.Stem(); } - } while (handle = cryPak->FindNext(handle)); - cryPak->FindClose(handle); + LoadControlsLibrary(xmlRootNode, folderPath, level, fileName.Native()); + } } } @@ -233,74 +209,93 @@ namespace AudioControls } //-------------------------------------------------------------------------------------------// - void CAudioControlsLoader::LoadControlsLibrary(XmlNodeRef rootNode, [[maybe_unused]] const AZStd::string_view filePath, const AZStd::string_view level, const AZStd::string_view fileName) + void CAudioControlsLoader::LoadControlsLibrary( + const AZ::rapidxml::xml_node* rootNode, + [[maybe_unused]] const AZStd::string_view filePath, + const AZStd::string_view level, + const AZStd::string_view fileName) { QStandardItem* rootFolderItem = AddUniqueFolderPath(m_layoutModel->invisibleRootItem(), QString(fileName.data())); if (rootFolderItem && rootNode) { - const int numControlTypes = rootNode->getChildCount(); - for (int i = 0; i < numControlTypes; ++i) + auto controlTypeNode = rootNode->first_node(); // e.g. "AudioTriggers", "AudioRtpcs", etc + while (controlTypeNode) { - XmlNodeRef node = rootNode->getChild(i); - const int numControls = node->getChildCount(); - for (int j = 0; j < numControls; ++j) + auto controlNode = controlTypeNode->first_node(); // e.g. "ATLTrigger", "ATLRtpc", etc + while (controlNode) { - LoadControl(node->getChild(j), rootFolderItem, level); + LoadControl(controlNode, rootFolderItem, level); + controlNode = controlNode->next_sibling(); } + controlTypeNode = controlTypeNode->next_sibling(); } } } //-------------------------------------------------------------------------------------------// - CATLControl* CAudioControlsLoader::LoadControl(XmlNodeRef node, QStandardItem* folderItem, const AZStd::string_view scope) + CATLControl* CAudioControlsLoader::LoadControl(AZ::rapidxml::xml_node* node, QStandardItem* folderItem, const AZStd::string_view scope) { CATLControl* control = nullptr; - if (node) + + AZStd::string controlPath; + if (auto controlPathAttr = node->first_attribute("path", 0, false); + controlPathAttr != nullptr) { - QStandardItem* parentItem = AddUniqueFolderPath(folderItem, QString(node->getAttr(LoaderStrings::PathAttribute))); - if (parentItem) + controlPath = controlPathAttr->value(); + } + + QStandardItem* parentItem = AddUniqueFolderPath(folderItem, QString(controlPath.c_str())); + if (parentItem) + { + AZStd::string name; + if (auto nameAttr = node->first_attribute(Audio::ATLXmlTags::ATLNameAttribute, 0, false); + nameAttr != nullptr) { - const AZStd::string name = node->getAttr(Audio::ATLXmlTags::ATLNameAttribute); - const EACEControlType controlType = TagToType(node->getTag()); + name = nameAttr->value(); + } - control = m_atlControlsModel->CreateControl(name, controlType); - if (control) + const EACEControlType controlType = TagToType(node->name()); + + control = m_atlControlsModel->CreateControl(name, controlType); + if (control) + { + QStandardItem* item = new QAudioControlItem(QString(control->GetName().c_str()), control); + if (item) { - QStandardItem* item = new QAudioControlItem(QString(control->GetName().c_str()), control); - if (item) - { - parentItem->appendRow(item); - } - - switch (controlType) - { - case eACET_SWITCH: - { - const int numStates = node->getChildCount(); - for (int i = 0; i < numStates; ++i) - { - CATLControl* stateControl = LoadControl(node->getChild(i), item, scope); - if (stateControl) - { - stateControl->SetParent(control); - control->AddChild(stateControl); - } - } - break; - } - case eACET_PRELOAD: - { - LoadPreloadConnections(node, control); - break; - } - default: - { - LoadConnections(node, control); - break; - } - } - control->SetScope(scope); + parentItem->appendRow(item); } + + switch (controlType) + { + case eACET_SWITCH: + { + auto switchStateNode = node->first_node(); + while (switchStateNode) + { + CATLControl* stateControl = LoadControl(switchStateNode, item, scope); + if (stateControl) + { + stateControl->SetParent(control); + control->AddChild(stateControl); + } + + switchStateNode = switchStateNode->next_sibling(); + } + break; + } + case eACET_PRELOAD: + { + LoadPreloadConnections(node, control); + break; + } + default: + { + LoadConnections(node, control); + break; + } + } + + control->SetScope(scope); } } @@ -310,44 +305,37 @@ namespace AudioControls //-------------------------------------------------------------------------------------------// void CAudioControlsLoader::LoadScopes() { - AZStd::string levelsFolderPath; - AZ::StringFunc::Path::Join(Path::GetEditingGameDataFolder().c_str(), LoaderStrings::LevelsSubFolder, levelsFolderPath); - LoadScopesImpl(levelsFolderPath); + AZ::IO::FixedMaxPath levelsFolderPath = AZ::Utils::GetProjectPath(); + levelsFolderPath /= "Levels"; + LoadScopesImpl(levelsFolderPath.Native()); } //-------------------------------------------------------------------------------------------// void CAudioControlsLoader::LoadScopesImpl(const AZStd::string_view levelsFolder) { - AZStd::string search; - AZ::StringFunc::Path::Join(levelsFolder.data(), "*", search, true, false); - auto cryPak = gEnv->pCryPak; - AZ::IO::ArchiveFileIterator handle = cryPak->FindFirst(search.c_str()); - if (handle) + auto fileIO = AZ::IO::FileIOBase::GetInstance(); + AZ::IO::FixedMaxPath searchPath{ levelsFolder }; + + auto foundFiles = Audio::FindFilesInPath(searchPath.Native(), "*"); + for (auto& file : foundFiles) { - do + AZ::IO::PathView filePath{ file }; + AZ::IO::PathView fileName = filePath.Filename(); + if (fileIO->IsDirectory(filePath.Native().data())) { - AZStd::string name = static_cast(handle.m_filename); - if (name != "." && name != ".." && !name.empty()) + LoadScopesImpl((searchPath / fileName).Native()); + } + else + { + AZ::IO::PathView fileExt = filePath.Extension(); + if (fileExt == ".ly" || fileExt == ".cry" || fileExt == ".prefab") { - if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory) - { - AZ::StringFunc::Path::Join(levelsFolder.data(), name.c_str(), search); - LoadScopesImpl(search); - } - else - { - AZStd::string extension; - AZ::StringFunc::Path::GetExtension(name.c_str(), extension, false); - if (extension.compare("cry") == 0 || extension.compare("ly") == 0) - { - AZ::StringFunc::Path::StripExtension(name); - m_atlControlsModel->AddScope(name); - } - } + AZ::IO::PathView fileStem = filePath.Stem(); + // May need to verify that .prefabs are the actual "level" prefab + // i.e. that it matches levels//.prefab + m_atlControlsModel->AddScope(fileStem.Native()); } } - while (handle = cryPak->FindNext(handle)); - cryPak->FindClose(handle); } } @@ -475,100 +463,80 @@ namespace AudioControls } //-------------------------------------------------------------------------------------------// - void CAudioControlsLoader::LoadConnections(XmlNodeRef rootNode, CATLControl* control) + void CAudioControlsLoader::LoadConnections(AZ::rapidxml::xml_node* rootNode, CATLControl* control) { - if (!rootNode || !control) + if (control && rootNode && m_audioSystemImpl) { - return; - } - - const int numChildren = rootNode->getChildCount(); - for (int i = 0; i < numChildren; ++i) - { - XmlNodeRef node = rootNode->getChild(i); - const AZStd::string tag = node->getTag(); - if (m_audioSystemImpl) + auto childNode = rootNode->first_node(); + while (childNode) { - TConnectionPtr connection = m_audioSystemImpl->CreateConnectionFromXMLNode(node, control->GetType()); + TConnectionPtr connection = m_audioSystemImpl->CreateConnectionFromXMLNode(childNode, control->GetType()); if (connection) { control->AddConnection(connection); } - control->m_connectionNodes.push_back(SRawConnectionData(node, connection != nullptr)); + + control->m_connectionNodes.push_back(SRawConnectionData(childNode, connection != nullptr)); + + childNode = childNode->next_sibling(); } } } //-------------------------------------------------------------------------------------------// - void CAudioControlsLoader::LoadPreloadConnections(XmlNodeRef node, CATLControl* control) + void CAudioControlsLoader::LoadPreloadConnections(AZ::rapidxml::xml_node* node, CATLControl* control) { - if (!node || !control) + if (!control || !node || !m_audioSystemImpl) { return; } - AZStd::string type = node->getAttr(Audio::ATLXmlTags::ATLTypeAttribute); - if (type.compare(Audio::ATLXmlTags::ATLDataLoadType) == 0) + AZStd::string type; + if (auto typeAttr = node->first_attribute(Audio::ATLXmlTags::ATLTypeAttribute, 0, false); + typeAttr != nullptr) { - control->SetAutoLoad(true); - } - else - { - control->SetAutoLoad(false); + type = typeAttr->value(); } - // Legacy Preload XML parsing... - // Read all the platform definitions for this control - XmlNodeRef platformsGroupNode = node->findChild(Audio::ATLXmlTags::ATLPlatformsTag); - if (platformsGroupNode) + control->SetAutoLoad(type == Audio::ATLXmlTags::ATLDataLoadType); + + auto platformGroupNode = node->first_node(Audio::ATLXmlTags::ATLPlatformsTag, 0, false); + if (platformGroupNode) { + // Legacy preload parsing... // Don't parse the platform groups xml chunk anymore. // Read the connection information for all connected preloads... - const int numChildren = node->getChildCount(); - for (int i = 0; i < numChildren; ++i) + auto configGroupNode = node->first_node(Audio::ATLXmlTags::ATLConfigGroupTag, 0, false); + while (configGroupNode) { - XmlNodeRef groupNode = node->getChild(i); - const AZStd::string tag = groupNode->getTag(); - if (tag.compare(Audio::ATLXmlTags::ATLConfigGroupTag) != 0) - { - continue; - } - - const AZStd::string groupName = groupNode->getAttr(Audio::ATLXmlTags::ATLNameAttribute); - const int numConnections = groupNode->getChildCount(); - for (int j = 0; j < numConnections; ++j) - { - XmlNodeRef connectionNode = groupNode->getChild(j); - if (connectionNode && m_audioSystemImpl) - { - TConnectionPtr connection = m_audioSystemImpl->CreateConnectionFromXMLNode(connectionNode, control->GetType()); - if (connection) - { - control->AddConnection(connection); - } - control->m_connectionNodes.push_back(SRawConnectionData(connectionNode, connection != nullptr)); - } - } - } - } - else - { - // New Preload XML parsing... - const int numChildren = node->getChildCount(); - for (int i = 0; i < numChildren; ++i) - { - XmlNodeRef connectionNode = node->getChild(i); - if (connectionNode && m_audioSystemImpl) + auto connectionNode = configGroupNode->first_node(); + while (connectionNode) { TConnectionPtr connection = m_audioSystemImpl->CreateConnectionFromXMLNode(connectionNode, control->GetType()); if (connection) { control->AddConnection(connection); } - control->m_connectionNodes.push_back(SRawConnectionData(connectionNode, connection != nullptr)); + connectionNode = connectionNode->next_sibling(); } + configGroupNode = configGroupNode->next_sibling(); + } + } + else + { + // New format preload parsing... + auto connectionNode = node->first_node(); + while (connectionNode) + { + TConnectionPtr connection = m_audioSystemImpl->CreateConnectionFromXMLNode(connectionNode, control->GetType()); + if (connection) + { + control->AddConnection(connection); + } + control->m_connectionNodes.push_back(SRawConnectionData(connectionNode, connection != nullptr)); + connectionNode = connectionNode->next_sibling(); } } } @@ -586,15 +554,28 @@ namespace AudioControls } //-------------------------------------------------------------------------------------------// - CATLControl* CAudioControlsLoader::CreateInternalSwitchState(CATLControl* parentControl, const AZStd::string& switchName, const AZStd::string& stateName) + CATLControl* CAudioControlsLoader::CreateInternalSwitchState(CATLControl* parentControl, [[maybe_unused]] const AZStd::string& switchName, const AZStd::string& stateName) { CATLControl* childControl = m_atlControlsModel->CreateControl(stateName, eACET_SWITCH_STATE, parentControl); - XmlNodeRef requestNode = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::ATLSwitchRequestTag); - requestNode->setAttr(Audio::ATLXmlTags::ATLNameAttribute, switchName.c_str()); - XmlNodeRef valueNode = requestNode->createNode(Audio::ATLXmlTags::ATLValueTag); - valueNode->setAttr(Audio::ATLXmlTags::ATLNameAttribute, stateName.c_str()); - requestNode->addChild(valueNode); + XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator); + AZ::rapidxml::xml_node* requestNode = + xmlAlloc.allocate_node(AZ::rapidxml::node_element, xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLSwitchRequestTag)); + + AZ::rapidxml::xml_attribute* switchNameAttr = xmlAlloc.allocate_attribute( + xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLNameAttribute), xmlAlloc.allocate_string(switchName.c_str())); + + requestNode->append_attribute(switchNameAttr); + + AZ::rapidxml::xml_node* valueNode = + xmlAlloc.allocate_node(AZ::rapidxml::node_element, xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLValueTag)); + + AZ::rapidxml::xml_attribute* stateNameAttr = xmlAlloc.allocate_attribute( + xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLNameAttribute), xmlAlloc.allocate_string(stateName.c_str())); + + valueNode->append_attribute(stateNameAttr); + + requestNode->append_node(valueNode); childControl->m_connectionNodes.push_back(SRawConnectionData(requestNode, false)); return childControl; diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.h b/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.h index bee55289e3..290327b946 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.h +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.h @@ -10,12 +10,11 @@ #pragma once #include +#include #include #include -#include - #include class QStandardItemModel; @@ -38,11 +37,11 @@ namespace AudioControls private: void LoadAllLibrariesInFolder(const AZStd::string_view folderPath, const AZStd::string_view level); - void LoadControlsLibrary(XmlNodeRef rootNode, const AZStd::string_view filePath, const AZStd::string_view level, const AZStd::string_view fileName); - CATLControl* LoadControl(XmlNodeRef node, QStandardItem* folderItem, const AZStd::string_view scope); + void LoadControlsLibrary(const AZ::rapidxml::xml_node* rootNode, const AZStd::string_view filePath, const AZStd::string_view level, const AZStd::string_view fileName); + CATLControl* LoadControl(AZ::rapidxml::xml_node* node, QStandardItem* folderItem, const AZStd::string_view scope); - void LoadPreloadConnections(XmlNodeRef node, CATLControl* control); - void LoadConnections(XmlNodeRef rootNode, CATLControl* control); + void LoadPreloadConnections(AZ::rapidxml::xml_node* node, CATLControl* control); + void LoadConnections(AZ::rapidxml::xml_node* rootNode, CATLControl* control); void CreateDefaultControls(); QStandardItem* AddControl(CATLControl* control, QStandardItem* folderItem); diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp index bb7409e179..80f05804f1 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp @@ -9,29 +9,28 @@ #include +#include +#include #include #include +#include +#include #include #include -#include #include #include #include + #include #include #include -#include -#include -#include #include #include #include -using namespace PathUtil; - namespace AudioControls { namespace WriterStrings @@ -80,6 +79,18 @@ namespace AudioControls index = index.sibling(++i, 0); } + auto fileIO = AZ::IO::FileIOBase::GetInstance(); + AZStd::for_each( + m_foundLibraryPaths.begin(), m_foundLibraryPaths.end(), + [fileIO](AZStd::string& libraryPath) -> void + { + AZStd::optional newLength = fileIO->ConvertToAlias(libraryPath.data(), libraryPath.size()); + if (newLength) + { + libraryPath.resize_no_construct(*newLength); + } + AZStd::to_lower(libraryPath.begin(), libraryPath.end()); + }); // Delete libraries that don't exist anymore from disk FilepathSet librariesToDelete; @@ -103,7 +114,10 @@ namespace AudioControls //-------------------------------------------------------------------------------------------// void CAudioControlsWriter::WriteLibrary(const AZStd::string_view libraryName, QModelIndex root) { - if (root.isValid()) + const char* controlsPath = nullptr; + Audio::AudioSystemRequestBus::BroadcastResult(controlsPath, &Audio::AudioSystemRequestBus::Events::GetControlsPath); + + if (root.isValid() && controlsPath) { TLibraryStorage library; int i = 0; @@ -114,68 +128,63 @@ namespace AudioControls child = root.model()->index(++i, 0, root); } - const char* controlsPath = nullptr; - Audio::AudioSystemRequestBus::BroadcastResult(controlsPath, &Audio::AudioSystemRequestBus::Events::GetControlsPath); - for (auto& libraryPair : library) { - AZStd::string libraryPath; + AZ::IO::FixedMaxPath libraryPath{ controlsPath }; const AZStd::string& scope = libraryPair.first; if (scope.empty()) { // no scope, file at the root level - libraryPath.append(controlsPath); - AZ::StringFunc::Path::Join(libraryPath.c_str(), libraryName.data(), libraryPath); - libraryPath.append(WriterStrings::LibraryExtension); + libraryPath /= libraryName; + libraryPath.ReplaceExtension(WriterStrings::LibraryExtension); } else { // with scope, inside level folder - libraryPath.append(controlsPath); - libraryPath.append(WriterStrings::LevelsSubFolder); - AZ::StringFunc::Path::Join(libraryPath.c_str(), scope.c_str(), libraryPath); - AZ::StringFunc::Path::Join(libraryPath.c_str(), libraryName.data(), libraryPath); - libraryPath.append(WriterStrings::LibraryExtension); + libraryPath /= AZ::IO::FixedMaxPath{ WriterStrings::LevelsSubFolder } / scope / libraryName; + libraryPath.ReplaceExtension(WriterStrings::LibraryExtension); } - // should be able to change this back to GamePathToFullPath once a path normalization bug has been fixed: - AZStd::string fullFilePath; - AZ::StringFunc::Path::Join(Path::GetEditingGameDataFolder().c_str(), libraryPath.c_str(), fullFilePath); - AZStd::to_lower(fullFilePath.begin(), fullFilePath.end()); + AZ::IO::FixedMaxPath fullFilePath = AZ::Utils::GetProjectPath(); + fullFilePath /= libraryPath; m_foundLibraryPaths.insert(fullFilePath.c_str()); const SLibraryScope& libScope = libraryPair.second; if (libScope.m_isDirty) { - XmlNodeRef fileNode = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::RootNodeTag); - fileNode->setAttr(Audio::ATLXmlTags::ATLNameAttribute, libraryName.data()); + XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator); + AZ::rapidxml::xml_node* fileNode = + xmlAlloc.allocate_node(AZ::rapidxml::node_element, xmlAlloc.allocate_string(Audio::ATLXmlTags::RootNodeTag)); + + AZ::rapidxml::xml_attribute* nameAttr = xmlAlloc.allocate_attribute( + xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLNameAttribute), xmlAlloc.allocate_string(libraryName.data())); + + fileNode->append_attribute(nameAttr); for (int ii = 0; ii < eACET_NUM_TYPES; ++ii) { - if (ii != eACET_SWITCH_STATE) // switch_states are written inside the switches + if (libScope.m_nodes[ii] && libScope.m_nodes[ii]->first_node() != nullptr) { - if (libScope.m_nodes[ii]->getChildCount() > 0) - { - fileNode->addChild(libScope.m_nodes[ii]); - } + fileNode->append_node(libScope.m_nodes[ii]); } } - if (QFileInfo::exists(fullFilePath.c_str())) + if (auto fileInfo = QFileInfo(fullFilePath.c_str()); + fileInfo.exists()) { - const DWORD fileAttributes = GetFileAttributes(fullFilePath.c_str()); - if (fileAttributes & FILE_ATTRIBUTE_READONLY) + if (!fileInfo.isWritable()) { - // file is read-only - CheckOutFile(fullFilePath); + // file exists and is read-only + CheckOutFile(fullFilePath.Native()); } - fileNode->saveToFile(fullFilePath.c_str()); + + [[maybe_unused]] bool writeOk = WriteXmlToFile(fullFilePath.Native(), fileNode); } else { - // save the file, CheckOutFile will add it, since it's new - fileNode->saveToFile(fullFilePath.c_str()); - CheckOutFile(fullFilePath); + // since it's a new file, save the file first, CheckOutFile will add it + [[maybe_unused]] bool writeOk = WriteXmlToFile(fullFilePath.Native(), fileNode); + CheckOutFile(fullFilePath.Native()); } } } @@ -249,14 +258,63 @@ namespace AudioControls } //-------------------------------------------------------------------------------------------// - void CAudioControlsWriter::WriteControlToXml(XmlNodeRef node, CATLControl* control, const AZStd::string_view path) + bool CAudioControlsWriter::WriteXmlToFile(const AZStd::string_view filepath, AZ::rapidxml::xml_node* rootNode) { + if (!rootNode) + { + return false; + } + + using namespace AZ::IO; + AZStd::string docString; + ByteContainerStream stringStream(&docString); + + AZ::rapidxml::xml_document xmlDoc; + xmlDoc.append_node(rootNode); + + RapidXMLStreamWriter streamWriter(&stringStream); + AZ::rapidxml::print(streamWriter.Iterator(), xmlDoc); + streamWriter.FlushCache(); + + constexpr int openMode = + (SystemFile::SF_OPEN_WRITE_ONLY | SystemFile::SF_OPEN_CREATE | SystemFile::SF_OPEN_CREATE_PATH); + + if (SystemFile fileOut; + fileOut.Open(filepath.data(), openMode)) + { + auto bytesWritten = fileOut.Write(docString.data(), docString.size()); + return (bytesWritten == docString.size()); + } + return false; + } + + //-------------------------------------------------------------------------------------------// + void CAudioControlsWriter::WriteControlToXml(AZ::rapidxml::xml_node* node, CATLControl* control, const AZStd::string_view path) + { + if (!node || !control) + { + return; + } + + XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator); + const EACEControlType type = control->GetType(); - XmlNodeRef childNode = node->createNode(TypeToTag(type).data()); - childNode->setAttr(Audio::ATLXmlTags::ATLNameAttribute, control->GetName().c_str()); + AZStd::string_view typeName = TypeToTag(type); + + AZ::rapidxml::xml_node* childNode = + xmlAlloc.allocate_node(AZ::rapidxml::node_element, xmlAlloc.allocate_string(typeName.data())); + + AZ::rapidxml::xml_attribute* nameAttr = xmlAlloc.allocate_attribute( + xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLNameAttribute), xmlAlloc.allocate_string(control->GetName().c_str())); + + childNode->append_attribute(nameAttr); + if (!path.empty()) { - childNode->setAttr("path", path.data()); + AZ::rapidxml::xml_attribute* pathAttr = xmlAlloc.allocate_attribute( + xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLPathAttribute), xmlAlloc.allocate_string(path.data())); + + childNode->append_attribute(pathAttr); } if (type == eACET_SWITCH) @@ -271,7 +329,11 @@ namespace AudioControls { if (control->IsAutoLoad()) { - childNode->setAttr(Audio::ATLXmlTags::ATLTypeAttribute, Audio::ATLXmlTags::ATLDataLoadType); + AZ::rapidxml::xml_attribute* loadAttr = xmlAlloc.allocate_attribute( + xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLTypeAttribute), + xmlAlloc.allocate_string(Audio::ATLXmlTags::ATLDataLoadType)); + + childNode->append_attribute(loadAttr); } // New Preloads XML... @@ -282,38 +344,39 @@ namespace AudioControls WriteConnectionsToXml(childNode, control); } - node->addChild(childNode); + node->append_node(childNode); } //-------------------------------------------------------------------------------------------// - void CAudioControlsWriter::WriteConnectionsToXml(XmlNodeRef node, CATLControl* control) + void CAudioControlsWriter::WriteConnectionsToXml(AZ::rapidxml::xml_node* node, CATLControl* control) { - if (control && m_audioSystemImpl) + if (node && control && m_audioSystemImpl) { TXmlNodeList otherNodes = control->m_connectionNodes; - auto end = AZStd::remove_if(otherNodes.begin(), otherNodes.end(), - [](const SRawConnectionData& node) + auto end = AZStd::remove_if( + otherNodes.begin(), otherNodes.end(), + [](const SRawConnectionData& connection) { - return node.m_isValid; + return connection.m_isValid; } ); otherNodes.erase(end, otherNodes.end()); for (auto& connectionNode : otherNodes) { - node->addChild(connectionNode.m_xmlNode); + node->append_node(SRawConnectionData::DeepCopyNode(connectionNode.m_xmlNode)); } const size_t size = control->ConnectionCount(); for (size_t i = 0; i < size; ++i) { - TConnectionPtr connection = control->GetConnectionAt(i); - if (connection) + if (TConnectionPtr connection = control->GetConnectionAt(i); + connection != nullptr) { - XmlNodeRef childNode = m_audioSystemImpl->CreateXMLNodeFromConnection(connection, control->GetType()); - if (childNode) + if (auto childNode = m_audioSystemImpl->CreateXMLNodeFromConnection(connection, control->GetType()); + childNode != nullptr) { - node->addChild(childNode); + node->append_node(childNode); control->m_connectionNodes.push_back(SRawConnectionData(childNode, true)); } } @@ -322,24 +385,24 @@ namespace AudioControls } //-------------------------------------------------------------------------------------------// - void CAudioControlsWriter::CheckOutFile(const AZStd::string& filepath) + void CAudioControlsWriter::CheckOutFile(const AZStd::string_view filepath) { IEditor* editor = GetIEditor(); IFileUtil* fileUtil = editor ? editor->GetFileUtil() : nullptr; if (fileUtil) { - fileUtil->CheckoutFile(filepath.c_str(), nullptr); + fileUtil->CheckoutFile(filepath.data(), nullptr); } } //-------------------------------------------------------------------------------------------// - void CAudioControlsWriter::DeleteLibraryFile(const AZStd::string& filepath) + void CAudioControlsWriter::DeleteLibraryFile(const AZStd::string_view filepath) { IEditor* editor = GetIEditor(); IFileUtil* fileUtil = editor ? editor->GetFileUtil() : nullptr; if (fileUtil) { - fileUtil->DeleteFromSourceControl(filepath.c_str(), nullptr); + fileUtil->DeleteFromSourceControl(filepath.data(), nullptr); } } diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.h b/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.h index 9e3978b94b..1414f6725d 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.h +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.h @@ -16,10 +16,9 @@ #include #include #include -#include + #include -#include class QStandardItemModel; @@ -33,15 +32,16 @@ namespace AudioControls { SLibraryScope() { - m_nodes[eACET_TRIGGER] = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::TriggersNodeTag); - m_nodes[eACET_RTPC] = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::RtpcsNodeTag); - m_nodes[eACET_SWITCH] = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::SwitchesNodeTag); + XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator); + m_nodes[eACET_TRIGGER] = xmlAlloc.allocate_node(AZ::rapidxml::node_element, Audio::ATLXmlTags::TriggersNodeTag); + m_nodes[eACET_RTPC] = xmlAlloc.allocate_node(AZ::rapidxml::node_element, Audio::ATLXmlTags::RtpcsNodeTag); + m_nodes[eACET_SWITCH] = xmlAlloc.allocate_node(AZ::rapidxml::node_element, Audio::ATLXmlTags::SwitchesNodeTag); m_nodes[eACET_SWITCH_STATE] = nullptr; - m_nodes[eACET_ENVIRONMENT] = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::EnvironmentsNodeTag); - m_nodes[eACET_PRELOAD] = GetISystem()->CreateXmlNode(Audio::ATLXmlTags::PreloadsNodeTag); + m_nodes[eACET_ENVIRONMENT] = xmlAlloc.allocate_node(AZ::rapidxml::node_element, Audio::ATLXmlTags::EnvironmentsNodeTag); + m_nodes[eACET_PRELOAD] = xmlAlloc.allocate_node(AZ::rapidxml::node_element, Audio::ATLXmlTags::PreloadsNodeTag); } - XmlNodeRef m_nodes[eACET_NUM_TYPES]; + AZ::rapidxml::xml_node* m_nodes[eACET_NUM_TYPES]; bool m_isDirty = false; }; @@ -56,12 +56,13 @@ namespace AudioControls private: void WriteLibrary(const AZStd::string_view libraryName, QModelIndex root); void WriteItem(QModelIndex index, const AZStd::string& path, TLibraryStorage& library, bool isParentModified); - void WriteControlToXml(XmlNodeRef node, CATLControl* control, const AZStd::string_view path); - void WriteConnectionsToXml(XmlNodeRef node, CATLControl* control); + void WriteControlToXml(AZ::rapidxml::xml_node* node, CATLControl* control, const AZStd::string_view path); + void WriteConnectionsToXml(AZ::rapidxml::xml_node* node, CATLControl* control); bool IsItemModified(QModelIndex index); - void CheckOutFile(const AZStd::string& filepath); - void DeleteLibraryFile(const AZStd::string& filepath); + bool WriteXmlToFile(const AZStd::string_view filepath, AZ::rapidxml::xml_node* rootNode); + void CheckOutFile(const AZStd::string_view filepath); + void DeleteLibraryFile(const AZStd::string_view filepath); CATLControlsModel* m_atlModel; QStandardItemModel* m_layoutModel; diff --git a/Gems/AudioSystem/Code/Source/Editor/QAudioControlEditorIcons.h b/Gems/AudioSystem/Code/Source/Editor/QAudioControlEditorIcons.h index ce77125443..4d322a49d9 100644 --- a/Gems/AudioSystem/Code/Source/Editor/QAudioControlEditorIcons.h +++ b/Gems/AudioSystem/Code/Source/Editor/QAudioControlEditorIcons.h @@ -31,7 +31,7 @@ namespace AudioControls iconFile = ":/Icons/Switch_Icon.svg"; break; case AudioControls::eACET_SWITCH_STATE: - iconFile = ":/Icons/Property_Icon.svg"; + iconFile = ":/Icons/Property_Icon.png"; break; case AudioControls::eACET_ENVIRONMENT: iconFile = ":/Icons/Environment_Icon.svg"; @@ -41,7 +41,7 @@ namespace AudioControls break; default: // should make a "default"/empty icon... - iconFile = ":/Icons/RTPC_Icon.svg"; + iconFile = ":/Icons/Unassigned.svg"; } QIcon icon(iconFile); diff --git a/Gems/AudioSystem/Code/Source/Engine/ATLComponents.cpp b/Gems/AudioSystem/Code/Source/Engine/ATLComponents.cpp index ca9cb214e2..7010012e87 100644 --- a/Gems/AudioSystem/Code/Source/Engine/ATLComponents.cpp +++ b/Gems/AudioSystem/Code/Source/Engine/ATLComponents.cpp @@ -1005,14 +1005,14 @@ namespace Audio AZStd::string searchPath; AZ::StringFunc::Path::Join(m_rootPath.c_str(), folderPath, searchPath); - AZStd::vector foundFiles = Audio::FindFilesInPath(searchPath, "*.xml"); + auto foundFiles = Audio::FindFilesInPath(searchPath, "*.xml"); for (const auto& file : foundFiles) { AZ_Assert(AZ::IO::FileIOBase::GetInstance()->Exists(file.c_str()), "FindFiles found file '%s' but FileIO says it doesn't exist!", file.c_str()); g_audioLogger.Log(eALT_ALWAYS, "Loading Audio Controls Library: '%s'", file.c_str()); - Audio::ScopedXmlLoader xmlFileLoader(file); + Audio::ScopedXmlLoader xmlFileLoader(file.Native()); if (xmlFileLoader.HasError()) { continue; @@ -1053,14 +1053,14 @@ namespace Audio AZStd::string searchPath; AZ::StringFunc::Path::Join(m_rootPath.c_str(), folderPath, searchPath); - AZStd::vector foundFiles = Audio::FindFilesInPath(searchPath, "*.xml"); + auto foundFiles = Audio::FindFilesInPath(searchPath, "*.xml"); for (const auto& file : foundFiles) { AZ_Assert(AZ::IO::FileIOBase::GetInstance()->Exists(file.c_str()), "FindFiles found file '%s' but FileIO says it doesn't exist!", file.c_str()); g_audioLogger.Log(eALT_ALWAYS, "Loading Audio Preloads Library: '%s'", file.c_str()); - Audio::ScopedXmlLoader xmlFileLoader(file); + Audio::ScopedXmlLoader xmlFileLoader(file.Native()); if (xmlFileLoader.HasError()) { continue; From 976272d9cf9d2e22dceb6eea011d2f993854d320 Mon Sep 17 00:00:00 2001 From: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Date: Thu, 22 Jul 2021 21:06:04 -0500 Subject: [PATCH 02/19] Cleans up more legacy code from ACE Rewrote some code to replace gEnv->pCryPak and ISystem usage. One place was grabbing a Camera view from ISystem, replaced with simple identity matrix. Cleaned up include headers. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- .../Code/Source/Editor/ATLControlsModel.cpp | 1 - .../Code/Source/Editor/ATLControlsPanel.cpp | 5 --- .../Code/Source/Editor/AudioControl.cpp | 1 - .../Editor/AudioControlsEditorPlugin.cpp | 12 ++---- .../Editor/AudioControlsEditorWindow.cpp | 39 ++++++------------- .../Source/Editor/AudioControlsEditorWindow.h | 1 - .../Source/Editor/AudioControlsLoader.cpp | 1 - .../Source/Editor/AudioResourceSelectors.cpp | 1 - .../Code/Source/Editor/AudioSystemPanel.cpp | 3 -- .../Source/Editor/ImplementationManager.cpp | 2 - .../Source/Editor/ImplementationManager.h | 3 -- .../Code/Source/Editor/InspectorPanel.cpp | 1 - .../Code/Source/Editor/QConnectionsWidget.cpp | 1 - 13 files changed, 14 insertions(+), 57 deletions(-) diff --git a/Gems/AudioSystem/Code/Source/Editor/ATLControlsModel.cpp b/Gems/AudioSystem/Code/Source/Editor/ATLControlsModel.cpp index 4fbc2a72c4..7bcce84ea4 100644 --- a/Gems/AudioSystem/Code/Source/Editor/ATLControlsModel.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/ATLControlsModel.cpp @@ -13,7 +13,6 @@ #include #include #include -#include namespace AudioControls { diff --git a/Gems/AudioSystem/Code/Source/Editor/ATLControlsPanel.cpp b/Gems/AudioSystem/Code/Source/Editor/ATLControlsPanel.cpp index 6503e83545..48a16e54e6 100644 --- a/Gems/AudioSystem/Code/Source/Editor/ATLControlsPanel.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/ATLControlsPanel.cpp @@ -16,13 +16,8 @@ #include #include #include -#include -#include -#include -#include #include #include -#include #include #include diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControl.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControl.cpp index 40c1cd3c79..c0a8fb8dde 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControl.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControl.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include namespace AudioControls diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorPlugin.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorPlugin.cpp index b8761c4d62..973cb836a0 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorPlugin.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorPlugin.cpp @@ -14,9 +14,6 @@ #include #include -#include -#include -#include #include #include @@ -28,7 +25,6 @@ using namespace AudioControls; -using namespace PathUtil; CATLControlsModel CAudioControlsEditorPlugin::ms_ATLModel; QATLTreeModel CAudioControlsEditorPlugin::ms_layoutModel; @@ -152,20 +148,18 @@ void CAudioControlsEditorPlugin::ExecuteTrigger(const AZStd::string_view sTrigge Audio::AudioSystemRequestBus::BroadcastResult(ms_nAudioTriggerID, &Audio::AudioSystemRequestBus::Events::GetAudioTriggerID, sTriggerName.data()); if (ms_nAudioTriggerID != INVALID_AUDIO_CONTROL_ID) { - const CCamera& camera = GetIEditor()->GetSystem()->GetViewCamera(); - Audio::SAudioRequest request; request.nFlags = Audio::eARF_PRIORITY_NORMAL; - const AZ::Matrix3x4 cameraMatrix = LYTransformToAZMatrix3x4(camera.GetMatrix()); + const AZ::Matrix3x4 listenerTxfm = AZ::Matrix3x4::CreateIdentity(); - Audio::SAudioListenerRequestData requestData(cameraMatrix); + Audio::SAudioListenerRequestData requestData(listenerTxfm); requestData.oNewPosition.NormalizeForwardVec(); requestData.oNewPosition.NormalizeUpVec(); request.pData = &requestData; Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, request); - ms_pIAudioProxy->SetPosition(cameraMatrix); + ms_pIAudioProxy->SetPosition(listenerTxfm); ms_pIAudioProxy->ExecuteTrigger(ms_nAudioTriggerID); } } diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp index d19cfe7adb..54a8ce616d 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp @@ -9,7 +9,6 @@ #include -#include #include #include @@ -18,22 +17,14 @@ #include #include #include +#include #include - - -#include #include #include #include - - -#include -#include -#include -#include - #include +#include #include #include @@ -41,6 +32,7 @@ #include #include + void InitACEResources() { Q_INIT_RESOURCE(AudioControlsEditorUI); @@ -116,25 +108,16 @@ namespace AudioControls { m_fileSystemWatcher.addPath(folder.data()); - AZStd::string search; - AZ::StringFunc::Path::Join(folder.data(), "*", search, true, false); - auto pCryPak = gEnv->pCryPak; - AZ::IO::ArchiveFileIterator handle = pCryPak->FindFirst(search.c_str()); - if (handle) + auto fileIO = AZ::IO::FileIOBase::GetInstance(); + auto foundFiles = Audio::FindFilesInPath(folder, "*"); + for (auto& file : foundFiles) { - do + if (fileIO->IsDirectory(file.c_str())) { - AZStd::string sName = static_cast(handle.m_filename); - if (!sName.empty() && sName[0] != '.') - { - if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory) - { - AZ::StringFunc::Path::Join(folder.data(), sName.c_str(), sName); - StartWatchingFolder(sName); - } - } - } while (handle = pCryPak->FindNext(handle)); - pCryPak->FindClose(handle); + AZ::IO::FixedMaxPath resolvedPath; + fileIO->ReplaceAlias(resolvedPath, file); + StartWatchingFolder(file.Native()); + } } } diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h index bfa08828ac..761f0c1a8e 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h @@ -12,7 +12,6 @@ #if !defined(Q_MOC_RUN) #include #include -#include #include #include diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp index e0eba868d6..8714fd21da 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioResourceSelectors.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioResourceSelectors.cpp index 90c50ac741..f0c9ebe3d1 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioResourceSelectors.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioResourceSelectors.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioSystemPanel.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioSystemPanel.cpp index 99f615d1fe..deccfc1364 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioSystemPanel.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioSystemPanel.cpp @@ -12,10 +12,7 @@ #include #include #include -#include -#include #include -#include #include #include diff --git a/Gems/AudioSystem/Code/Source/Editor/ImplementationManager.cpp b/Gems/AudioSystem/Code/Source/Editor/ImplementationManager.cpp index fc146a109d..bee5c1dd51 100644 --- a/Gems/AudioSystem/Code/Source/Editor/ImplementationManager.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/ImplementationManager.cpp @@ -14,8 +14,6 @@ #include #include #include -#include -#include //-----------------------------------------------------------------------------------------------// diff --git a/Gems/AudioSystem/Code/Source/Editor/ImplementationManager.h b/Gems/AudioSystem/Code/Source/Editor/ImplementationManager.h index dd6d61d310..70a99fcc0e 100644 --- a/Gems/AudioSystem/Code/Source/Editor/ImplementationManager.h +++ b/Gems/AudioSystem/Code/Source/Editor/ImplementationManager.h @@ -10,9 +10,6 @@ #pragma once #if !defined(Q_MOC_RUN) -#include -#include - #include #endif diff --git a/Gems/AudioSystem/Code/Source/Editor/InspectorPanel.cpp b/Gems/AudioSystem/Code/Source/Editor/InspectorPanel.cpp index a9e2046348..318aea211c 100644 --- a/Gems/AudioSystem/Code/Source/Editor/InspectorPanel.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/InspectorPanel.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include diff --git a/Gems/AudioSystem/Code/Source/Editor/QConnectionsWidget.cpp b/Gems/AudioSystem/Code/Source/Editor/QConnectionsWidget.cpp index 8a6fece85c..b65e690f1b 100644 --- a/Gems/AudioSystem/Code/Source/Editor/QConnectionsWidget.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/QConnectionsWidget.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include From 91dcbe7fed88592507dcf9f41e2102ee0990b8d8 Mon Sep 17 00:00:00 2001 From: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Date: Thu, 22 Jul 2021 21:17:41 -0500 Subject: [PATCH 03/19] Fixes minor mistake in helper function desc Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- Gems/AudioSystem/Code/Source/Editor/AudioControl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControl.h b/Gems/AudioSystem/Code/Source/Editor/AudioControl.h index 9335429fd4..494b0465bd 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControl.h +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControl.h @@ -34,11 +34,11 @@ namespace AudioControls // indicates if the connection is valid for the currently loaded middleware bool m_isValid{ false }; - // Rapid XML provides a 'copy_node' utility that will copy an entire node tree, + // Rapid XML provides a 'clone_node' utility that will copy an entire node tree, // but it only copies pointers of any strings in the node names and values. // This causes problems with storing raw xml nodes as this class does because strings // will be pointing into the memory pool of an xml document that has gone out of scope. - // This function is a rewritten version of 'copy_node' that does the deep copy of strings + // This function is a rewritten version of 'clone_node' that does the deep copy of strings // into the new destination tree. static AZ::rapidxml::xml_node* DeepCopyNode(AZ::rapidxml::xml_node* srcNode) { From d385f6ed99170d0c50c9f8e0543d4abee648c03f Mon Sep 17 00:00:00 2001 From: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Date: Fri, 23 Jul 2021 11:29:47 -0500 Subject: [PATCH 04/19] Addresses feedback from PR review Change DeepCopyNode utility to return a unique_ptr, fix up some string/path usages to avoid temporaries, etc. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- .../Code/Source/Editor/AudioWwiseLoader.cpp | 4 +--- .../Code/Include/Editor/ACETypes.h | 2 +- .../Code/Include/Engine/AudioFileUtils.h | 2 +- .../Code/Source/Editor/AudioControl.cpp | 2 +- .../Code/Source/Editor/AudioControl.h | 12 +++++----- .../Source/Editor/AudioControlsWriter.cpp | 22 +++++++------------ 6 files changed, 18 insertions(+), 26 deletions(-) diff --git a/Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.cpp b/Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.cpp index 1a3ff121f8..616a30994c 100644 --- a/Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.cpp +++ b/Gems/AudioEngineWwise/Code/Source/Editor/AudioWwiseLoader.cpp @@ -9,8 +9,6 @@ #include -#include - #include #include #include @@ -77,7 +75,7 @@ namespace AudioControls isLocalizedLoaded = true; } } - else if (fileName.Extension() == Audio::Wwise::BankExtension && !AZ::StringFunc::Equal(fileName.Native(), Audio::Wwise::InitBank)) + else if (fileName.Extension() == Audio::Wwise::BankExtension && fileName != Audio::Wwise::InitBank) { m_audioSystemImpl->CreateControl( SControlDef(AZStd::string{ fileName.Native() }, eWCT_WWISE_SOUND_BANK, isLocalized, nullptr, subPath)); diff --git a/Gems/AudioSystem/Code/Include/Editor/ACETypes.h b/Gems/AudioSystem/Code/Include/Editor/ACETypes.h index 0bfaa6640c..1ff6112056 100644 --- a/Gems/AudioSystem/Code/Include/Editor/ACETypes.h +++ b/Gems/AudioSystem/Code/Include/Editor/ACETypes.h @@ -41,6 +41,6 @@ namespace AudioControls using FilepathSet = AZStd::set; using XmlAllocator = AZ::rapidxml::memory_pool<>; - inline static XmlAllocator s_xmlAllocator; + inline XmlAllocator s_xmlAllocator; } // namespace AudioControls diff --git a/Gems/AudioSystem/Code/Include/Engine/AudioFileUtils.h b/Gems/AudioSystem/Code/Include/Engine/AudioFileUtils.h index 37952eaa1a..65122662b1 100644 --- a/Gems/AudioSystem/Code/Include/Engine/AudioFileUtils.h +++ b/Gems/AudioSystem/Code/Include/Engine/AudioFileUtils.h @@ -26,7 +26,7 @@ namespace Audio AZStd::vector foundFiles; AZ::IO::FileIOBase::FindFilesCallbackType findFilesCallback = [&foundFiles](const char* file) -> bool { - foundFiles.emplace_back(AZ::IO::FixedMaxPath{ file }.LexicallyNormal()); + foundFiles.emplace_back(AZ::IO::PathView{ file }.LexicallyNormal()); return true; }; diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControl.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControl.cpp index c0a8fb8dde..ce268022a8 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControl.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControl.cpp @@ -345,7 +345,7 @@ namespace AudioControls { for (auto& connectionNode : m_connectionNodes) { - if (TConnectionPtr connection = audioSystemImpl->CreateConnectionFromXMLNode(connectionNode.m_xmlNode, m_type)) + if (TConnectionPtr connection = audioSystemImpl->CreateConnectionFromXMLNode(connectionNode.m_xmlNode.get(), m_type)) { AddConnection(connection); connectionNode.m_isValid = true; diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControl.h b/Gems/AudioSystem/Code/Source/Editor/AudioControl.h index 494b0465bd..c53f4aa831 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControl.h +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControl.h @@ -25,11 +25,11 @@ namespace AudioControls { SRawConnectionData(AZ::rapidxml::xml_node* node, bool isValid) { - m_xmlNode = DeepCopyNode(node); + m_xmlNode = AZStd::move(DeepCopyNode(node)); m_isValid = isValid; } - AZ::rapidxml::xml_node* m_xmlNode{ nullptr }; + AZStd::unique_ptr> m_xmlNode{}; // indicates if the connection is valid for the currently loaded middleware bool m_isValid{ false }; @@ -40,20 +40,20 @@ namespace AudioControls // will be pointing into the memory pool of an xml document that has gone out of scope. // This function is a rewritten version of 'clone_node' that does the deep copy of strings // into the new destination tree. - static AZ::rapidxml::xml_node* DeepCopyNode(AZ::rapidxml::xml_node* srcNode) + [[nodiscard]] static AZStd::unique_ptr> DeepCopyNode(AZ::rapidxml::xml_node* srcNode) { - AZ::rapidxml::xml_node* destNode = nullptr; + AZStd::unique_ptr> destNode{}; if (srcNode) { XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator); - destNode = xmlAlloc.allocate_node(srcNode->type()); + destNode.reset(xmlAlloc.allocate_node(srcNode->type())); destNode->name(xmlAlloc.allocate_string(srcNode->name(), srcNode->name_size()), srcNode->name_size()); destNode->value(xmlAlloc.allocate_string(srcNode->value(), srcNode->value_size()), srcNode->value_size()); for (AZ::rapidxml::xml_node* child = srcNode->first_node(); child != nullptr; child = child->next_sibling()) { - destNode->append_node(DeepCopyNode(child)); + destNode->append_node(DeepCopyNode(child).release()); } for (AZ::rapidxml::xml_attribute* attr = srcNode->first_attribute(); attr != nullptr; attr = attr->next_attribute()) diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp index 80f05804f1..973da60c90 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp @@ -352,19 +352,13 @@ namespace AudioControls { if (node && control && m_audioSystemImpl) { - TXmlNodeList otherNodes = control->m_connectionNodes; - auto end = AZStd::remove_if( - otherNodes.begin(), otherNodes.end(), - [](const SRawConnectionData& connection) - { - return connection.m_isValid; - } - ); - otherNodes.erase(end, otherNodes.end()); - - for (auto& connectionNode : otherNodes) + for (auto& connectionNode : control->m_connectionNodes) { - node->append_node(SRawConnectionData::DeepCopyNode(connectionNode.m_xmlNode)); + if (!connectionNode.m_isValid) + { + auto nodeCopy = SRawConnectionData::DeepCopyNode(connectionNode.m_xmlNode.get()); + node->append_node(nodeCopy.release()); + } } const size_t size = control->ConnectionCount(); @@ -391,7 +385,7 @@ namespace AudioControls IFileUtil* fileUtil = editor ? editor->GetFileUtil() : nullptr; if (fileUtil) { - fileUtil->CheckoutFile(filepath.data(), nullptr); + fileUtil->CheckoutFile(AZ::IO::FixedMaxPath{ filepath }.c_str(), nullptr); } } @@ -402,7 +396,7 @@ namespace AudioControls IFileUtil* fileUtil = editor ? editor->GetFileUtil() : nullptr; if (fileUtil) { - fileUtil->DeleteFromSourceControl(filepath.data(), nullptr); + fileUtil->DeleteFromSourceControl(AZ::IO::FixedMaxPath{ filepath }.c_str(), nullptr); } } From ce2fc6c1e6cfe31ac6ed2264c0930574c3305ebe Mon Sep 17 00:00:00 2001 From: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Date: Fri, 23 Jul 2021 11:43:03 -0500 Subject: [PATCH 05/19] Addresses one more piece of feedback Updates a call to ConvertToAlias to use PathView. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp index 973da60c90..91f9aa5138 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsWriter.cpp @@ -84,10 +84,10 @@ namespace AudioControls m_foundLibraryPaths.begin(), m_foundLibraryPaths.end(), [fileIO](AZStd::string& libraryPath) -> void { - AZStd::optional newLength = fileIO->ConvertToAlias(libraryPath.data(), libraryPath.size()); - if (newLength) + if (auto newPathOpt = fileIO->ConvertToAlias(AZ::IO::PathView{ libraryPath }); + newPathOpt.has_value()) { - libraryPath.resize_no_construct(*newLength); + libraryPath = newPathOpt.value().Native(); } AZStd::to_lower(libraryPath.begin(), libraryPath.end()); }); From dff425764ab95c58c62307c293f08db7a1f32dc2 Mon Sep 17 00:00:00 2001 From: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Date: Fri, 23 Jul 2021 12:17:34 -0500 Subject: [PATCH 06/19] Fix some minor things Removes unnecessary things added during development. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- Gems/AudioSystem/Code/Source/Editor/AudioControl.h | 2 +- Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControl.h b/Gems/AudioSystem/Code/Source/Editor/AudioControl.h index c53f4aa831..37e67c815c 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControl.h +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControl.h @@ -42,7 +42,7 @@ namespace AudioControls // into the new destination tree. [[nodiscard]] static AZStd::unique_ptr> DeepCopyNode(AZ::rapidxml::xml_node* srcNode) { - AZStd::unique_ptr> destNode{}; + AZStd::unique_ptr> destNode; if (srcNode) { XmlAllocator& xmlAlloc(AudioControls::s_xmlAllocator); diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp index 8714fd21da..220cd32b5c 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsLoader.cpp @@ -553,7 +553,7 @@ namespace AudioControls } //-------------------------------------------------------------------------------------------// - CATLControl* CAudioControlsLoader::CreateInternalSwitchState(CATLControl* parentControl, [[maybe_unused]] const AZStd::string& switchName, const AZStd::string& stateName) + CATLControl* CAudioControlsLoader::CreateInternalSwitchState(CATLControl* parentControl, const AZStd::string& switchName, const AZStd::string& stateName) { CATLControl* childControl = m_atlControlsModel->CreateControl(stateName, eACET_SWITCH_STATE, parentControl); From 41ebcefe667252c30549e6b2ac81b3c12b4d21e0 Mon Sep 17 00:00:00 2001 From: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Date: Mon, 26 Jul 2021 15:23:31 -0500 Subject: [PATCH 07/19] Restoring include headers that were removed These removals caused build errors on non-unity builds. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- Gems/AudioSystem/Code/Source/Editor/ATLControlsModel.cpp | 1 + Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h | 1 + 2 files changed, 2 insertions(+) diff --git a/Gems/AudioSystem/Code/Source/Editor/ATLControlsModel.cpp b/Gems/AudioSystem/Code/Source/Editor/ATLControlsModel.cpp index 7bcce84ea4..4fbc2a72c4 100644 --- a/Gems/AudioSystem/Code/Source/Editor/ATLControlsModel.cpp +++ b/Gems/AudioSystem/Code/Source/Editor/ATLControlsModel.cpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace AudioControls { diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h index 761f0c1a8e..bfa08828ac 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h @@ -12,6 +12,7 @@ #if !defined(Q_MOC_RUN) #include #include +#include #include #include From 900cc08510513e46fec38261e7f67301974e1ee7 Mon Sep 17 00:00:00 2001 From: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Date: Mon, 26 Jul 2021 15:29:00 -0500 Subject: [PATCH 08/19] Removing dependencies on legacy code (#2358) * Removes use of gEnv->mMainThreadId Save off the thread id that was used when initializing audio system and connecting EBuses, use that instead of gEnv. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> * Replace uses of gEnv->pCryPak with AZ::IO Updated uses of pCryPak to instead go through the AZ::IO::FileIOBase instance. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- .../Source/Engine/FileIOHandler_wwise.cpp | 91 ++++++++++--------- .../Code/Source/Engine/AudioSystem.cpp | 37 ++++---- .../Code/Source/Engine/FileCacheManager.cpp | 15 +-- 3 files changed, 80 insertions(+), 63 deletions(-) diff --git a/Gems/AudioEngineWwise/Code/Source/Engine/FileIOHandler_wwise.cpp b/Gems/AudioEngineWwise/Code/Source/Engine/FileIOHandler_wwise.cpp index 388a539be1..9571dd86a9 100644 --- a/Gems/AudioEngineWwise/Code/Source/Engine/FileIOHandler_wwise.cpp +++ b/Gems/AudioEngineWwise/Code/Source/Engine/FileIOHandler_wwise.cpp @@ -7,18 +7,16 @@ */ -#include #include + #include +#include #include #include #include #include - -#include -#include -#include +#include #define MAX_NUMBER_STRING_SIZE (10) // 4G #define ID_TO_STRING_FORMAT_BANK AKTEXT("%u.bnk") @@ -90,34 +88,36 @@ namespace Audio bool CBlockingDevice_wwise::Open(const char* filename, AkOpenMode openMode, AkFileDesc& fileDesc) { - const char* openModeString = nullptr; + AZ::IO::OpenMode azOpenMode = AZ::IO::OpenMode::ModeBinary; switch (openMode) { case AK_OpenModeRead: - openModeString = "rbx"; + azOpenMode |= AZ::IO::OpenMode::ModeRead; break; case AK_OpenModeWrite: - openModeString = "wbx"; + azOpenMode |= AZ::IO::OpenMode::ModeWrite; break; case AK_OpenModeWriteOvrwr: - openModeString = "w+bx"; + azOpenMode |= (AZ::IO::OpenMode::ModeUpdate | AZ::IO::OpenMode::ModeWrite); break; case AK_OpenModeReadWrite: - openModeString = "abx"; + azOpenMode |= (AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeWrite); break; default: AZ_Assert(false, "Unknown Wwise file open mode."); return false; } - const size_t fileSize = gEnv->pCryPak->FGetSize(filename); - if (fileSize > 0) + auto fileIO = AZ::IO::FileIOBase::GetInstance(); + if (AZ::u64 fileSize = 0; + fileIO->Size(filename, fileSize) && fileSize != 0) { - AZ::IO::HandleType fileHandle = gEnv->pCryPak->FOpen(filename, openModeString, AZ::IO::IArchive::FOPEN_HINT_DIRECT_OPERATION); + AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; + fileIO->Open(filename, azOpenMode, fileHandle); if (fileHandle != AZ::IO::InvalidHandle) { fileDesc.hFile = GetAkFileHandle(fileHandle); - fileDesc.iFileSize = static_cast(fileSize); + fileDesc.iFileSize = aznumeric_cast(fileSize); fileDesc.uSector = 0; fileDesc.deviceID = m_deviceID; fileDesc.pCustomParam = nullptr; @@ -132,50 +132,58 @@ namespace Audio AKRESULT CBlockingDevice_wwise::Read(AkFileDesc& fileDesc, const AkIoHeuristics&, void* buffer, AkIOTransferInfo& transferInfo) { - AZ_Assert(buffer, "Wwise didn't provide a valid buffer to write to."); + AZ_Assert(buffer, "Wwise didn't provide a valid desination buffer to Read into."); AZ::IO::HandleType fileHandle = GetRealFileHandle(fileDesc.hFile); - const uint64_t currentFileReadPos = gEnv->pCryPak->FTell(fileHandle); - const uint64_t wantedFileReadPos = static_cast(transferInfo.uFilePosition); + auto fileIO = AZ::IO::FileIOBase::GetInstance(); - if (currentFileReadPos != wantedFileReadPos) + AZ::u64 currentFileReadPos = 0; + fileIO->Tell(fileHandle, currentFileReadPos); + + if (currentFileReadPos != transferInfo.uFilePosition) { - gEnv->pCryPak->FSeek(fileHandle, wantedFileReadPos, SEEK_SET); + fileIO->Seek(fileHandle, aznumeric_cast(transferInfo.uFilePosition), AZ::IO::SeekType::SeekFromStart); } - const size_t bytesRead = gEnv->pCryPak->FReadRaw(buffer, 1, transferInfo.uRequestedSize, fileHandle); - AZ_Assert(bytesRead == static_cast(transferInfo.uRequestedSize), - "Number of bytes read (%zu) for Wwise request doesn't match the requested size (%u).", bytesRead, transferInfo.uRequestedSize); - return (bytesRead > 0) ? AK_Success : AK_Fail; + AZ::u64 bytesRead = 0; + fileIO->Read(fileHandle, buffer, aznumeric_cast(transferInfo.uRequestedSize), &bytesRead); + const bool readOk = (bytesRead == aznumeric_cast(transferInfo.uRequestedSize)); + + AZ_Assert(readOk, + "Number of bytes read (%" PRIu64 ") for read request doesn't match the requested size (%u).", + bytesRead, transferInfo.uRequestedSize); + return readOk ? AK_Success : AK_Fail; } AKRESULT CBlockingDevice_wwise::Write(AkFileDesc& fileDesc, const AkIoHeuristics&, void* data, AkIOTransferInfo& transferInfo) { - AZ_Assert(data, "Wwise didn't provide a valid buffer to read from."); + AZ_Assert(data, "Wwise didn't provide a valid source buffer to Write from."); AZ::IO::HandleType fileHandle = GetRealFileHandle(fileDesc.hFile); + auto fileIO = AZ::IO::FileIOBase::GetInstance(); - const uint64_t currentFileWritePos = gEnv->pCryPak->FTell(fileHandle); - const uint64_t wantedFileWritePos = static_cast(transferInfo.uFilePosition); + AZ::u64 currentFileWritePos = 0; + fileIO->Tell(fileHandle, currentFileWritePos); - if (currentFileWritePos != wantedFileWritePos) + if (currentFileWritePos != transferInfo.uFilePosition) { - gEnv->pCryPak->FSeek(fileHandle, wantedFileWritePos, SEEK_SET); + fileIO->Seek(fileHandle, aznumeric_cast(transferInfo.uFilePosition), AZ::IO::SeekType::SeekFromStart); } - const size_t bytesWritten = gEnv->pCryPak->FWrite(data, 1, static_cast(transferInfo.uRequestedSize), fileHandle); - if (bytesWritten != static_cast(transferInfo.uRequestedSize)) - { - AZ_Error("Wwise", false, "Number of bytes written (%zu) for Wwise request doesn't match the requested size (%u).", + AZ::u64 bytesWritten = 0; + fileIO->Write(fileHandle, data, aznumeric_cast(transferInfo.uRequestedSize), &bytesWritten); + const bool writeOk = (bytesWritten == aznumeric_cast(transferInfo.uRequestedSize)); + + AZ_Error("Wwise", writeOk, + "Number of bytes written (%" PRIu64 ") for write request doesn't match the requested size (%u).", bytesWritten, transferInfo.uRequestedSize); - return AK_Fail; - } - return AK_Success; + return writeOk ? AK_Success : AK_Fail; } AKRESULT CBlockingDevice_wwise::Close(AkFileDesc& fileDesc) { - return gEnv->pCryPak->FClose(GetRealFileHandle(fileDesc.hFile)) ? AK_Success : AK_Fail; + auto fileIO = AZ::IO::FileIOBase::GetInstance(); + return fileIO->Close(GetRealFileHandle(fileDesc.hFile)) ? AK_Success : AK_Fail; } AkUInt32 CBlockingDevice_wwise::GetBlockSize([[maybe_unused]] AkFileDesc& fileDesc) @@ -189,7 +197,7 @@ namespace Audio deviceDesc.bCanRead = true; deviceDesc.bCanWrite = true; deviceDesc.deviceID = m_deviceID; - AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "CryPak", AZ_ARRAY_SIZE(deviceDesc.szDeviceName)); + AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "IO::IArchive", AZ_ARRAY_SIZE(deviceDesc.szDeviceName)); deviceDesc.uStringSize = AKPLATFORM::AkUtf16StrLen(deviceDesc.szDeviceName); } @@ -231,12 +239,13 @@ namespace Audio bool CStreamingDevice_wwise::Open(const char* filename, [[maybe_unused]] AkOpenMode openMode, AkFileDesc& fileDesc) { AZ_Assert(openMode == AK_OpenModeRead, "Wwise Async File IO - Only supports opening files for reading.\n"); - const size_t fileSize = gEnv->pCryPak->FGetSize(filename); - if (fileSize) + auto fileIO = AZ::IO::FileIOBase::GetInstance(); + if (AZ::u64 fileSize = 0; + fileIO->Size(filename, fileSize) && fileSize != 0) { AZStd::string* filenameStore = azcreate(AZStd::string, (filename)); fileDesc.hFile = AkFileHandle(); - fileDesc.iFileSize = static_cast(fileSize); + fileDesc.iFileSize = aznumeric_cast(fileSize); fileDesc.uSector = 0; fileDesc.deviceID = m_deviceID; fileDesc.pCustomParam = filenameStore; @@ -326,7 +335,7 @@ namespace Audio deviceDesc.bCanRead = true; deviceDesc.bCanWrite = false; deviceDesc.deviceID = m_deviceID; - AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "Streamer", AZ_ARRAY_SIZE(deviceDesc.szDeviceName)); + AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "IO::IStreamer", AZ_ARRAY_SIZE(deviceDesc.szDeviceName)); deviceDesc.uStringSize = AKPLATFORM::AkUtf16StrLen(deviceDesc.szDeviceName); } diff --git a/Gems/AudioSystem/Code/Source/Engine/AudioSystem.cpp b/Gems/AudioSystem/Code/Source/Engine/AudioSystem.cpp index 0353a8b39d..30d815cbec 100644 --- a/Gems/AudioSystem/Code/Source/Engine/AudioSystem.cpp +++ b/Gems/AudioSystem/Code/Source/Engine/AudioSystem.cpp @@ -23,6 +23,9 @@ namespace Audio extern CAudioLogger g_audioLogger; static constexpr const char AudioControlsBasePath[]{ "libs/gameaudio/" }; + // Save off the threadId of the "Main Thread" that was used to connect EBuses. + AZStd::thread_id g_mainThreadId; + /////////////////////////////////////////////////////////////////////////////////////////////////// // CAudioThread /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -77,6 +80,8 @@ namespace Audio CAudioSystem::CAudioSystem() : m_bSystemInitialized(false) { + g_mainThreadId = AZStd::this_thread::get_id(); + m_apAudioProxies.reserve(Audio::CVars::s_AudioObjectPoolSize); m_apAudioProxiesToBeFreed.reserve(16); m_controlsPath.assign(Audio::AudioControlsBasePath); @@ -99,7 +104,7 @@ namespace Audio { CAudioRequestInternal request(audioRequestData); - AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::PushRequest - called from non-Main thread!"); + AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::PushRequest - called from non-Main thread!"); AZ_Assert(0 == (request.nFlags & eARF_THREAD_SAFE_PUSH), "AudioSystem::PushRequest - called with flag THREAD_SAFE_PUSH!"); AZ_Assert(0 == (request.nFlags & eARF_EXECUTE_BLOCKING), "AudioSystem::PushRequest - called with flag EXECUTE_BLOCKING!"); @@ -114,7 +119,7 @@ namespace Audio CAudioRequestInternal request(audioRequestData); - AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::PushRequestBlocking - called from non-Main thread!"); + AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::PushRequestBlocking - called from non-Main thread!"); AZ_Assert(0 != (request.nFlags & eARF_EXECUTE_BLOCKING), "AudioSystem::PushRequestBlocking - called without EXECUTE_BLOCKING flag!"); AZ_Assert(0 == (request.nFlags & eARF_THREAD_SAFE_PUSH), "AudioSystem::PushRequestBlocking - called with THREAD_SAFE_PUSH flag!"); @@ -139,7 +144,7 @@ namespace Audio const EAudioRequestType requestType, const TATLEnumFlagsType specificRequestMask) { - AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::AddRequestListener - called from a non-Main thread!"); + AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::AddRequestListener - called from a non-Main thread!"); if (func) { @@ -155,7 +160,7 @@ namespace Audio /////////////////////////////////////////////////////////////////////////////////////////////////// void CAudioSystem::RemoveRequestListener(AudioRequestCallbackType func, void* const callbackOwner) { - AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::RemoveRequestListener - called from a non-Main thread!"); + AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::RemoveRequestListener - called from a non-Main thread!"); SAudioEventListener listener; listener.m_callbackOwner = callbackOwner; @@ -167,7 +172,7 @@ namespace Audio void CAudioSystem::ExternalUpdate() { // Main Thread! - AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::ExternalUpdate - called from non-Main thread!"); + AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::ExternalUpdate - called from non-Main thread!"); // Notify callbacks on the pending callbacks queue... // These are requests that were completed then queued for callback processing to happen here. @@ -242,7 +247,7 @@ namespace Audio /////////////////////////////////////////////////////////////////////////////////////////////////// bool CAudioSystem::Initialize() { - AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::Initialize - called from a non-Main thread!"); + AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::Initialize - called from a non-Main thread!"); if (!m_bSystemInitialized) { @@ -265,7 +270,7 @@ namespace Audio /////////////////////////////////////////////////////////////////////////////////////////////////// void CAudioSystem::Release() { - AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::Release - called from a non-Main thread!"); + AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::Release - called from a non-Main thread!"); for (auto audioProxy : m_apAudioProxies) { @@ -331,14 +336,14 @@ namespace Audio /////////////////////////////////////////////////////////////////////////////////////////////////// bool CAudioSystem::ReserveAudioListenerID(TAudioObjectID& rAudioObjectID) { - AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::ReserveAudioListenerID - called from a non-Main thread!"); + AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::ReserveAudioListenerID - called from a non-Main thread!"); return m_oATL.ReserveAudioListenerID(rAudioObjectID); } /////////////////////////////////////////////////////////////////////////////////////////////////// bool CAudioSystem::ReleaseAudioListenerID(TAudioObjectID const nAudioObjectID) { - AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::ReleaseAudioListenerID - called from a non-Main thread!"); + AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::ReleaseAudioListenerID - called from a non-Main thread!"); return m_oATL.ReleaseAudioListenerID(nAudioObjectID); } @@ -385,7 +390,7 @@ namespace Audio void CAudioSystem::RefreshAudioSystem([[maybe_unused]] const char* const levelName) { #if !defined(AUDIO_RELEASE) - AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::RefreshAudioSystem - called from a non-Main thread!"); + AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::RefreshAudioSystem - called from a non-Main thread!"); // Get the controls path and a level-specific preload Id first. // This will be passed with the request so that it doesn't have to lookup this data @@ -409,7 +414,7 @@ namespace Audio /////////////////////////////////////////////////////////////////////////////////////////////////// IAudioProxy* CAudioSystem::GetFreeAudioProxy() { - AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::GetFreeAudioProxy - called from a non-Main thread!"); + AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::GetFreeAudioProxy - called from a non-Main thread!"); CAudioProxy* audioProxy = nullptr; if (!m_apAudioProxies.empty()) @@ -435,7 +440,7 @@ namespace Audio /////////////////////////////////////////////////////////////////////////////////////////////////// void CAudioSystem::FreeAudioProxy(IAudioProxy* const audioProxyI) { - AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::FreeAudioProxy - called from a non-Main thread!"); + AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::FreeAudioProxy - called from a non-Main thread!"); auto const audioProxy = static_cast(audioProxyI); if (AZStd::find(m_apAudioProxiesToBeFreed.begin(), m_apAudioProxiesToBeFreed.end(), audioProxy) != m_apAudioProxiesToBeFreed.end() || AZStd::find(m_apAudioProxies.begin(), m_apAudioProxies.end(), audioProxy) != m_apAudioProxies.end()) @@ -469,7 +474,7 @@ namespace Audio /////////////////////////////////////////////////////////////////////////////////////////////////// const char* CAudioSystem::GetAudioControlName([[maybe_unused]] const EAudioControlType controlType, [[maybe_unused]] const TATLIDType atlID) const { - AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::GetAudioControlName - called from non-Main thread!"); + AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::GetAudioControlName - called from non-Main thread!"); const char* sResult = nullptr; #if !defined(AUDIO_RELEASE) @@ -524,7 +529,7 @@ namespace Audio /////////////////////////////////////////////////////////////////////////////////////////////////// const char* CAudioSystem::GetAudioSwitchStateName([[maybe_unused]] const TAudioControlID switchID, [[maybe_unused]] const TAudioSwitchStateID stateID) const { - AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::GetAudioSwitchStateName - called from non-Main thread!"); + AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::GetAudioSwitchStateName - called from non-Main thread!"); const char* sResult = nullptr; #if !defined(AUDIO_RELEASE) @@ -638,7 +643,7 @@ namespace Audio AZ_PROFILE_SCOPE_DYNAMIC(AZ::Debug::ProfileCategory::Audio, "Normal Request: %s", request.ToString().c_str()); - AZ_Assert(gEnv->mMainThreadId != CryGetCurrentThreadId(), "AudioSystem::ProcessRequestByPriority - called from Main thread!"); + AZ_Assert(g_mainThreadId != AZStd::this_thread::get_id(), "AudioSystem::ProcessRequestByPriority - called from Main thread!"); if (m_oATL.CanProcessRequests()) { @@ -698,7 +703,7 @@ namespace Audio #if !defined(AUDIO_RELEASE) void CAudioSystem::DrawAudioDebugData() { - AZ_Assert(gEnv->mMainThreadId == CryGetCurrentThreadId(), "AudioSystem::DrawAudioDebugData - called from non-Main thread!"); + AZ_Assert(g_mainThreadId == AZStd::this_thread::get_id(), "AudioSystem::DrawAudioDebugData - called from non-Main thread!"); if (CVars::s_debugDrawOptions.GetRawFlags() != 0) { diff --git a/Gems/AudioSystem/Code/Source/Engine/FileCacheManager.cpp b/Gems/AudioSystem/Code/Source/Engine/FileCacheManager.cpp index d6fe618998..f1a360fd7d 100644 --- a/Gems/AudioSystem/Code/Source/Engine/FileCacheManager.cpp +++ b/Gems/AudioSystem/Code/Source/Engine/FileCacheManager.cpp @@ -9,12 +9,12 @@ #include +#include #include #include #include #include #include -#include #include #include @@ -115,9 +115,9 @@ namespace Audio newAudioFileEntry->m_dataScope = dataScope; AZStd::to_lower(newAudioFileEntry->m_filePath.begin(), newAudioFileEntry->m_filePath.end()); - const size_t fileSize = gEnv->pCryPak->FGetSize(newAudioFileEntry->m_filePath.c_str()); - - if (fileSize > 0) + auto fileIO = AZ::IO::FileIOBase::GetInstance(); + if (AZ::u64 fileSize = 0; + fileIO->Size(newAudioFileEntry->m_filePath.c_str(), fileSize) && fileSize != 0) { newAudioFileEntry->m_fileSize = fileSize; newAudioFileEntry->m_flags.ClearFlags(eAFF_NOTFOUND); @@ -770,9 +770,12 @@ namespace Audio } AZStd::to_lower(audioFileEntry->m_filePath.begin(), audioFileEntry->m_filePath.end()); - audioFileEntry->m_fileSize = gEnv->pCryPak->FGetSize(audioFileEntry->m_filePath.c_str()); + AZ::u64 fileSize = 0; + auto fileIO = AZ::IO::FileIOBase::GetInstance(); + fileIO->Size(audioFileEntry->m_filePath.c_str(), fileSize); + audioFileEntry->m_fileSize = fileSize; - AZ_Assert(audioFileEntry->m_fileSize > 0, "FileCacheManager - UpdateLocalizedFileEntryData expected file size to be greater than zero!"); + AZ_Assert(audioFileEntry->m_fileSize != 0, "FileCacheManager - UpdateLocalizedFileEntryData expected file size to be greater than zero!"); } /////////////////////////////////////////////////////////////////////////////////////////////// From e81f59d1e1bda849f7a716ab70eb3472293cd12b Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Mon, 26 Jul 2021 13:41:28 -0700 Subject: [PATCH 09/19] Create XCB Connection mechanism for WSISurface implementation for Linux (#2400) - Add new Linux Trait to determine which display driver client API to use (only xcb supported for now) - Add support for xcb connections (initial) for Linux/Vulkan - Fix minor assertion caused by wrong use of sizeof - Fix casing issue in a couple of material files (Linux is case sensitive) --- .../AzFramework/API/ApplicationAPI_Linux.h | 32 +++++++++++++ .../Application/Application_Linux.cpp | 47 +++++++++++++++++++ .../Platform/Linux/platform_linux.cmake | 27 +++++++++++ .../Platform/Linux/glad_vulkan_linux.cmake | 18 +++++-- .../Platform/Linux/RHI/WSISurface_Linux.cpp | 22 +++++++++ .../001_lucy_regression_test.material | 2 +- .../002_wrinkle_regression_test.material | 2 +- cmake/Platform/Linux/PAL_linux.cmake | 4 ++ 8 files changed, 149 insertions(+), 5 deletions(-) diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h b/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h index 2c7d6e100c..03c65ce0c3 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/API/ApplicationAPI_Linux.h @@ -9,8 +9,13 @@ #pragma once +#include #include +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB +#include +#endif // LY_COMPILE_DEFINITIONS + namespace AzFramework { class LinuxLifecycleEvents @@ -25,4 +30,31 @@ namespace AzFramework using Bus = AZ::EBus; }; + +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + class LinuxXcbConnectionManager + { + public: + AZ_RTTI(LinuxXcbConnectionManager, "{649951316-3626-4C9D-9DCA-2E7ABF84C0A9}"); + + virtual ~LinuxXcbConnectionManager() = default; + + virtual xcb_connection_t* GetXcbConnection() const = 0; + }; + + class LinuxXcbConnectionManagerBusTraits + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + }; + + using LinuxXcbConnectionManagerBus = AZ::EBus; + using LinuxXcbConnectionManagerInterface = AZ::Interface; + +#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB } // namespace AzFramework diff --git a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp index 71779444b1..eb4165453e 100644 --- a/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp +++ b/Code/Framework/AzFramework/Platform/Linux/AzFramework/Application/Application_Linux.cpp @@ -12,6 +12,32 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// namespace AzFramework { +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + class LinuxXcbConnectionManagerImpl + : public LinuxXcbConnectionManagerBus::Handler + { + public: + LinuxXcbConnectionManagerImpl() + { + m_xcbConnection = xcb_connect(nullptr, nullptr); + AZ_Error("ApplicationLinux", m_xcbConnection != nullptr, "Unable to connect to X11 Server."); + LinuxXcbConnectionManagerBus::Handler::BusConnect(); + } + + ~LinuxXcbConnectionManagerImpl() + { + LinuxXcbConnectionManagerBus::Handler::BusDisconnect(); + xcb_disconnect(m_xcbConnection); + } + xcb_connection_t* GetXcbConnection() const override + { + return m_xcbConnection; + } + private: + xcb_connection_t* m_xcbConnection = nullptr; + }; +#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + //////////////////////////////////////////////////////////////////////////////////////////////// class ApplicationLinux : public Application::Implementation @@ -27,6 +53,12 @@ namespace AzFramework // Application::Implementation void PumpSystemEventLoopOnce() override; void PumpSystemEventLoopUntilEmpty() override; + private: + +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + AZStd::unique_ptr m_xcbConnectionManager; +#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + }; //////////////////////////////////////////////////////////////////////////////////////////////// @@ -39,11 +71,26 @@ namespace AzFramework ApplicationLinux::ApplicationLinux() { LinuxLifecycleEvents::Bus::Handler::BusConnect(); + +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + m_xcbConnectionManager = AZStd::make_unique(); + if (LinuxXcbConnectionManagerInterface::Get() == nullptr) + { + LinuxXcbConnectionManagerInterface::Register(m_xcbConnectionManager.get()); + } +#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB } //////////////////////////////////////////////////////////////////////////////////////////////// ApplicationLinux::~ApplicationLinux() { +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + if (LinuxXcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get()) + { + LinuxXcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get()); + } + m_xcbConnectionManager.reset(); +#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB LinuxLifecycleEvents::Bus::Handler::BusDisconnect(); } diff --git a/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake b/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake index 7a325ca97e..c79c5f1dff 100644 --- a/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake +++ b/Code/Framework/AzFramework/Platform/Linux/platform_linux.cmake @@ -5,3 +5,30 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT # # + +# Based on the linux window manager trait, perform the appropriate additional build configurations +# Only 'xcb', 'wayland', and 'xlib' are recognized +if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb") + + find_library(XCB_LIBRARY xcb) + + set(LY_BUILD_DEPENDENCIES + PRIVATE + ${XCB_LIBRARY} + ) + + set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB) + +elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "wayland") + + set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND) + +elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "xlib") + + set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XLIB) + +else() + + message(FATAL_ERROR, "Linux Window Manager ${PAL_TRAIT_LINUX_WINDOW_MANAGER} is not recognized") + +endif() diff --git a/Gems/Atom/RHI/Vulkan/3rdParty/Platform/Linux/glad_vulkan_linux.cmake b/Gems/Atom/RHI/Vulkan/3rdParty/Platform/Linux/glad_vulkan_linux.cmake index 41de383023..1936c5b911 100644 --- a/Gems/Atom/RHI/Vulkan/3rdParty/Platform/Linux/glad_vulkan_linux.cmake +++ b/Gems/Atom/RHI/Vulkan/3rdParty/Platform/Linux/glad_vulkan_linux.cmake @@ -6,6 +6,18 @@ # # -set(GLAD_VULKAN_COMPILE_DEFINITIONS - VK_USE_PLATFORM_XCB_KHR -) +if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb") + set(GLAD_VULKAN_COMPILE_DEFINITIONS + VK_USE_PLATFORM_XCB_KHR + ) +elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "wayland") + set(GLAD_VULKAN_COMPILE_DEFINITIONS + VK_USE_PLATFORM_WAYLAND_KHR + ) +elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "xlib") + set(GLAD_VULKAN_COMPILE_DEFINITIONS + VK_USE_PLATFORM_XLIB_KHR + ) +else() + message(FATAL_ERROR, "Linux Window Manager ${PAL_TRAIT_LINUX_WINDOW_MANAGER} is not recognized") +endif() diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp index 87b75d8609..98e91519ea 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Linux/RHI/WSISurface_Linux.cpp @@ -5,6 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ +#include #include #include #include @@ -17,15 +18,36 @@ namespace AZ { Instance& instance = Instance::GetInstance(); +#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB + + xcb_connection_t* xcb_connection = nullptr; + if (auto xcbConnectionManager = AzFramework::LinuxXcbConnectionManagerInterface::Get(); + xcbConnectionManager != nullptr) + { + xcb_connection = xcbConnectionManager->GetXcbConnection(); + } + AZ_Error("AtomVulkan_RHI", xcb_connection!=nullptr, "Unable to get XCB Connection"); + VkXcbSurfaceCreateInfoKHR createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; createInfo.pNext = nullptr; createInfo.flags = 0; + createInfo.connection = xcb_connection; createInfo.window = static_cast(m_descriptor.m_windowHandle.GetIndex()); const VkResult result = vkCreateXcbSurfaceKHR(instance.GetNativeInstance(), &createInfo, nullptr, &m_nativeSurface); AssertSuccess(result); return ConvertResult(result); +#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND + #error "Linux Window Manager Wayland not supported." + return RHI::ResultCode::Unimplemented; +#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_XLIB + #error "Linux Window Manager XLIB not supported." + return RHI::ResultCode::Unimplemented; +#else + #error "Linux Window Manager not recognized." + return RHI::ResultCode::Unimplemented; +#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB } } } diff --git a/Gems/Atom/TestData/TestData/Materials/SkinTestCases/001_lucy_regression_test.material b/Gems/Atom/TestData/TestData/Materials/SkinTestCases/001_lucy_regression_test.material index c359fea3b5..bafb047be9 100644 --- a/Gems/Atom/TestData/TestData/Materials/SkinTestCases/001_lucy_regression_test.material +++ b/Gems/Atom/TestData/TestData/Materials/SkinTestCases/001_lucy_regression_test.material @@ -30,7 +30,7 @@ }, "normal": { "flipY": true, - "textureMap": "Objects/Lucy/Lucy_normal.png" + "textureMap": "Objects/Lucy/Lucy_Normal.png" }, "subsurfaceScattering": { "enableSubsurfaceScattering": true, diff --git a/Gems/Atom/TestData/TestData/Materials/SkinTestCases/002_wrinkle_regression_test.material b/Gems/Atom/TestData/TestData/Materials/SkinTestCases/002_wrinkle_regression_test.material index ce42f32b67..400044d29f 100644 --- a/Gems/Atom/TestData/TestData/Materials/SkinTestCases/002_wrinkle_regression_test.material +++ b/Gems/Atom/TestData/TestData/Materials/SkinTestCases/002_wrinkle_regression_test.material @@ -29,7 +29,7 @@ }, "normal": { "flipY": true, - "textureMap": "Objects/Lucy/Lucy_normal.png" + "textureMap": "Objects/Lucy/Lucy_Normal.png" }, "subsurfaceScattering": { "enableSubsurfaceScattering": true, diff --git a/cmake/Platform/Linux/PAL_linux.cmake b/cmake/Platform/Linux/PAL_linux.cmake index 2f60b7e2c8..c137538ac0 100644 --- a/cmake/Platform/Linux/PAL_linux.cmake +++ b/cmake/Platform/Linux/PAL_linux.cmake @@ -37,3 +37,7 @@ set(LY_ASSET_DEPLOY_ASSET_TYPE "pc" CACHE STRING "Set the asset type for deploym # Set the python cmd tool ly_set(LY_PYTHON_CMD ${CMAKE_CURRENT_SOURCE_DIR}/python/python.sh) + +# Set the default window manager that applications should be using on Linux +# Note: Only ("xcb", "wayland", or "xlib" should be considered) +set(PAL_TRAIT_LINUX_WINDOW_MANAGER "xcb" CACHE STRING "Sets the Window Manager type to use when configuring Linux (xcb, wayland, or xlib)") From 7f84a4318c5c5a2cc21ca3a8e8718d1350b5513b Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Mon, 26 Jul 2021 13:58:50 -0700 Subject: [PATCH 10/19] Add an Orthogonal Projection option to the Camera Gem (#2414) * Add an Orthogonal Projection option to the Camera Gem This adds a check-box to opt into an ortho projection along with a half-width parameter to adjust the size of the visible area. Includes some light tweaks to ensure debug rendering looks OK and that we generate a correct camera state for these non-perspective views. Known issue: while in "Be this camera" mode in the Editor using an ortho projection manipulators aren't working correctly. This appears to be a downstream issue with CameraState consumers not actually checking the ortho flag. Signed-off-by: nvsickle * Fix some typos Signed-off-by: nvsickle * Account for reversed depth buffer Signed-off-by: nvsickle * Clarify depth reversal for MakeOrthographicMatrixRH Signed-off-by: nvsickle --- Code/Editor/EditorViewportWidget.cpp | 13 ++- .../AzCore/AzCore/Math/MatrixUtils.cpp | 7 +- .../AzCore/AzCore/Math/MatrixUtils.h | 3 +- .../AzFramework/Components/CameraBus.h | 16 +++ .../AzToolsFramework/API/EditorCameraBus.h | 7 +- .../Component/DebugCamera/CameraComponent.h | 4 + .../Code/Source/CameraComponent.cpp | 19 ++++ Gems/Camera/Code/Source/CameraComponent.cpp | 6 ++ .../Code/Source/CameraComponentController.cpp | 102 ++++++++++++++---- .../Code/Source/CameraComponentController.h | 11 ++ .../Code/Source/EditorCameraComponent.cpp | 75 +++++++++++-- .../Code/Source/EditorCameraComponent.h | 2 + 12 files changed, 232 insertions(+), 33 deletions(-) diff --git a/Code/Editor/EditorViewportWidget.cpp b/Code/Editor/EditorViewportWidget.cpp index 1f0c87ff6f..357c27fd72 100644 --- a/Code/Editor/EditorViewportWidget.cpp +++ b/Code/Editor/EditorViewportWidget.cpp @@ -463,7 +463,7 @@ void EditorViewportWidget::Update() if (m_updateCameraPositionNextTick) { - auto cameraState = m_renderViewport->GetCameraState(); + auto cameraState = GetCameraState(); AZ::Matrix3x4 matrix; matrix.SetBasisAndTranslation(cameraState.m_side, cameraState.m_forward, cameraState.m_up, cameraState.m_position); auto m = AZMatrix3x4ToLYMatrix3x4(matrix); @@ -1138,6 +1138,17 @@ void EditorViewportWidget::OnMenuSelectCurrentCamera() AzFramework::CameraState EditorViewportWidget::GetCameraState() { + if (m_viewEntityId.IsValid()) + { + bool cameraStateAcquired = false; + AzFramework::CameraState cameraState; + Camera::EditorCameraViewRequestBus::BroadcastResult(cameraStateAcquired, + &Camera::EditorCameraViewRequestBus::Events::GetCameraState, cameraState); + if (cameraStateAcquired) + { + return cameraState; + } + } return m_renderViewport->GetCameraState(); } diff --git a/Code/Framework/AzCore/AzCore/Math/MatrixUtils.cpp b/Code/Framework/AzCore/AzCore/Math/MatrixUtils.cpp index ccdabc9198..a578640d0d 100644 --- a/Code/Framework/AzCore/AzCore/Math/MatrixUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Math/MatrixUtils.cpp @@ -71,7 +71,7 @@ namespace AZ return &out; } - Matrix4x4* MakeOrthographicMatrixRH(Matrix4x4& out, float left, float right, float bottom, float top, float nearDist, float farDist) + Matrix4x4* MakeOrthographicMatrixRH(Matrix4x4& out, float left, float right, float bottom, float top, float nearDist, float farDist, bool reverseDepth) { AZ_Assert(right > left, "right should be greater than left"); // valid to have matrix invert top/bottom and far/near @@ -83,6 +83,11 @@ namespace AZ return nullptr; } + if (reverseDepth) + { + AZStd::swap(nearDist, farDist); + } + out.SetRow(0, 2.f/(right - left), 0.f, 0.f, - (right + left) / (right - left) ); out.SetRow(1, 0.f, 2.f / (top - bottom), 0.f, - (top + bottom) / (top - bottom) ); out.SetRow(2, 0.f, 0.f, 1 / (nearDist - farDist), nearDist / (nearDist - farDist) ); diff --git a/Code/Framework/AzCore/AzCore/Math/MatrixUtils.h b/Code/Framework/AzCore/AzCore/Math/MatrixUtils.h index fc85b7fccc..72a7b29887 100644 --- a/Code/Framework/AzCore/AzCore/Math/MatrixUtils.h +++ b/Code/Framework/AzCore/AzCore/Math/MatrixUtils.h @@ -57,8 +57,9 @@ namespace AZ //! @param top The y coordinate of top view-plane //! @param near Distance to the near view-plane. Must be no less than zero. //! @param far Distance to the far view-plane. Must be greater than zero. + //! @param reverseDepth Set to true to reverse depth which means near distance maps to 1 and far distance maps to 0. //! @return Pointer of the output matrix - Matrix4x4* MakeOrthographicMatrixRH(Matrix4x4& out, float left, float right, float bottom, float top, float nearDist, float farDist); + Matrix4x4* MakeOrthographicMatrixRH(Matrix4x4& out, float left, float right, float bottom, float top, float nearDist, float farDist, bool reverseDepth = false); //! Transforms a position by a matrix. This function can be used with any generic cases which include projection matrices. Vector3 MatrixTransformPosition(const Matrix4x4& matrix, const Vector3& inPosition); diff --git a/Code/Framework/AzFramework/AzFramework/Components/CameraBus.h b/Code/Framework/AzFramework/AzFramework/Components/CameraBus.h index 618aa76401..0b2a0cbb78 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/CameraBus.h +++ b/Code/Framework/AzFramework/AzFramework/Components/CameraBus.h @@ -63,6 +63,13 @@ namespace Camera //! @return The camera frustum's height virtual float GetFrustumHeight() = 0; + //! Gets whether or not the camera is using an orthographic projection. + //! @return True if the camera is using an orthographic projection, or false if the camera is using a perspective projection. + virtual bool IsOrthographic() = 0; + + //! @return The half width of the orthographic projection, @see SetOrthographicHalfWidth. + virtual float GetOrthographicHalfWidth() = 0; + //! Sets the camera's field of view in degrees between 0 < fov < 180 degrees //! @param fov The camera frustum's new field of view in degrees virtual void SetFov(float fov) @@ -95,6 +102,15 @@ namespace Camera //! @param height The camera frustum's new height virtual void SetFrustumHeight(float height) = 0; + //! Sets whether or not the camera should use an orthographic projection in place of a perspective projection. + //! @param orthographic If true, the camera will use an orthographic projection + virtual void SetOrthographic(bool orthographic) = 0; + + //! Sets the half-width of the orthographic projection. + //! @params halfWidth Used to calculate the bounds of the projection while in orthographic mode. + //! The height is calculated automatically based on the aspect ratio. + virtual void SetOrthographicHalfWidth(float halfWidth) = 0; + //! Makes the camera the active view virtual void MakeActiveView() = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorCameraBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorCameraBus.h index cb4a003fb4..60d19dfd85 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorCameraBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorCameraBus.h @@ -102,7 +102,7 @@ namespace Camera using EditorCameraNotificationBus = AZ::EBus; /** - * This bus is for requesting any camera-view-related changes + * This bus is for requesting any camera-view-related changes or information */ class EditorCameraViewRequests : public AZ::ComponentBus { @@ -115,6 +115,11 @@ namespace Camera * Sets this camera as the active view in the scene, otherwise restores the default editor camera if it was already active */ virtual void ToggleCameraAsActiveView() = 0; + + /** + * Gets the camera state associated with this view. + */ + virtual bool GetCameraState(AzFramework::CameraState& cameraState) = 0; }; using EditorCameraViewRequestBus = AZ::EBus; diff --git a/Gems/Atom/Component/DebugCamera/Code/Include/Atom/Component/DebugCamera/CameraComponent.h b/Gems/Atom/Component/DebugCamera/Code/Include/Atom/Component/DebugCamera/CameraComponent.h index 6f8484de9f..d5c84c7441 100644 --- a/Gems/Atom/Component/DebugCamera/Code/Include/Atom/Component/DebugCamera/CameraComponent.h +++ b/Gems/Atom/Component/DebugCamera/Code/Include/Atom/Component/DebugCamera/CameraComponent.h @@ -92,12 +92,16 @@ namespace AZ float GetFarClipDistance() override; float GetFrustumWidth() override; float GetFrustumHeight() override; + bool IsOrthographic() override; + float GetOrthographicHalfWidth() override; void SetFovDegrees(float fov) override; void SetFovRadians(float fov) override; void SetNearClipDistance(float nearClipDistance) override; void SetFarClipDistance(float farClipDistance) override; void SetFrustumWidth(float width) override; void SetFrustumHeight(float height) override; + void SetOrthographic(bool orthographic) override; + void SetOrthographicHalfWidth(float halfWidth) override; void MakeActiveView() override; // RPI::WindowContextNotificationBus overrides... diff --git a/Gems/Atom/Component/DebugCamera/Code/Source/CameraComponent.cpp b/Gems/Atom/Component/DebugCamera/Code/Source/CameraComponent.cpp index 27359ebdce..9a6f7c83ab 100644 --- a/Gems/Atom/Component/DebugCamera/Code/Source/CameraComponent.cpp +++ b/Gems/Atom/Component/DebugCamera/Code/Source/CameraComponent.cpp @@ -185,6 +185,15 @@ namespace AZ return m_componentConfig.m_depthFar * tanf(m_componentConfig.m_fovY / 2) * 2; } + bool CameraComponent::IsOrthographic() + { + return false; + } + + float CameraComponent::GetOrthographicHalfWidth() + { + return 0.0f; + } void CameraComponent::SetFovDegrees(float fov) { @@ -226,6 +235,16 @@ namespace AZ UpdateViewToClipMatrix(); } + void CameraComponent::SetOrthographic(bool orthographic) + { + AZ_Assert(!orthographic, "DebugCamera does not support orthographic projection"); + } + + void CameraComponent::SetOrthographicHalfWidth([[maybe_unused]] float halfWidth) + { + AZ_Assert(false, "DebugCamera does not support orthographic projection"); + } + void CameraComponent::MakeActiveView() { // do nothing diff --git a/Gems/Camera/Code/Source/CameraComponent.cpp b/Gems/Camera/Code/Source/CameraComponent.cpp index 814fd1c4e7..04c245bc95 100644 --- a/Gems/Camera/Code/Source/CameraComponent.cpp +++ b/Gems/Camera/Code/Source/CameraComponent.cpp @@ -103,9 +103,15 @@ namespace Camera ->Event("SetNearClipDistance", &CameraRequestBus::Events::SetNearClipDistance) ->Event("SetFarClipDistance", &CameraRequestBus::Events::SetFarClipDistance) ->Event("MakeActiveView", &CameraRequestBus::Events::MakeActiveView) + ->Event("IsOrthographic", &CameraRequestBus::Events::IsOrthographic) + ->Event("SetOrthographic", &CameraRequestBus::Events::SetOrthographic) + ->Event("GetOrthographicHalfWidth", &CameraRequestBus::Events::GetOrthographicHalfWidth) + ->Event("SetOrthographicHalfWidth", &CameraRequestBus::Events::SetOrthographicHalfWidth) ->VirtualProperty("FieldOfView","GetFovDegrees","SetFovDegrees") ->VirtualProperty("NearClipDistance", "GetNearClipDistance", "SetNearClipDistance") ->VirtualProperty("FarClipDistance", "GetFarClipDistance", "SetFarClipDistance") + ->VirtualProperty("Orthographic", "IsOrthographic", "SetOrthographic") + ->VirtualProperty("OrthographicHalfWidth", "GetOrthographicHalfWidth", "SetOrthographicHalfWidth") ; behaviorContext->Class()->RequestBus("CameraRequestBus"); diff --git a/Gems/Camera/Code/Source/CameraComponentController.cpp b/Gems/Camera/Code/Source/CameraComponentController.cpp index dbcc7fe6f1..bbe8235449 100644 --- a/Gems/Camera/Code/Source/CameraComponentController.cpp +++ b/Gems/Camera/Code/Source/CameraComponentController.cpp @@ -24,7 +24,9 @@ namespace Camera if (auto serializeContext = azrtti_cast(context)) { serializeContext->Class() - ->Version(2) + ->Version(3) + ->Field("Orthographic", &CameraComponentConfig::m_orthographic) + ->Field("Orthographic Half Width", &CameraComponentConfig::m_orthographicHalfWidth) ->Field("Field of View", &CameraComponentConfig::m_fov) ->Field("Near Clip Plane Distance", &CameraComponentConfig::m_nearClipDistance) ->Field("Far Clip Plane Distance", &CameraComponentConfig::m_farClipDistance) @@ -42,25 +44,33 @@ namespace Camera ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_makeActiveViewOnActivation, "Make active camera on activation?", "If true, this camera will become the active render camera when it activates") + ->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_orthographic, "Orthographic", + "If set, this camera will use an orthographic projection instead of a perspective one. Objects will appear as the same size, regardless of distance from the camera.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) + ->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_orthographicHalfWidth, "Orthographic Half-width", "The half-width used to calculate the orthographic projection. The height will be determined by the aspect ratio.") + ->Attribute(AZ::Edit::Attributes::Visibility, &CameraComponentConfig::GetOrthographicParameterVisibility) + ->Attribute(AZ::Edit::Attributes::Min, 0.001f) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::ValuesOnly) ->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_fov, "Field of view", "Vertical field of view in degrees") ->Attribute(AZ::Edit::Attributes::Min, MIN_FOV) ->Attribute(AZ::Edit::Attributes::Suffix, " degrees") ->Attribute(AZ::Edit::Attributes::Step, 1.f) ->Attribute(AZ::Edit::Attributes::Max, AZ::RadToDeg(AZ::Constants::Pi) - 0.0001f) //We assert at fovs >= Pi so set the max for this field to be just under that - ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshValues", 0x28e720d4)) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::ValuesOnly) + ->Attribute(AZ::Edit::Attributes::Visibility, &CameraComponentConfig::GetPerspectiveParameterVisibility) ->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_nearClipDistance, "Near clip distance", "Distance to the near clip plane of the view Frustum") ->Attribute(AZ::Edit::Attributes::Min, CAMERA_MIN_NEAR) ->Attribute(AZ::Edit::Attributes::Suffix, " m") ->Attribute(AZ::Edit::Attributes::Step, 0.1f) ->Attribute(AZ::Edit::Attributes::Max, &CameraComponentConfig::GetFarClipDistance) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues", 0xcbc2147c)) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) ->DataElement(AZ::Edit::UIHandlers::Default, &CameraComponentConfig::m_farClipDistance, "Far clip distance", "Distance to the far clip plane of the view Frustum") ->Attribute(AZ::Edit::Attributes::Min, &CameraComponentConfig::GetNearClipDistance) ->Attribute(AZ::Edit::Attributes::Suffix, " m") ->Attribute(AZ::Edit::Attributes::Step, 10.f) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues", 0xcbc2147c)) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) ; } } @@ -81,6 +91,16 @@ namespace Camera return AZ::EntityId(m_editorEntityId); } + AZ::u32 CameraComponentConfig::GetPerspectiveParameterVisibility() const + { + return m_orthographic ? AZ::Edit::PropertyVisibility::Hide : AZ::Edit::PropertyVisibility::Show; + } + + AZ::u32 CameraComponentConfig::GetOrthographicParameterVisibility() const + { + return m_orthographic ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide; + } + CameraComponentController::CameraComponentController(const CameraComponentConfig& config) { SetConfiguration(config); @@ -289,6 +309,16 @@ namespace Camera return m_config; } + AZ::RPI::ViewportContextPtr CameraComponentController::GetViewportContext() + { + auto atomViewportRequests = AZ::Interface::Get(); + if (m_atomCamera && atomViewportRequests) + { + return atomViewportRequests->GetDefaultViewportContext(); + } + return nullptr; + } + AZ::EntityId CameraComponentController::GetCameras() { return m_entityId; @@ -324,6 +354,16 @@ namespace Camera return m_config.m_frustumHeight; } + bool CameraComponentController::IsOrthographic() + { + return m_config.m_orthographic; + } + + float CameraComponentController::GetOrthographicHalfWidth() + { + return m_config.m_orthographicHalfWidth; + } + void CameraComponentController::SetFovDegrees(float fov) { m_config.m_fov = AZ::GetClamp(fov, MinFoV, MaxFoV); @@ -359,6 +399,18 @@ namespace Camera UpdateCamera(); } + void CameraComponentController::SetOrthographic(bool orthographic) + { + m_config.m_orthographic = orthographic; + UpdateCamera(); + } + + void CameraComponentController::SetOrthographicHalfWidth(float halfWidth) + { + m_config.m_orthographicHalfWidth = halfWidth; + UpdateCamera(); + } + void CameraComponentController::MakeActiveView() { // Set Legacy Cry view, if it exists @@ -423,30 +475,38 @@ namespace Camera m_view->SetCurrentParams(viewParams); } - auto atomViewportRequests = AZ::Interface::Get(); - if (m_atomCamera && atomViewportRequests) + if (auto viewportContext = GetViewportContext()) { AZ::Matrix4x4 viewToClipMatrix; float aspectRatio = m_view ? m_view->GetCamera().GetPixelAspectRatio() : 1.f; - auto viewportContext = atomViewportRequests->GetViewportContextByName( - atomViewportRequests->GetDefaultViewportContextName()); - if (viewportContext) + if (!m_atomAuxGeom) { - if (!m_atomAuxGeom) - { - SetupAtomAuxGeom(viewportContext); - } - auto windowSize = viewportContext->GetViewportSize(); - aspectRatio = aznumeric_cast(windowSize.m_width) / aznumeric_cast(windowSize.m_height); + SetupAtomAuxGeom(viewportContext); } + auto windowSize = viewportContext->GetViewportSize(); + aspectRatio = aznumeric_cast(windowSize.m_width) / aznumeric_cast(windowSize.m_height); // This assumes a reversed depth buffer, in line with other LY Atom integration - AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix, - AZ::DegToRad(m_config.m_fov), - aspectRatio, - m_config.m_nearClipDistance, - m_config.m_farClipDistance, - true); + if (m_config.m_orthographic) + { + AZ::MakeOrthographicMatrixRH(viewToClipMatrix, + -m_config.m_orthographicHalfWidth, + m_config.m_orthographicHalfWidth, + -m_config.m_orthographicHalfWidth / aspectRatio, + m_config.m_orthographicHalfWidth / aspectRatio, + m_config.m_nearClipDistance, + m_config.m_farClipDistance, + true); + } + else + { + AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix, + AZ::DegToRad(m_config.m_fov), + aspectRatio, + m_config.m_nearClipDistance, + m_config.m_farClipDistance, + true); + } m_updatingTransformFromEntity = true; m_atomCamera->SetViewToClipMatrix(viewToClipMatrix); m_updatingTransformFromEntity = false; diff --git a/Gems/Camera/Code/Source/CameraComponentController.h b/Gems/Camera/Code/Source/CameraComponentController.h index 7f4e419176..c004dbe6ec 100644 --- a/Gems/Camera/Code/Source/CameraComponentController.h +++ b/Gems/Camera/Code/Source/CameraComponentController.h @@ -40,6 +40,9 @@ namespace Camera float GetNearClipDistance() const; AZ::EntityId GetEditorEntityId() const; + AZ::u32 GetPerspectiveParameterVisibility() const; + AZ::u32 GetOrthographicParameterVisibility() const; + // Reflected members float m_fov = DefaultFoV; float m_nearClipDistance = DefaultNearPlaneDistance; @@ -49,6 +52,8 @@ namespace Camera bool m_specifyFrustumDimensions = false; AZ::u64 m_editorEntityId = AZ::EntityId::InvalidEntityId; bool m_makeActiveViewOnActivation = true; + bool m_orthographic = false; + float m_orthographicHalfWidth = 5.f; }; class CameraComponentController @@ -78,6 +83,7 @@ namespace Camera void Deactivate(); void SetConfiguration(const CameraComponentConfig& config); const CameraComponentConfig& GetConfiguration() const; + AZ::RPI::ViewportContextPtr GetViewportContext(); // CameraBus::Handler interface AZ::EntityId GetCameras() override; @@ -89,12 +95,17 @@ namespace Camera float GetFarClipDistance() override; float GetFrustumWidth() override; float GetFrustumHeight() override; + bool IsOrthographic() override; + float GetOrthographicHalfWidth() override; void SetFovDegrees(float fov) override; void SetFovRadians(float fov) override; void SetNearClipDistance(float nearClipDistance) override; void SetFarClipDistance(float farClipDistance) override; void SetFrustumWidth(float width) override; void SetFrustumHeight(float height) override; + void SetOrthographic(bool orthographic) override; + void SetOrthographicHalfWidth(float halfWidth) override; + void MakeActiveView() override; // AZ::TransformNotificationBus::Handler interface diff --git a/Gems/Camera/Code/Source/EditorCameraComponent.cpp b/Gems/Camera/Code/Source/EditorCameraComponent.cpp index beed4d7097..14e8e46e72 100644 --- a/Gems/Camera/Code/Source/EditorCameraComponent.cpp +++ b/Gems/Camera/Code/Source/EditorCameraComponent.cpp @@ -17,6 +17,9 @@ #include #include +#include +#include + namespace Camera { namespace ClassConverters @@ -155,6 +158,42 @@ namespace Camera } } + bool EditorCameraComponent::GetCameraState(AzFramework::CameraState& cameraState) + { + const CameraComponentConfig& config = m_controller.GetConfiguration(); + AZ::RPI::ViewportContextPtr viewportContext = m_controller.GetViewportContext(); + AZ::RPI::ViewPtr view = m_controller.GetView(); + + if (viewportContext == nullptr || view == nullptr) + { + return false; + } + + AzFramework::SetCameraTransform(cameraState, view->GetCameraTransform()); + + { + const AzFramework::WindowSize viewportSize = viewportContext->GetViewportSize(); + cameraState.m_viewportSize = + AZ::Vector2{aznumeric_cast(viewportSize.m_width), aznumeric_cast(viewportSize.m_height)}; + } + + if (config.m_orthographic) + { + cameraState.m_fovOrZoom = cameraState.m_viewportSize.GetX() / (config.m_orthographicHalfWidth * 2.0f); + cameraState.m_orthographic = true; + } + else + { + cameraState.m_fovOrZoom = config.m_fov; + cameraState.m_orthographic = false; + } + + cameraState.m_nearClip = config.m_nearClipDistance; + cameraState.m_farClip = config.m_farClipDistance; + + return true; + } + AZ::Crc32 EditorCameraComponent::OnPossessCameraButtonClicked() { AZ::EntityId currentViewEntity; @@ -201,9 +240,20 @@ namespace Camera const CameraComponentConfig& config = m_controller.GetConfiguration(); const float distance = config.m_farClipDistance * m_frustumViewPercentLength * 0.01f; - float tangent = static_cast(tan(0.5f * AZ::DegToRad(config.m_fov))); - float height = distance * tangent; - float width = height * debugDisplay.GetAspectRatio(); + float width; + float height; + + if (config.m_orthographic) + { + width = config.m_orthographicHalfWidth; + height = width / debugDisplay.GetAspectRatio(); + } + else + { + const float tangent = static_cast(tan(0.5f * AZ::DegToRad(config.m_fov))); + height = distance * tangent; + width = height * debugDisplay.GetAspectRatio(); + } AZ::Vector3 farPoints[4]; farPoints[0] = AZ::Vector3( width, distance, height); @@ -211,12 +261,21 @@ namespace Camera farPoints[2] = AZ::Vector3(-width, distance, -height); farPoints[3] = AZ::Vector3( width, distance, -height); - AZ::Vector3 start(0, 0, 0); AZ::Vector3 nearPoints[4]; - nearPoints[0] = farPoints[0].GetNormalizedSafe() * config.m_nearClipDistance; - nearPoints[1] = farPoints[1].GetNormalizedSafe() * config.m_nearClipDistance; - nearPoints[2] = farPoints[2].GetNormalizedSafe() * config.m_nearClipDistance; - nearPoints[3] = farPoints[3].GetNormalizedSafe() * config.m_nearClipDistance; + if (config.m_orthographic) + { + nearPoints[0] = AZ::Vector3( width, config.m_nearClipDistance, height); + nearPoints[1] = AZ::Vector3(-width, config.m_nearClipDistance, height); + nearPoints[2] = AZ::Vector3(-width, config.m_nearClipDistance, -height); + nearPoints[3] = AZ::Vector3( width, config.m_nearClipDistance, -height); + } + else + { + nearPoints[0] = farPoints[0].GetNormalizedSafe() * config.m_nearClipDistance; + nearPoints[1] = farPoints[1].GetNormalizedSafe() * config.m_nearClipDistance; + nearPoints[2] = farPoints[2].GetNormalizedSafe() * config.m_nearClipDistance; + nearPoints[3] = farPoints[3].GetNormalizedSafe() * config.m_nearClipDistance; + } debugDisplay.PushMatrix(world); debugDisplay.SetColor(m_frustumDrawColor.GetAsVector4()); diff --git a/Gems/Camera/Code/Source/EditorCameraComponent.h b/Gems/Camera/Code/Source/EditorCameraComponent.h index bf788256a9..095427a4a7 100644 --- a/Gems/Camera/Code/Source/EditorCameraComponent.h +++ b/Gems/Camera/Code/Source/EditorCameraComponent.h @@ -58,7 +58,9 @@ namespace Camera /// EditorCameraNotificationBus::Handler interface void OnViewportViewEntityChanged(const AZ::EntityId& newViewId) override; + /// EditorCameraViewRequestBus::Handler interface void ToggleCameraAsActiveView() override { OnPossessCameraButtonClicked(); } + bool GetCameraState(AzFramework::CameraState& cameraState) override; protected: void EditorDisplay(AzFramework::DebugDisplayRequests& displayInterface, const AZ::Transform& world); From da474357f3d0ae9d9c3e884a397ccdcaec7e9be6 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Mon, 26 Jul 2021 14:14:56 -0700 Subject: [PATCH 11/19] =?UTF-8?q?Some=20var=20cleanup=20so=20it=20shows=20?= =?UTF-8?q?better-organized=20in=20cmake-gui.=20Some=20vars=E2=80=A6=20(#2?= =?UTF-8?q?361)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Some var cleanup so it shows better-organized in cmake-gui. Some vars were also not following the namign convention we are using Removed some unnecessary messaging Fixed a TIF bug where it would report the wrong test in a message, fixed a message that was being triggered Changed TIF to be enabled just by the binary so running the ci_build scripts locally doesnt trigger TIF messaging Removed `LY_ENABLE_MULTIPLAYER_COMPRESSION`, it was not being used Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * handling case where a parameter can be empty Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> * needs to be var name, not contents Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- Code/Framework/AzCore/CMakeLists.txt | 2 +- .../profile_telemetry_platform_android.cmake | 2 +- .../Mac/profile_telemetry_platform_mac.cmake | 2 +- .../profile_telemetry_platform_windows.cmake | 2 +- .../iOS/profile_telemetry_platform_ios.cmake | 2 +- Code/Framework/AzFramework/CMakeLists.txt | 4 ++-- Code/Tools/TestImpactFramework/CMakeLists.txt | 8 +++++--- Gems/AudioEngineWwise/Code/CMakeLists.txt | 3 --- Gems/MultiplayerCompression/Code/CMakeLists.txt | 2 -- Gems/RADTelemetry/Code/CMakeLists.txt | 5 +++-- cmake/3rdParty/FindWwise.cmake | 6 ++---- cmake/Deployment.cmake | 2 +- cmake/EngineJson.cmake | 3 ++- cmake/FileUtil.cmake | 2 +- cmake/O3DEJson.cmake | 2 -- cmake/Platform/Windows/Packaging_windows.cmake | 8 ++++---- .../LYTestImpactFramework.cmake | 14 ++++++-------- scripts/build/Platform/Windows/build_config.json | 4 ++-- scripts/ctest/CMakeLists.txt | 1 + 19 files changed, 34 insertions(+), 40 deletions(-) diff --git a/Code/Framework/AzCore/CMakeLists.txt b/Code/Framework/AzCore/CMakeLists.txt index ff8a825aab..ea7cc27af5 100644 --- a/Code/Framework/AzCore/CMakeLists.txt +++ b/Code/Framework/AzCore/CMakeLists.txt @@ -12,7 +12,7 @@ ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME}) ly_get_list_relative_pal_filename(common_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/Common) -if(LY_ENABLE_RAD_TELEMETRY) +if(LY_RAD_TELEMETRY_ENABLED) set(AZ_CORE_RADTELEMETRY_FILES ${common_dir}/azcore_profile_telemetry_files.cmake) set(AZ_CORE_RADTELEMETRY_PLATFORM_INCLUDES ${pal_dir}/profile_telemetry_platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) set(AZ_CORE_RADTELEMETRY_INCLUDE_DIRECTORIES ${common_dir}) diff --git a/Code/Framework/AzCore/Platform/Android/profile_telemetry_platform_android.cmake b/Code/Framework/AzCore/Platform/Android/profile_telemetry_platform_android.cmake index 5b74429383..df12777586 100644 --- a/Code/Framework/AzCore/Platform/Android/profile_telemetry_platform_android.cmake +++ b/Code/Framework/AzCore/Platform/Android/profile_telemetry_platform_android.cmake @@ -12,6 +12,6 @@ # is being avoided to prevent overriding functions declared in other targets platfrom # specific cmake files -if(LY_ENABLE_RAD_TELEMETRY) +if(LY_RAD_TELEMETRY_ENABLED) set(LY_COMPILE_DEFINITIONS PUBLIC AZ_PROFILE_TELEMETRY) endif() diff --git a/Code/Framework/AzCore/Platform/Mac/profile_telemetry_platform_mac.cmake b/Code/Framework/AzCore/Platform/Mac/profile_telemetry_platform_mac.cmake index 5b74429383..df12777586 100644 --- a/Code/Framework/AzCore/Platform/Mac/profile_telemetry_platform_mac.cmake +++ b/Code/Framework/AzCore/Platform/Mac/profile_telemetry_platform_mac.cmake @@ -12,6 +12,6 @@ # is being avoided to prevent overriding functions declared in other targets platfrom # specific cmake files -if(LY_ENABLE_RAD_TELEMETRY) +if(LY_RAD_TELEMETRY_ENABLED) set(LY_COMPILE_DEFINITIONS PUBLIC AZ_PROFILE_TELEMETRY) endif() diff --git a/Code/Framework/AzCore/Platform/Windows/profile_telemetry_platform_windows.cmake b/Code/Framework/AzCore/Platform/Windows/profile_telemetry_platform_windows.cmake index 5b74429383..df12777586 100644 --- a/Code/Framework/AzCore/Platform/Windows/profile_telemetry_platform_windows.cmake +++ b/Code/Framework/AzCore/Platform/Windows/profile_telemetry_platform_windows.cmake @@ -12,6 +12,6 @@ # is being avoided to prevent overriding functions declared in other targets platfrom # specific cmake files -if(LY_ENABLE_RAD_TELEMETRY) +if(LY_RAD_TELEMETRY_ENABLED) set(LY_COMPILE_DEFINITIONS PUBLIC AZ_PROFILE_TELEMETRY) endif() diff --git a/Code/Framework/AzCore/Platform/iOS/profile_telemetry_platform_ios.cmake b/Code/Framework/AzCore/Platform/iOS/profile_telemetry_platform_ios.cmake index 1a12c4b4e0..aeb91ebce6 100644 --- a/Code/Framework/AzCore/Platform/iOS/profile_telemetry_platform_ios.cmake +++ b/Code/Framework/AzCore/Platform/iOS/profile_telemetry_platform_ios.cmake @@ -6,6 +6,6 @@ # # -if(LY_ENABLE_RAD_TELEMETRY) +if(LY_RAD_TELEMETRY_ENABLED) set(LY_COMPILE_DEFINITIONS PUBLIC AZ_PROFILE_TELEMETRY) endif() diff --git a/Code/Framework/AzFramework/CMakeLists.txt b/Code/Framework/AzFramework/CMakeLists.txt index 8e80234263..8a68aac887 100644 --- a/Code/Framework/AzFramework/CMakeLists.txt +++ b/Code/Framework/AzFramework/CMakeLists.txt @@ -10,7 +10,7 @@ ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME}) ly_get_list_relative_pal_filename(common_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/Common) -set(LY_ENABLE_STATISTICAL_PROFILING OFF CACHE BOOL "Enables statistical profiling when using AZ_PROFILE_SCOPE. If True, it takes effect only if RAD Telemetry is disabled.") +set(LY_STATISTICAL_PROFILING_ENABLED OFF CACHE BOOL "Enables statistical profiling when using AZ_PROFILE_SCOPE. If True, it takes effect only if RAD Telemetry is disabled.") set(LY_TOUCHBENDING_LAYER_BIT 63 CACHE STRING "Use TouchBending as the collision layer. The TouchBending layer can be a number from 1 to 63 (Default=63).") ly_add_target( @@ -38,7 +38,7 @@ ly_add_target( 3rdParty::lz4 ) -if(LY_ENABLE_STATISTICAL_PROFILING) +if(LY_STATISTICAL_PROFILING_ENABLED) ly_add_source_properties( SOURCES AzFramework/Debug/StatisticalProfilerProxy.h PROPERTY COMPILE_DEFINITIONS diff --git a/Code/Tools/TestImpactFramework/CMakeLists.txt b/Code/Tools/TestImpactFramework/CMakeLists.txt index 1fc59d1711..04cbee98dc 100644 --- a/Code/Tools/TestImpactFramework/CMakeLists.txt +++ b/Code/Tools/TestImpactFramework/CMakeLists.txt @@ -10,7 +10,9 @@ ly_get_list_relative_pal_filename(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Platf include(${pal_source_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) -if(${LY_TEST_IMPACT_ACTIVE} AND PAL_TRAIT_TEST_IMPACT_FRAMEWORK_SUPPORTED) - add_subdirectory(Runtime) - add_subdirectory(Frontend) +if(PAL_TRAIT_TEST_IMPACT_FRAMEWORK_SUPPORTED) + if(LY_TEST_IMPACT_INSTRUMENTATION_BIN) + add_subdirectory(Runtime) + add_subdirectory(Frontend) + endif() endif() diff --git a/Gems/AudioEngineWwise/Code/CMakeLists.txt b/Gems/AudioEngineWwise/Code/CMakeLists.txt index c6442df1ba..98a31fb91a 100644 --- a/Gems/AudioEngineWwise/Code/CMakeLists.txt +++ b/Gems/AudioEngineWwise/Code/CMakeLists.txt @@ -16,9 +16,6 @@ set(AUDIOENGINEWWISE_COMPILEDEFINITIONS ) find_package(Wwise MODULE) -if (NOT Wwise_FOUND) - message(STATUS "** Update the LY_WWISE_INSTALL_PATH cache variable if you intend to use Wwise.") -endif() ################################################################################ # Server / Unsupported diff --git a/Gems/MultiplayerCompression/Code/CMakeLists.txt b/Gems/MultiplayerCompression/Code/CMakeLists.txt index 405d92e683..f7494be3fd 100644 --- a/Gems/MultiplayerCompression/Code/CMakeLists.txt +++ b/Gems/MultiplayerCompression/Code/CMakeLists.txt @@ -6,8 +6,6 @@ # # -set(LY_ENABLE_MULTIPLAYER_COMPRESSION OFF CACHE BOOL "Enables usage of Multiplayer Compressor.") - ly_add_target( NAME MultiplayerCompression.Static STATIC NAMESPACE Gem diff --git a/Gems/RADTelemetry/Code/CMakeLists.txt b/Gems/RADTelemetry/Code/CMakeLists.txt index 3b65d6d47a..544540f273 100644 --- a/Gems/RADTelemetry/Code/CMakeLists.txt +++ b/Gems/RADTelemetry/Code/CMakeLists.txt @@ -6,8 +6,9 @@ # # -set(LY_ENABLE_RAD_TELEMETRY OFF CACHE BOOL "Enables RAD Telemetry in Debug/Profile mode.") -set(LY_RAD_TELEMETRY_INSTALL_ROOT "${LY_3RDPARTY_PATH}/RadTelemetry" CACHE PATH "Install path to RAD Telemetry.") +set(LY_RAD_TELEMETRY_ENABLED OFF CACHE BOOL "Enables RAD Telemetry in Debug/Profile mode.") +set(LY_RAD_TELEMETRY_INSTALL_ROOT "@LY_3RDPARTY_PATH@/RadTelemetry" CACHE PATH "Install path to RAD Telemetry.") +string(CONFIGURE ${LY_RAD_TELEMETRY_INSTALL_ROOT} LY_RAD_TELEMETRY_INSTALL_ROOT @ONLY) ly_get_list_relative_pal_filename(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME}) diff --git a/cmake/3rdParty/FindWwise.cmake b/cmake/3rdParty/FindWwise.cmake index fa73ced3cc..8cd6db31dd 100644 --- a/cmake/3rdParty/FindWwise.cmake +++ b/cmake/3rdParty/FindWwise.cmake @@ -44,7 +44,7 @@ foreach(test_path ${WWISE_SDK_PATHS}) is_valid_sdk(${test_path} found_sdk) if(found_sdk) # Update the Wwise Install Path cache variable - set(LY_WWISE_INSTALL_PATH "${test_path}" CACHE PATH "Path to Wwise version ${WWISE_VERSION} installation." FORCE) + set(LY_WWISE_INSTALL_PATH "${test_path}") break() endif() endforeach() @@ -52,12 +52,10 @@ endforeach() if(NOT found_sdk) # If we don't find a path that appears to be a valid Wwise install, we can bail here. # No 3rdParty::Wwise target will exist, so that can be checked elsewhere. - message(STATUS "Wwise SDK version ${WWISE_VERSION} was not found.") return() -else() - message(STATUS "Using Wwise SDK at ${LY_WWISE_INSTALL_PATH}") endif() +message(STATUS "Using Wwise SDK at ${LY_WWISE_INSTALL_PATH}") set(WWISE_COMMON_LIB_NAMES # Core AK diff --git a/cmake/Deployment.cmake b/cmake/Deployment.cmake index ab18e3bfec..b6b7aa1869 100644 --- a/cmake/Deployment.cmake +++ b/cmake/Deployment.cmake @@ -9,6 +9,6 @@ # Define options that control the different options for deployment for target platforms set(LY_ASSET_DEPLOY_MODE "LOOSE" CACHE STRING "Set the Asset deployment when deploying to the target platform (LOOSE, PAK, VFS)") -set(LY_OVERRIDE_PAK_FOLDER_ROOT "" CACHE STRING "Optional root path to where Pak file folders are stored. By default, blank will use a predefined 'paks' root.") +set(LY_ASSET_OVERRIDE_PAK_FOLDER_ROOT "" CACHE STRING "Optional root path to where Pak file folders are stored. By default, blank will use a predefined 'paks' root.") diff --git a/cmake/EngineJson.cmake b/cmake/EngineJson.cmake index 180a6395b0..f175ff5a8b 100644 --- a/cmake/EngineJson.cmake +++ b/cmake/EngineJson.cmake @@ -10,7 +10,8 @@ include_guard() -set(LY_EXTERNAL_SUBDIRS "" CACHE STRING "List of subdirectories to recurse into when running cmake against the engine's CMakeLists.txt") +set(LY_EXTERNAL_SUBDIRS "" CACHE STRING "Additional list of subdirectory to recurse into via the cmake `add_subdirectory()` command. \ + The subdirectories are included after the restricted platform folders have been visited by a call to `add_subdirectory(restricted/\${restricted_platform})`") #! read_engine_external_subdirs # Read the external subdirectories from the engine.json file diff --git a/cmake/FileUtil.cmake b/cmake/FileUtil.cmake index 69a6ecc377..4607e14452 100644 --- a/cmake/FileUtil.cmake +++ b/cmake/FileUtil.cmake @@ -110,7 +110,7 @@ platform=${PAL_PLATFORM_NAME} game_projects=${LY_PROJECTS_TARGET_NAME} asset_deploy_mode=${LY_ASSET_DEPLOY_MODE} asset_deploy_type=${LY_ASSET_DEPLOY_ASSET_TYPE} -override_pak_root=${LY_OVERRIDE_PAK_FOLDER_ROOT} +override_pak_root=${LY_ASSET_OVERRIDE_PAK_FOLDER_ROOT} ") endfunction() diff --git a/cmake/O3DEJson.cmake b/cmake/O3DEJson.cmake index af55075fb8..daae3b4529 100644 --- a/cmake/O3DEJson.cmake +++ b/cmake/O3DEJson.cmake @@ -8,8 +8,6 @@ include_guard() -set(LY_EXTERNAL_SUBDIRS "" CACHE STRING "List of subdirectories to recurse into when running cmake against the engine's CMakeLists.txt") - #! read_json_external_subdirs # Read the "external_subdirectories" array from a *.json file # External subdirectories are any folders with CMakeLists.txt in them diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index d5663dadfb..7c62a4984c 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -6,11 +6,11 @@ # # -set(CPACK_WIX_ROOT "" CACHE PATH "Path to the WiX install path") +set(LY_INSTALLER_WIX_ROOT "" CACHE PATH "Path to the WiX install path") -if(CPACK_WIX_ROOT) - if(NOT EXISTS ${CPACK_WIX_ROOT}) - message(FATAL_ERROR "Invalid path supplied for CPACK_WIX_ROOT argument") +if(LY_INSTALLER_WIX_ROOT) + if(NOT EXISTS ${LY_INSTALLER_WIX_ROOT}) + message(FATAL_ERROR "Invalid path supplied for LY_INSTALLER_WIX_ROOT argument") endif() else() # early out as no path to WiX has been supplied effectively disabling support diff --git a/cmake/TestImpactFramework/LYTestImpactFramework.cmake b/cmake/TestImpactFramework/LYTestImpactFramework.cmake index 48bc5bf3df..61cc8c200a 100644 --- a/cmake/TestImpactFramework/LYTestImpactFramework.cmake +++ b/cmake/TestImpactFramework/LYTestImpactFramework.cmake @@ -6,11 +6,8 @@ # # -# Switch to enable/disable test impact analysis (and related build targets) -option(LY_TEST_IMPACT_ACTIVE "Enable test impact framework" OFF) - # Path to test instrumentation binary -option(LY_TEST_IMPACT_INSTRUMENTATION_BIN "Path to test impact framework instrumentation binary" OFF) +set(LY_TEST_IMPACT_INSTRUMENTATION_BIN "" CACHE PATH "Path to test impact framework instrumentation binary") # Name of test impact framework console static library target set(LY_TEST_IMPACT_CONSOLE_STATIC_TARGET "TestImpact.Frontend.Console.Static") @@ -213,9 +210,9 @@ function(ly_test_impact_extract_python_test_params COMPOSITE_TEST COMPOSITE_SUIT list(GET suite_components 2 test_timeout) # Get python script path relative to repo root ly_test_impact_rebase_file_to_repo_root( - ${script_path} + "${script_path}" script_path - ${LY_ROOT_FOLDER} + "${LY_ROOT_FOLDER}" ) set(suite_params "{ \"suite\": \"${test_suite}\", \"script\": \"${script_path}\", \"timeout\": ${test_timeout} }") list(APPEND test_suites "${suite_params}") @@ -259,7 +256,8 @@ function(ly_test_impact_write_test_enumeration_file TEST_ENUMERATION_TEMPLATE_FI ly_test_impact_extract_google_test_params(${test} "${test_params}" test_name test_suites) list(APPEND google_benchmarks " { \"name\": \"${test_name}\", \"launch_method\": \"${launch_method}\", \"suites\": [${test_suites}] }") else() - message("${test_name} is of unknown type (TEST_LIBRARY property is empty)") + ly_test_impact_extract_python_test_params(${test} "${test_params}" test_name test_suites) + message("${test_name} is of unknown type (TEST_LIBRARY property is \"${test_type}\")") list(APPEND unknown_tests " { \"name\": \"${test}\", \"type\": \"${test_type}\" }") endif() endforeach() @@ -440,7 +438,7 @@ endfunction() #! ly_test_impact_post_step: runs the post steps to be executed after all other cmake scripts have been executed. function(ly_test_impact_post_step) - if(NOT ${LY_TEST_IMPACT_ACTIVE}) + if(NOT LY_TEST_IMPACT_INSTRUMENTATION_BIN) return() endif() diff --git a/scripts/build/Platform/Windows/build_config.json b/scripts/build/Platform/Windows/build_config.json index 0e8a6b8746..235e1406ab 100644 --- a/scripts/build/Platform/Windows/build_config.json +++ b/scripts/build/Platform/Windows/build_config.json @@ -132,7 +132,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_TEST_IMPACT_ACTIVE=1 -DLY_TEST_IMPACT_INSTRUMENTATION_BIN=!TEST_IMPACT_WIN_BINARY!", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_TEST_IMPACT_INSTRUMENTATION_BIN=!TEST_IMPACT_WIN_BINARY!", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "ALL_BUILD", "CMAKE_NATIVE_BUILD_ARGS": "/m /nologo" @@ -328,7 +328,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build\\windows_vs2019", - "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_DISABLE_TEST_MODULES=TRUE -DLY_VERSION_ENGINE_NAME=o3de-sdk -DCPACK_WIX_ROOT=\"!WIX! \"", + "CMAKE_OPTIONS": "-G \"Visual Studio 16 2019\" -DCMAKE_SYSTEM_VERSION=10.0 -DLY_UNITY_BUILD=TRUE -DLY_DISABLE_TEST_MODULES=TRUE -DLY_VERSION_ENGINE_NAME=o3de-sdk -DLY_INSTALLER_WIX_ROOT=\"!WIX! \"", "EXTRA_CMAKE_OPTIONS": "-DLY_INSTALLER_AUTO_GEN_TAG=ON -DLY_INSTALLER_DOWNLOAD_URL=https://dkb1uj4hs9ikv.cloudfront.net -DLY_INSTALLER_LICENSE_URL=https://www.o3debinaries.org/license -DLY_INSTALLER_3RD_PARTY_LICENSE_URL=https://dkb1uj4hs9ikv.cloudfront.net/SPDX-Licenses.txt", "CPACK_BUCKET": "spectra-prism-staging-us-west-2", "CMAKE_LY_PROJECTS": "", diff --git a/scripts/ctest/CMakeLists.txt b/scripts/ctest/CMakeLists.txt index 45fded24ed..98ea21e938 100644 --- a/scripts/ctest/CMakeLists.txt +++ b/scripts/ctest/CMakeLists.txt @@ -42,5 +42,6 @@ ly_add_test( TEST_COMMAND ${LY_PYTHON_CMD} ${CMAKE_CURRENT_LIST_DIR}/ctest_driver_test.py -x ${CMAKE_CTEST_COMMAND} --build-path ${CMAKE_BINARY_DIR} + TEST_LIBRARY pytest ) From b8300c6248d18bbb6ab947e656fa7180069dba69 Mon Sep 17 00:00:00 2001 From: chcurran <82187351+carlitosan@users.noreply.github.com> Date: Mon, 26 Jul 2021 16:02:17 -0700 Subject: [PATCH 12/19] fix entity reference removal when not in a variable Signed-off-by: chcurran <82187351+carlitosan@users.noreply.github.com> --- .../ScriptCanvas/Code/Builder/ScriptCanvasBuilder.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilder.cpp b/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilder.cpp index 1771d9b4a6..6a5344ed9b 100644 --- a/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilder.cpp +++ b/Gems/ScriptCanvas/Code/Builder/ScriptCanvasBuilder.cpp @@ -137,16 +137,9 @@ namespace ScriptCanvasBuilder if (!ScriptCanvas::Grammar::IsParserGeneratedId(entityId.first)) { - auto graphEntityId = variables.FindVariable(entityId.first); - if (!graphEntityId) - { - AZ_Error("ScriptCanvasBuilder", false, "Missing EntityId from graph data that was just parsed"); - continue; - } - - // copy to override list for editor display - if (graphEntityId->IsComponentProperty()) + if (auto graphEntityId = variables.FindVariable(entityId.first); graphEntityId && graphEntityId->IsComponentProperty()) { + // copy to override list for editor display m_overrides.push_back(*graphEntityId); auto& overrideValue = m_overrides.back(); overrideValue.SetScriptInputControlVisibility(AZ::Edit::PropertyVisibility::Hide); From ed6dbb48f4b292d17518c478ece4212922a070cc Mon Sep 17 00:00:00 2001 From: amzn-phist <52085794+amzn-phist@users.noreply.github.com> Date: Mon, 26 Jul 2021 18:19:07 -0500 Subject: [PATCH 13/19] Fixes failing tests and linux compile error Replaced a 'uint32' with AZ::u32 to fix a linux compile error that likely came about after cleaning up includes. Rewrites a failing unit test after the code under test was updated from CryPak to AZ::IO. Signed-off-by: amzn-phist <52085794+amzn-phist@users.noreply.github.com> --- Code/Editor/Util/UndoUtil.h | 2 +- .../Code/Tests/AudioSystemEditorTest.cpp | 111 +++++++++++------- 2 files changed, 69 insertions(+), 44 deletions(-) diff --git a/Code/Editor/Util/UndoUtil.h b/Code/Editor/Util/UndoUtil.h index f352eb317b..e825647f6b 100644 --- a/Code/Editor/Util/UndoUtil.h +++ b/Code/Editor/Util/UndoUtil.h @@ -31,7 +31,7 @@ public: static void Record(IUndoObject* undo); private: - static const uint32 scDescSize = 256; + static const AZ::u32 scDescSize = 256; char m_description[scDescSize]; bool m_bCancelled; bool m_bStartedRecord; diff --git a/Gems/AudioSystem/Code/Tests/AudioSystemEditorTest.cpp b/Gems/AudioSystem/Code/Tests/AudioSystemEditorTest.cpp index a486e24326..0debe128e2 100644 --- a/Gems/AudioSystem/Code/Tests/AudioSystemEditorTest.cpp +++ b/Gems/AudioSystem/Code/Tests/AudioSystemEditorTest.cpp @@ -10,54 +10,45 @@ #include #include +#include + #include #include -#include -#include -#include - using ::testing::NiceMock; using namespace AudioControls; namespace CustomMocks { - class AudioControlsEditorTest_CryPakMock - : public CryPakMock + class AudioControlsEditorTest_FileIOMock + : public AZ::IO::MockFileIOBase { public: - AZ_TEST_CLASS_ALLOCATOR(AudioControlsEditorTest_CryPakMock) + AZ_TEST_CLASS_ALLOCATOR(AudioControlsEditorTest_FileIOMock); - AudioControlsEditorTest_CryPakMock(const char* levelName) - : m_levelName(levelName) - {} - - AZ::IO::ArchiveFileIterator FindFirst([[maybe_unused]] AZStd::string_view dir, AZ::IO::IArchive::EFileSearchType) override + AudioControlsEditorTest_FileIOMock() { - AZ::IO::FileDesc fileDesc; - fileDesc.nSize = sizeof(AZ::IO::FileDesc); - // Add a filename and file description reference to the TestFindData map to make sure the file iterator is valid - m_findData = new TestFindData(); - m_findData->m_fileSet.emplace(AZ::IO::ArchiveFileIterator{ static_cast(m_findData.get()), m_levelName, fileDesc }); - return m_findData->Fetch(); } - AZ::IO::ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator iter) override + bool IsDirectory([[maybe_unused]] const char* path) override { - return ++iter; + return false; + } + + AZ::IO::Result FindFiles( + [[maybe_unused]] const char* path, + [[maybe_unused]] const char* filter, + AZ::IO::FileIOBase::FindFilesCallbackType callback) override + { + if (callback) + { + callback(m_levelName.c_str()); + return AZ::IO::ResultCode::Success; + } + return AZ::IO::ResultCode::Error; } - // public: for easy resetting... AZStd::string m_levelName; - - // Add an inherited FindData class to control the adding of a mapfile which indicates that a FileIterator is valid - struct TestFindData - : AZ::IO::FindData - { - using AZ::IO::FindData::m_fileSet; - }; - - AZStd::intrusive_ptr m_findData; }; } // namespace CustomMocks @@ -75,10 +66,6 @@ protected: void SetupEnvironment() override { m_allocatorScope.ActivateAllocators(); - - m_stubEnv.pCryPak = nullptr; - m_stubEnv.pFileIO = nullptr; - gEnv = &m_stubEnv; } void TeardownEnvironment() override @@ -87,30 +74,68 @@ protected: } private: - AZ::AllocatorScope m_allocatorScope; - SSystemGlobalEnvironment m_stubEnv; + AZ::AllocatorScope m_allocatorScope; }; AZ_UNIT_TEST_HOOK(new AudioControlsEditorTestEnvironment); -TEST(AudioControlsEditorTest, AudioControlsLoader_LoadScopes_ScopesAreAdded) +class AudioControlsEditorTest + : public ::testing::Test { - ASSERT_TRUE(gEnv != nullptr); - ASSERT_TRUE(gEnv->pCryPak == nullptr); +public: + void SetUp() override + { + // Store and remove the existing fileIO... + m_prevFileIO = AZ::IO::FileIOBase::GetInstance(); + if (m_prevFileIO) + { + AZ::IO::FileIOBase::SetInstance(nullptr); + } - NiceMock m_cryPakMock("ly_extension.ly"); - gEnv->pCryPak = &m_cryPakMock; + // Replace with a new FileIO Mock... + m_fileIO = AZStd::make_unique(); + AZ::IO::FileIOBase::SetInstance(m_fileIO.get()); + } + void TearDown() override + { + // Destroy our LocalFileIO... + m_fileIO.reset(); + + // Replace the old fileIO (set instance to null first)... + AZ::IO::FileIOBase::SetInstance(nullptr); + if (m_prevFileIO) + { + AZ::IO::FileIOBase::SetInstance(m_prevFileIO); + m_prevFileIO = nullptr; + } + } + +protected: + AZ::IO::FileIOBase* m_prevFileIO = nullptr; + AZStd::unique_ptr m_fileIO; +}; + +TEST_F(AudioControlsEditorTest, AudioControlsLoader_LoadScopes_ScopesAreAdded) +{ CATLControlsModel atlModel; CAudioControlsLoader loader(&atlModel, nullptr, nullptr); + m_fileIO->m_levelName = "ly_extension.ly"; loader.LoadScopes(); EXPECT_TRUE(atlModel.ScopeExists("ly_extension")); - m_cryPakMock.m_levelName = "cry_extension.cry"; + m_fileIO->m_levelName = "cry_extension.cry"; loader.LoadScopes(); EXPECT_TRUE(atlModel.ScopeExists("cry_extension")); + m_fileIO->m_levelName = "prefab_extension.prefab"; + loader.LoadScopes(); + EXPECT_TRUE(atlModel.ScopeExists("prefab_extension")); + + m_fileIO->m_levelName = "spawnable_extension.spawnable"; + loader.LoadScopes(); + EXPECT_FALSE(atlModel.ScopeExists("spawnable_extension")); + atlModel.ClearScopes(); - gEnv->pCryPak = nullptr; } From 223654c41bc9fb0505769804859477926acf4296 Mon Sep 17 00:00:00 2001 From: chcurran <82187351+carlitosan@users.noreply.github.com> Date: Mon, 26 Jul 2021 17:51:10 -0700 Subject: [PATCH 14/19] Add fix and unit tests for pure on graph start functions and direct entity id input Signed-off-by: chcurran <82187351+carlitosan@users.noreply.github.com> --- .../ScriptCanvas/Translation/GraphToLua.cpp | 3 +- ..._EntityIdInputForOnGraphStart.scriptcanvas | 1047 +++++++++++++++++ .../Tests/ScriptCanvas_RuntimeInterpreted.cpp | 5 + 3 files changed, 1054 insertions(+), 1 deletion(-) create mode 100644 Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_EntityIdInputForOnGraphStart.scriptcanvas diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.cpp index f957071bc1..9a1ef73126 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Translation/GraphToLua.cpp @@ -1943,7 +1943,8 @@ namespace ScriptCanvas { const auto requirement = ParseConstructionRequirement(variable); - if (requirement == Grammar::VariableConstructionRequirement::None || (requirement != Grammar::VariableConstructionRequirement::Static && !execution->IsStartCall())) + if (requirement == Grammar::VariableConstructionRequirement::None + || requirement != Grammar::VariableConstructionRequirement::Static && execution != m_model.GetStart()) { m_dotLua.WriteLineIndented("local %s = %s", variable->m_name.data(), ToValueString(variable->m_datum, m_configuration).data()); } diff --git a/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_EntityIdInputForOnGraphStart.scriptcanvas b/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_EntityIdInputForOnGraphStart.scriptcanvas new file mode 100644 index 0000000000..1614e30678 --- /dev/null +++ b/Gems/ScriptCanvasTesting/Assets/ScriptCanvas/UnitTests/LY_SC_UnitTest_EntityIdInputForOnGraphStart.scriptcanvas @@ -0,0 +1,1047 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_RuntimeInterpreted.cpp b/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_RuntimeInterpreted.cpp index 2a563b0904..df29a56fd5 100644 --- a/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_RuntimeInterpreted.cpp +++ b/Gems/ScriptCanvasTesting/Code/Tests/ScriptCanvas_RuntimeInterpreted.cpp @@ -89,6 +89,11 @@ TEST_F(ScriptCanvasTestFixture, ProveError) EXPECT_TRUE(false); } +TEST_F(ScriptCanvasTestFixture, EntityIdInputForOnGraphStart) +{ + RunUnitTestGraph("LY_SC_UnitTest_EntityIdInputForOnGraphStart"); +} + TEST_F(ScriptCanvasTestFixture, ParseErrorOnKnownNull) { ExpectParseError("LY_SC_UnitTest_ParseErrorOnKnownNull"); From 23fb27e2a4786cc00ce2c81d077ff216fbfb68d4 Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Mon, 26 Jul 2021 19:40:39 -0700 Subject: [PATCH 15/19] New AzNetworking metrics display plus connection quality debug widgets using ImGui Signed-off-by: kberg-amzn --- .../ConnectionLayer/ConnectionMetrics.cpp | 18 ++ .../ConnectionLayer/ConnectionMetrics.h | 28 +- .../ConnectionLayer/ConnectionMetrics.inl | 29 ++ .../ConnectionLayer/IConnection.h | 16 +- .../ConnectionLayer/IConnection.inl | 10 + .../TcpTransport/TcpConnection.cpp | 13 +- .../AzNetworking/TcpTransport/TcpConnection.h | 1 - .../UdpTransport/UdpConnection.cpp | 10 +- .../AzNetworking/UdpTransport/UdpConnection.h | 8 - .../UdpTransport/UdpConnection.inl | 10 - .../UdpTransport/UdpNetworkInterface.cpp | 3 +- .../AzNetworking/UdpTransport/UdpSocket.cpp | 8 +- .../Debug/MultiplayerDebugSystemComponent.cpp | 302 ++++++++++++------ 13 files changed, 310 insertions(+), 146 deletions(-) diff --git a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.cpp b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.cpp index cc0851e53d..3a3ab06d02 100644 --- a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.cpp @@ -22,6 +22,7 @@ namespace AzNetworking const AZ::TimeMs deltaTimeMs = currentTimeMs - m_lastLoggedTimeMs; m_atoms[m_activeAtom].m_bytesTransmitted += byteCount; + m_atoms[m_activeAtom].m_packetsSent++; m_atoms[m_activeAtom].m_timeAccumulatorMs += deltaTimeMs; if (m_atoms[m_activeAtom].m_timeAccumulatorMs >= m_maxSampleTimeMs) @@ -32,6 +33,11 @@ namespace AzNetworking m_lastLoggedTimeMs = currentTimeMs; } + void DatarateMetrics::LogPacketLost() + { + m_atoms[m_activeAtom].m_packetsLost++; + } + float DatarateMetrics::GetBytesPerSecond() const { const uint32_t sampleAtom = 1 - m_activeAtom; @@ -47,6 +53,18 @@ namespace AzNetworking return (bytesLogged * 1000.0f) / sampleTime; // (* 1000) to convert from bytes per millisecond to bytes per second } + float DatarateMetrics::GetLossRatePercent() const + { + const uint32_t sampleAtom = 1 - m_activeAtom; + + if (m_atoms[sampleAtom].m_packetsSent == 0) + { + return 0.0f; + } + + return float(m_atoms[sampleAtom].m_packetsLost) / float(m_atoms[sampleAtom].m_packetsSent); + } + void ConnectionComputeRtt::LogPacketSent(PacketId packetId, AZ::TimeMs currentTimeMs) { for (uint32_t i = 0; i < MaxTrackableEntries; i++) diff --git a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.h b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.h index c2227b07b6..b576c64e86 100644 --- a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.h +++ b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.h @@ -19,8 +19,10 @@ namespace AzNetworking { DatarateAtom() = default; + AZ::TimeMs m_timeAccumulatorMs = AZ::TimeMs{ 0 }; uint32_t m_bytesTransmitted = 0; - AZ::TimeMs m_timeAccumulatorMs = AZ::TimeMs{0}; + uint32_t m_packetsSent = 0; + uint32_t m_packetsLost = 0; }; //! @class DatarateMetrics @@ -40,19 +42,26 @@ namespace AzNetworking //! @param currentTimeMs current process time in milliseconds void LogPacket(uint32_t byteCount, AZ::TimeMs currentTimeMs); + //! Invoked whenever a packet has determined to be lost. + void LogPacketLost(); + //! Retrieve a sample of the datarate being incurred by this connection in bytes per second. //! @return datarate for traffic sent to or from the connection in bytes per second float GetBytesPerSecond() const; + //! Returns the estimated packet loss rate as a percentage of packets. + //! @return the estimated percentage loss rate + float GetLossRatePercent() const; + private: //! Used internally to swap buffers used for metric gathering. void SwapBuffers(); - static constexpr AZ::TimeMs MaxSampleTimeMs = AZ::TimeMs{500}; + static constexpr AZ::TimeMs MaxSampleTimeMs = AZ::TimeMs{ 2000 }; - AZ::TimeMs m_maxSampleTimeMs = MaxSampleTimeMs; - AZ::TimeMs m_lastLoggedTimeMs = MaxSampleTimeMs; + AZ::TimeMs m_maxSampleTimeMs = MaxSampleTimeMs; + AZ::TimeMs m_lastLoggedTimeMs = MaxSampleTimeMs; uint32_t m_activeAtom = 0; DatarateAtom m_atoms[2]; }; @@ -69,7 +78,7 @@ namespace AzNetworking ConnectionPacketEntry(PacketId packetId, AZ::TimeMs sendTimeMs); PacketId m_packetId = InvalidPacketId; - AZ::TimeMs m_sendTimeMs = AZ::TimeMs{0}; + AZ::TimeMs m_sendTimeMs = AZ::TimeMs{0}; }; //! @class ConnectionComputeRtt @@ -100,8 +109,8 @@ namespace AzNetworking private: - static constexpr uint32_t MaxTrackableEntries = 4; - static constexpr float InitialRoundTripTime = 0.1f; //< Start off with a 100 millisecond estimate for Rtt + static constexpr uint32_t MaxTrackableEntries = 8; + static constexpr float InitialRoundTripTime = 0.1f; //< Start off with a 100 millisecond estimate for Rtt float m_roundTripTime = InitialRoundTripTime; ConnectionPacketEntry m_entries[MaxTrackableEntries]; @@ -117,6 +126,11 @@ namespace AzNetworking //! Resets all internal metrics to defaults. void Reset(); + void LogPacketSent(uint32_t byteCount, AZ::TimeMs currentTimeMs); + void LogPacketRecv(uint32_t byteCount, AZ::TimeMs currentTimeMs); + void LogPacketLost(); + void LogPacketAcked(); + uint32_t m_packetsSent = 0; uint32_t m_packetsRecv = 0; uint32_t m_packetsLost = 0; diff --git a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.inl b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.inl index 5d7d18f709..5f196c4ed1 100644 --- a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.inl +++ b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/ConnectionMetrics.inl @@ -40,4 +40,33 @@ namespace AzNetworking { *this = ConnectionMetrics(); } + + inline void ConnectionMetrics::LogPacketSent(uint32_t byteCount, AZ::TimeMs currentTimeMs) + { + if (byteCount > 0) + { + m_packetsSent++; + } + m_sendDatarate.LogPacket(byteCount, currentTimeMs); + } + + inline void ConnectionMetrics::LogPacketRecv(uint32_t byteCount, AZ::TimeMs currentTimeMs) + { + if (byteCount > 0) + { + m_packetsRecv++; + } + m_recvDatarate.LogPacket(byteCount, currentTimeMs); + } + + inline void ConnectionMetrics::LogPacketLost() + { + m_packetsLost++; + m_sendDatarate.LogPacketLost(); + } + + inline void ConnectionMetrics::LogPacketAcked() + { + m_packetsAcked++; + } } diff --git a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.h b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.h index 1034f17585..363fd1d37b 100644 --- a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.h +++ b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.h @@ -95,11 +95,6 @@ namespace AzNetworking //! @return the max transmission unit for this connection virtual uint32_t GetConnectionMtu() const = 0; - //! Sets connection quality values for testing poor connection conditions. - //! Currently unsupported on TcpConnections - //! @param connectionQuality simulated connection quality values to use - virtual void SetConnectionQuality(const ConnectionQuality& connectionQuality) = 0; - //! Returns the connection identifier for this connection instance. //! @return the connection identifier for this connection instance ConnectionId GetConnectionId() const; @@ -128,12 +123,23 @@ namespace AzNetworking //! @return reference to the connection metric info ConnectionMetrics& GetMetrics(); + //! Retrieves debug connection quality settings. + //! Currently unsupported on TcpConnections + //! @return connection quality structure for this connection + const ConnectionQuality& GetConnectionQuality() const; + + //! Retrieves debug connection quality settings, non-const. + //! Currently unsupported on TcpConnections + //! @return connection quality structure for this connection + ConnectionQuality& GetConnectionQuality(); + private: // The following data members are here in the interface for performance reasons ConnectionId m_connectionId = InvalidConnectionId; IpAddress m_remoteAddress; ConnectionMetrics m_connectionMetrics; + ConnectionQuality m_connectionQuality; void* m_userData = nullptr; }; } diff --git a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.inl b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.inl index 646afac8f0..61e92010c7 100644 --- a/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.inl +++ b/Code/Framework/AzNetworking/AzNetworking/ConnectionLayer/IConnection.inl @@ -59,4 +59,14 @@ namespace AzNetworking { return m_connectionMetrics; } + + inline const ConnectionQuality& IConnection::GetConnectionQuality() const + { + return m_connectionQuality; + } + + inline ConnectionQuality& IConnection::GetConnectionQuality() + { + return m_connectionQuality; + } } diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp index ea746b9d00..1beb83e19f 100644 --- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp @@ -122,7 +122,7 @@ namespace AzNetworking bool TcpConnection::UpdateRecv() { const AZ::TimeMs startTimeMs = AZ::GetElapsedTimeMs(); - GetMetrics().m_recvDatarate.LogPacket(0, startTimeMs); + GetMetrics().LogPacketRecv(0, startTimeMs); // Read new data off the input socket { @@ -261,11 +261,6 @@ namespace AzNetworking return 0; // do nothing, unsupported on TCP connections } - void TcpConnection::SetConnectionQuality([[maybe_unused]] const ConnectionQuality& connectionQuality) - { - ; // do nothing, unsupported on TCP connections - } - bool TcpConnection::SendPacketInternal(PacketType packetType, TcpPacketEncodingBuffer& payloadBuffer, AZ::TimeMs currentTimeMs) { AZ_Assert(payloadBuffer.GetCapacity() < AZStd::numeric_limits::max(), "Buffer capacity should be representable using 2 bytes or less"); @@ -333,8 +328,7 @@ namespace AzNetworking } m_sendRingbuffer.AdvanceWriteBuffer(headerSize + payloadSize); - GetMetrics().m_packetsSent++; - GetMetrics().m_sendDatarate.LogPacket(headerSize + payloadSize, currentTimeMs); + GetMetrics().LogPacketSent(headerSize + payloadSize, currentTimeMs); m_networkInterface.GetMetrics().m_sendPackets++; UpdateSend(); return true; @@ -379,8 +373,7 @@ namespace AzNetworking memcpy(dstData, srcData, packetSize); m_recvRingbuffer.AdvanceReadBuffer(serializer.GetReadSize() + packetSize); - GetMetrics().m_packetsRecv++; - GetMetrics().m_recvDatarate.LogPacket(packetSize, currentTimeMs); + GetMetrics().LogPacketRecv(packetSize, currentTimeMs); m_networkInterface.GetMetrics().m_recvPackets++; return true; } diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h index af2c69292c..b769aea086 100644 --- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h +++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h @@ -102,7 +102,6 @@ namespace AzNetworking bool Disconnect(DisconnectReason reason, TerminationEndpoint endpoint) override; void SetConnectionMtu(uint32_t connectionMtu) override; uint32_t GetConnectionMtu() const override; - void SetConnectionQuality(const ConnectionQuality& connectionQuality) override; // @} //! Sets the registered socket file descriptor for this TcpConnection in the associated ConnectionSet instance. diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp index 798116d180..3efc6a51a8 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp @@ -152,7 +152,7 @@ namespace AzNetworking void UdpConnection::ProcessAcked(PacketId packetId, AZ::TimeMs currentTimeMs) { - GetMetrics().m_packetsAcked++; + GetMetrics().LogPacketAcked(); m_reliableQueue.OnPacketAcked(m_networkInterface, *this, packetId); // Compute Rtt adjustments @@ -172,8 +172,7 @@ namespace AzNetworking GetMetrics().m_connectionRtt.LogPacketSent(packetId, currentTimeMs); } - GetMetrics().m_packetsSent++; - GetMetrics().m_sendDatarate.LogPacket(packetSize, currentTimeMs); + GetMetrics().LogPacketSent(packetSize, currentTimeMs); m_lastSentPacketMs = currentTimeMs; m_unackedPacketCount = 0; } @@ -193,7 +192,7 @@ namespace AzNetworking return PacketTimeoutResult::Acked; case PacketAckState::Nacked: - GetMetrics().m_packetsLost++; + GetMetrics().LogPacketLost(); if (reliability == ReliabilityType::Reliable) { m_reliableQueue.OnPacketLost(m_networkInterface, *this, packetId); @@ -224,8 +223,7 @@ namespace AzNetworking return false; } - GetMetrics().m_packetsRecv++; - GetMetrics().m_recvDatarate.LogPacket(packetSize, currentTimeMs); + GetMetrics().LogPacketRecv(packetSize, currentTimeMs); if (header.GetIsReliable() && !m_reliableQueue.OnPacketReceived(header)) { diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h index c8e0c2cdc9..67d626d6cb 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h @@ -66,13 +66,8 @@ namespace AzNetworking bool Disconnect(DisconnectReason reason, TerminationEndpoint endpoint) override; void SetConnectionMtu(uint32_t connectionMtu) override; uint32_t GetConnectionMtu() const override; - void SetConnectionQuality(const ConnectionQuality& connectionQuality) override; // @} - //! Gets connection quality values for testing poor connection conditions. - //! @return connection quality values for this IConnection instance - const ConnectionQuality& GetConnectionQuality() const; - //! Returns a suitable encryption endpoint for this connection type. //! @return reference to the connections encryption endpoint DtlsEndpoint& GetDtlsEndpoint(); @@ -146,8 +141,6 @@ namespace AzNetworking UdpFragmentQueue m_fragmentQueue; ConnectionState m_state = ConnectionState::Disconnected; ConnectionRole m_connectionRole = ConnectionRole::Connector; - - ConnectionQuality m_connectionQuality; DtlsEndpoint m_dtlsEndpoint; AZ::TimeMs m_lastSentPacketMs; @@ -160,4 +153,3 @@ namespace AzNetworking } #include - diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.inl b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.inl index 1ab273d53d..b31595263d 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.inl +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.inl @@ -10,16 +10,6 @@ namespace AzNetworking { - inline void UdpConnection::SetConnectionQuality(const ConnectionQuality& connectionQuality) - { - m_connectionQuality = connectionQuality; - } - - inline const ConnectionQuality& UdpConnection::GetConnectionQuality() const - { - return m_connectionQuality; - } - inline DtlsEndpoint& UdpConnection::GetDtlsEndpoint() { return m_dtlsEndpoint; diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp index 1a29dd4cae..a3ddb856d2 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp @@ -224,8 +224,7 @@ namespace AzNetworking continue; } - connection->GetMetrics().m_recvDatarate.LogPacket(packet.m_receivedBytes + UdpPacketHeaderSize, currentTimeMs); - connection->GetMetrics().m_packetsRecv++; + connection->GetMetrics().LogPacketRecv(packet.m_receivedBytes + UdpPacketHeaderSize, currentTimeMs); // Decode the packet flag bitset first since it's always uncompressed UdpPacketHeader header; diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp index bb0b6d0fff..29a99f96a1 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp @@ -126,7 +126,7 @@ namespace AzNetworking #ifdef ENABLE_LATENCY_DEBUG if (connectionQuality.m_lossPercentage > 0) { - if (int32_t(m_random.GetRandom() % 100) < (connectionQuality.m_lossPercentage / 2)) + if (int32_t(m_random.GetRandom() % 100) < (connectionQuality.m_lossPercentage)) { // Pretend we sent, but don't actually send return true; @@ -157,9 +157,11 @@ namespace AzNetworking #ifdef ENABLE_LATENCY_DEBUG else if ((connectionQuality.m_latencyMs > AZ::TimeMs{ 0 }) || (connectionQuality.m_varianceMs > AZ::TimeMs{ 0 })) { - const AZ::TimeMs jitterMs = aznumeric_cast(m_random.GetRandom()) % (connectionQuality.m_varianceMs / aznumeric_cast(2)); + const AZ::TimeMs jitterMs = aznumeric_cast(m_random.GetRandom()) % (connectionQuality.m_varianceMs > AZ::TimeMs{ 0 } + ? connectionQuality.m_varianceMs + : AZ::TimeMs{ 1 }); const AZ::TimeMs currTimeMs = AZ::GetElapsedTimeMs(); - const AZ::TimeMs deferTimeMs = (connectionQuality.m_latencyMs / aznumeric_cast(2)) + jitterMs; + const AZ::TimeMs deferTimeMs = (connectionQuality.m_latencyMs) + jitterMs; DeferredData deferred = DeferredData(address, data, size, encrypt, dtlsEndpoint); AZ::Interface::Get()->AddCallback([&, deferredData = deferred] diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp index 1611a3213d..4d2d47444c 100644 --- a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp @@ -23,36 +23,30 @@ namespace Multiplayer ->Version(1); } } - void MultiplayerDebugSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { provided.push_back(AZ_CRC_CE("MultiplayerDebugSystemComponent")); } - void MultiplayerDebugSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) { ; } - void MultiplayerDebugSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatbile) { incompatbile.push_back(AZ_CRC_CE("MultiplayerDebugSystemComponent")); } - void MultiplayerDebugSystemComponent::Activate() { #ifdef IMGUI_ENABLED ImGui::ImGuiUpdateListenerBus::Handler::BusConnect(); #endif } - void MultiplayerDebugSystemComponent::Deactivate() { #ifdef IMGUI_ENABLED ImGui::ImGuiUpdateListenerBus::Handler::BusDisconnect(); #endif } - #ifdef IMGUI_ENABLED void MultiplayerDebugSystemComponent::OnImGuiMainMenuUpdate() { @@ -63,7 +57,6 @@ namespace Multiplayer ImGui::EndMenu(); } } - void AccumulatePerSecondValues(const MultiplayerStats& stats, const MultiplayerStats::Metric& metric, float& outCallsPerSecond, float& outBytesPerSecond) { uint64_t summedCalls = 0; @@ -80,8 +73,9 @@ namespace Multiplayer bool DrawMetricsRow(const char* name, bool expandable, uint64_t totalCalls, uint64_t totalBytes, float callsPerSecond, float bytesPerSecond) { - const ImGuiTreeNodeFlags flags = expandable ? ImGuiTreeNodeFlags_SpanFullWidth - : (ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth); + const ImGuiTreeNodeFlags flags = expandable + ? ImGuiTreeNodeFlags_SpanFullWidth + : (ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth); ImGui::TableNextRow(); ImGui::TableNextColumn(); const bool open = ImGui::TreeNodeEx(name, flags); @@ -95,14 +89,12 @@ namespace Multiplayer ImGui::Text("%11.2f", bytesPerSecond); return open; } - bool DrawSummaryRow(const char* name, const MultiplayerStats& stats) { const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateTotalPropertyUpdateSentMetrics(); const MultiplayerStats::Metric propertyUpdatesRecv = stats.CalculateTotalPropertyUpdateRecvMetrics(); const MultiplayerStats::Metric rpcsSent = stats.CalculateTotalRpcsSentMetrics(); const MultiplayerStats::Metric rpcsRecv = stats.CalculateTotalRpcsRecvMetrics(); - const uint64_t totalCalls = propertyUpdatesSent.m_totalCalls + propertyUpdatesRecv.m_totalCalls + rpcsSent.m_totalCalls + rpcsRecv.m_totalCalls; const uint64_t totalBytes = propertyUpdatesSent.m_totalBytes + propertyUpdatesRecv.m_totalBytes + rpcsSent.m_totalBytes + rpcsRecv.m_totalBytes; float callsPerSecond = 0.0f; @@ -111,17 +103,14 @@ namespace Multiplayer AccumulatePerSecondValues(stats, propertyUpdatesRecv, callsPerSecond, bytesPerSecond); AccumulatePerSecondValues(stats, rpcsSent, callsPerSecond, bytesPerSecond); AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond); - return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond); } - bool DrawComponentRow(const char* name, const MultiplayerStats& stats, NetComponentId netComponentId) { const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateComponentPropertyUpdateSentMetrics(netComponentId); const MultiplayerStats::Metric propertyUpdatesRecv = stats.CalculateComponentPropertyUpdateRecvMetrics(netComponentId); const MultiplayerStats::Metric rpcsSent = stats.CalculateComponentRpcsSentMetrics(netComponentId); const MultiplayerStats::Metric rpcsRecv = stats.CalculateComponentRpcsRecvMetrics(netComponentId); - const uint64_t totalCalls = propertyUpdatesSent.m_totalCalls + propertyUpdatesRecv.m_totalCalls + rpcsSent.m_totalCalls + rpcsRecv.m_totalCalls; const uint64_t totalBytes = propertyUpdatesSent.m_totalBytes + propertyUpdatesRecv.m_totalBytes + rpcsSent.m_totalBytes + rpcsRecv.m_totalBytes; float callsPerSecond = 0.0f; @@ -130,10 +119,8 @@ namespace Multiplayer AccumulatePerSecondValues(stats, propertyUpdatesRecv, callsPerSecond, bytesPerSecond); AccumulatePerSecondValues(stats, rpcsSent, callsPerSecond, bytesPerSecond); AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond); - return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond); } - void DrawComponentDetails(const MultiplayerStats& stats, NetComponentId netComponentId) { MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry(); @@ -158,7 +145,6 @@ namespace Multiplayer ImGui::TreePop(); } } - { const MultiplayerStats::Metric metric = stats.CalculateComponentPropertyUpdateRecvMetrics(netComponentId); float callsPerSecond = 0.0f; @@ -180,7 +166,6 @@ namespace Multiplayer ImGui::TreePop(); } } - { const MultiplayerStats::Metric metric = stats.CalculateComponentRpcsSentMetrics(netComponentId); float callsPerSecond = 0.0f; @@ -202,7 +187,6 @@ namespace Multiplayer ImGui::TreePop(); } } - { const MultiplayerStats::Metric metric = stats.CalculateComponentRpcsRecvMetrics(netComponentId); float callsPerSecond = 0.0f; @@ -226,45 +210,218 @@ namespace Multiplayer } } - void MultiplayerDebugSystemComponent::OnImGuiUpdate() + void DrawNetworkingStats() { const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x; const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing(); + const ImGuiTableFlags flags = ImGuiTableFlags_BordersV + | ImGuiTableFlags_BordersOuterH + | ImGuiTableFlags_Resizable + | ImGuiTableFlags_RowBg + | ImGuiTableFlags_NoBordersInBody; + + const ImGuiTreeNodeFlags nodeFlags = (ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth); + + AzNetworking::INetworking* networking = AZ::Interface::Get(); + + ImGui::Text("Total sockets monitored by TcpListenThread: %u", networking->GetTcpListenThreadSocketCount()); + ImGui::Text("Total time spent updating TcpListenThread: %lld", aznumeric_cast(networking->GetTcpListenThreadUpdateTime())); + ImGui::Text("Total sockets monitored by UdpReaderThread: %u", networking->GetUdpReaderThreadSocketCount()); + ImGui::Text("Total time spent updating UdpReaderThread: %lld", aznumeric_cast(networking->GetUdpReaderThreadUpdateTime())); + ImGui::NewLine(); + + for (auto& networkInterface : networking->GetNetworkInterfaces()) + { + if (ImGui::CollapsingHeader(networkInterface.second->GetName().GetCStr())) + { + const char* protocol = networkInterface.second->GetType() == AzNetworking::ProtocolType::Tcp ? "Tcp" : "Udp"; + const char* trustZone = networkInterface.second->GetTrustZone() == AzNetworking::TrustZone::ExternalClientToServer ? "ExternalClientToServer" : "InternalServerToServer"; + const uint32_t port = aznumeric_cast(networkInterface.second->GetPort()); + ImGui::Text("%sNetworkInterface open to %s on port %u", protocol, trustZone, port); + + if (ImGui::BeginTable("", 2, flags)) + { + const AzNetworking::NetworkInterfaceMetrics& metrics = networkInterface.second->GetMetrics(); + ImGui::TableSetupColumn("Stat", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); + ImGui::TableHeadersRow(); + ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Text("Total time spent updating (ms)"); + ImGui::TableNextColumn(); + ImGui::Text("%lld", aznumeric_cast(metrics.m_updateTimeMs)); + ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Text("Total number of connections"); + ImGui::TableNextColumn(); + ImGui::Text("%llu", aznumeric_cast(metrics.m_connectionCount)); + ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Text("Total send time (ms)"); + ImGui::TableNextColumn(); + ImGui::Text("%lld", aznumeric_cast(metrics.m_sendTimeMs)); + ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Text("Total sent packets"); + ImGui::TableNextColumn(); + ImGui::Text("%llu", aznumeric_cast(metrics.m_sendPackets)); + ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Text("Total sent bytes after compression"); + ImGui::TableNextColumn(); + ImGui::Text("%llu", aznumeric_cast(metrics.m_sendBytes)); + ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Text("Total sent bytes before compression"); + ImGui::TableNextColumn(); + ImGui::Text("%llu", aznumeric_cast(metrics.m_sendBytesUncompressed)); + ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Text("Total sent compressed packets without benefit"); + ImGui::TableNextColumn(); + ImGui::Text("%llu", aznumeric_cast(metrics.m_sendCompressedPacketsNoGain)); + ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Text("Total gain from packet compression"); + ImGui::TableNextColumn(); + ImGui::Text("%lld", aznumeric_cast(metrics.m_sendBytesCompressedDelta)); + ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Text("Total packets resent"); + ImGui::TableNextColumn(); + ImGui::Text("%llu", aznumeric_cast(metrics.m_resentPackets)); + ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Text("Total receive time (ms)"); + ImGui::TableNextColumn(); + ImGui::Text("%lld", aznumeric_cast(metrics.m_recvTimeMs)); + ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Text("Total received packets"); + ImGui::TableNextColumn(); + ImGui::Text("%llu", aznumeric_cast(metrics.m_recvPackets)); + ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Text("Total received bytes after compression"); + ImGui::TableNextColumn(); + ImGui::Text("%llu", aznumeric_cast(metrics.m_recvBytes)); + ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Text("Total received bytes before compression"); + ImGui::TableNextColumn(); + ImGui::Text("%llu", aznumeric_cast(metrics.m_recvBytesUncompressed)); + ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Text("Total packets discarded due to load"); + ImGui::TableNextColumn(); + ImGui::Text("%llu", aznumeric_cast(metrics.m_discardedPackets)); + ImGui::EndTable(); + } + + if (ImGui::BeginTable("", 7, flags)) + { + // The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On + ImGui::TableSetupColumn("RemoteAddr", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("Conn. Id", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 6.0f); + ImGui::TableSetupColumn("Send (Bps)", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 10.0f); + ImGui::TableSetupColumn("Recv (Bps)", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 10.0f); + ImGui::TableSetupColumn("RTT (ms)", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 8.0f); + ImGui::TableSetupColumn("% Lost", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 8.0f); + ImGui::TableSetupColumn("Debug Settings", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 32.0f); + ImGui::TableHeadersRow(); + + auto displayConnectionRow = [](AzNetworking::IConnection& connection) + { + ImGui::PushID(&connection); + + const AzNetworking::ConnectionMetrics& metrics = connection.GetMetrics(); + const AzNetworking::IpAddress::IpString remoteAddr = connection.GetRemoteAddress().GetString(); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TreeNodeEx(remoteAddr.c_str(), nodeFlags); + ImGui::TableNextColumn(); + ImGui::Text("%5llu", aznumeric_cast(connection.GetConnectionId())); + ImGui::TableNextColumn(); + ImGui::Text("%9.2f", metrics.m_sendDatarate.GetBytesPerSecond()); + ImGui::TableNextColumn(); + ImGui::Text("%9.2f", metrics.m_recvDatarate.GetBytesPerSecond()); + ImGui::TableNextColumn(); + ImGui::Text("%7.2f", metrics.m_connectionRtt.GetRoundTripTimeSeconds() * 1000.0f); + ImGui::TableNextColumn(); + ImGui::Text("%7.2f", metrics.m_sendDatarate.GetLossRatePercent()); + ImGui::TableNextColumn(); + + { + AzNetworking::ConnectionQuality& quality = connection.GetConnectionQuality(); + int32_t latency = aznumeric_cast(quality.m_latencyMs); + int32_t variance = aznumeric_cast(quality.m_varianceMs); + ImGui::SliderInt("Loss %", &quality.m_lossPercentage, 0, 100); + if (ImGui::SliderInt("Latency(ms)", &latency, 0, 3000)) + { + quality.m_latencyMs = AZ::TimeMs{ latency }; + } + if (ImGui::SliderInt("Jitter(ms)", &variance, 0, 1000)) + { + quality.m_varianceMs = AZ::TimeMs{ variance }; + } + } + ImGui::PopID(); + }; + networkInterface.second->GetConnectionSet().VisitConnections(displayConnectionRow); + ImGui::EndTable(); + } + } + ImGui::NewLine(); + } + ImGui::End(); + } + + void DrawMultiplayerStats() + { + const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x; + const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing(); + + IMultiplayer* multiplayer = AZ::Interface::Get(); + MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry(); + const Multiplayer::MultiplayerStats& stats = multiplayer->GetStats(); + ImGui::Text("Multiplayer operating in %s mode", GetEnumString(multiplayer->GetAgentType())); + ImGui::Text("Total networked entities: %llu", aznumeric_cast(stats.m_entityCount)); + ImGui::Text("Total client connections: %llu", aznumeric_cast(stats.m_clientConnectionCount)); + ImGui::Text("Total server connections: %llu", aznumeric_cast(stats.m_serverConnectionCount)); + ImGui::NewLine(); + + static ImGuiTableFlags flags = ImGuiTableFlags_BordersV + | ImGuiTableFlags_BordersOuterH + | ImGuiTableFlags_Resizable + | ImGuiTableFlags_RowBg + | ImGuiTableFlags_NoBordersInBody; + + if (ImGui::BeginTable("", 5, flags)) + { + // The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On + ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("Total Calls", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); + ImGui::TableSetupColumn("Total Bytes", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); + ImGui::TableSetupColumn("Calls/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); + ImGui::TableSetupColumn("Bytes/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); + ImGui::TableHeadersRow(); + + if (DrawSummaryRow("Totals", stats)) + { + for (AZStd::size_t index = 0; index < stats.m_componentStats.size(); ++index) + { + const NetComponentId netComponentId = aznumeric_cast(index); + using StringLabel = AZStd::fixed_string<128>; + const StringLabel gemName = componentRegistry->GetComponentGemName(netComponentId); + const StringLabel componentName = componentRegistry->GetComponentName(netComponentId); + const StringLabel label = gemName + "::" + componentName; + if (DrawComponentRow(label.c_str(), stats, netComponentId)) + { + DrawComponentDetails(stats, netComponentId); + ImGui::TreePop(); + } + } + } + ImGui::EndTable(); + ImGui::NewLine(); + } + ImGui::End(); + } + + void MultiplayerDebugSystemComponent::OnImGuiUpdate() + { if (m_displayNetworkingStats) { if (ImGui::Begin("Networking Stats", &m_displayNetworkingStats, ImGuiWindowFlags_None)) { - AzNetworking::INetworking* networking = AZ::Interface::Get(); - - ImGui::Text("Total sockets monitored by TcpListenThread: %u", networking->GetTcpListenThreadSocketCount()); - ImGui::Text("Total time spent updating TcpListenThread: %lld", aznumeric_cast(networking->GetTcpListenThreadUpdateTime())); - ImGui::Text("Total sockets monitored by UdpReaderThread: %u", networking->GetUdpReaderThreadSocketCount()); - ImGui::Text("Total time spent updating UdpReaderThread: %lld", aznumeric_cast(networking->GetUdpReaderThreadUpdateTime())); - - for (auto& networkInterface : networking->GetNetworkInterfaces()) - { - const char* protocol = networkInterface.second->GetType() == AzNetworking::ProtocolType::Tcp ? "Tcp" : "Udp"; - const char* trustZone = networkInterface.second->GetTrustZone() == AzNetworking::TrustZone::ExternalClientToServer ? "ExternalClientToServer" : "InternalServerToServer"; - const uint32_t port = aznumeric_cast(networkInterface.second->GetPort()); - ImGui::Text("%sNetworkInterface: %s - open to %s on port %u", protocol, networkInterface.second->GetName().GetCStr(), trustZone, port); - - const AzNetworking::NetworkInterfaceMetrics& metrics = networkInterface.second->GetMetrics(); - ImGui::Text(" - Total time spent updating in milliseconds: %lld", aznumeric_cast(metrics.m_updateTimeMs)); - ImGui::Text(" - Total number of connections: %llu", aznumeric_cast(metrics.m_connectionCount)); - ImGui::Text(" - Total send time in milliseconds: %lld", aznumeric_cast(metrics.m_sendTimeMs)); - ImGui::Text(" - Total sent packets: %llu", aznumeric_cast(metrics.m_sendPackets)); - ImGui::Text(" - Total sent bytes after compression: %llu", aznumeric_cast(metrics.m_sendBytes)); - ImGui::Text(" - Total sent bytes before compression: %llu", aznumeric_cast(metrics.m_sendBytesUncompressed)); - ImGui::Text(" - Total sent compressed packets without benefit: %llu", aznumeric_cast(metrics.m_sendCompressedPacketsNoGain)); - ImGui::Text(" - Total gain from packet compression: %lld", aznumeric_cast(metrics.m_sendBytesCompressedDelta)); - ImGui::Text(" - Total packets resent: %llu", aznumeric_cast(metrics.m_resentPackets)); - ImGui::Text(" - Total receive time in milliseconds: %lld", aznumeric_cast(metrics.m_recvTimeMs)); - ImGui::Text(" - Total received packets: %llu", aznumeric_cast(metrics.m_recvPackets)); - ImGui::Text(" - Total received bytes after compression: %llu", aznumeric_cast(metrics.m_recvBytes)); - ImGui::Text(" - Total received bytes before compression: %llu", aznumeric_cast(metrics.m_recvBytesUncompressed)); - ImGui::Text(" - Total packets discarded due to load: %llu", aznumeric_cast(metrics.m_discardedPackets)); - } + DrawNetworkingStats(); } } @@ -272,50 +429,7 @@ namespace Multiplayer { if (ImGui::Begin("Multiplayer Stats", &m_displayMultiplayerStats, ImGuiWindowFlags_None)) { - IMultiplayer* multiplayer = AZ::Interface::Get(); - MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry(); - const Multiplayer::MultiplayerStats& stats = multiplayer->GetStats(); - ImGui::Text("Multiplayer operating in %s mode", GetEnumString(multiplayer->GetAgentType())); - ImGui::Text("Total networked entities: %llu", aznumeric_cast(stats.m_entityCount)); - ImGui::Text("Total client connections: %llu", aznumeric_cast(stats.m_clientConnectionCount)); - ImGui::Text("Total server connections: %llu", aznumeric_cast(stats.m_serverConnectionCount)); - ImGui::NewLine(); - - static ImGuiTableFlags flags = ImGuiTableFlags_BordersV - | ImGuiTableFlags_BordersOuterH - | ImGuiTableFlags_Resizable - | ImGuiTableFlags_RowBg - | ImGuiTableFlags_NoBordersInBody; - - if (ImGui::BeginTable("", 5, flags)) - { - // The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On - ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("Total Calls", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); - ImGui::TableSetupColumn("Total Bytes", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); - ImGui::TableSetupColumn("Calls/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); - ImGui::TableSetupColumn("Bytes/Sec", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); - ImGui::TableHeadersRow(); - - if (DrawSummaryRow("Totals", stats)) - { - for (AZStd::size_t index = 0; index < stats.m_componentStats.size(); ++index) - { - const NetComponentId netComponentId = aznumeric_cast(index); - using StringLabel = AZStd::fixed_string<128>; - const StringLabel gemName = componentRegistry->GetComponentGemName(netComponentId); - const StringLabel componentName = componentRegistry->GetComponentName(netComponentId); - const StringLabel label = gemName + "::" + componentName; - if (DrawComponentRow(label.c_str(), stats, netComponentId)) - { - DrawComponentDetails(stats, netComponentId); - ImGui::TreePop(); - } - } - } - ImGui::EndTable(); - } - ImGui::End(); + DrawMultiplayerStats(); } } } From a3712b5564e72bdab9d2e273ddae9a2cf33f88cd Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Tue, 27 Jul 2021 00:15:26 -0700 Subject: [PATCH 16/19] TSpace setting for MikkT tangent generation (#2386) * Added TSpace method setting which is only visible for MikkT generation. * Fixed a bug with generating tangents for blend shapes. * Renamed tangent space into generation method. * Some code cleaning Signed-off-by: Benjamin Jillich --- .../AssImpBitangentStreamImporter.cpp | 2 +- .../Importers/AssImpTangentStreamImporter.cpp | 2 +- .../GraphData/IMeshVertexBitangentData.h | 40 ++--- .../GraphData/IMeshVertexTangentData.h | 58 +++--- .../GraphData/MeshVertexBitangentData.cpp | 167 ++++++++--------- .../GraphData/MeshVertexBitangentData.h | 60 +++---- .../GraphData/MeshVertexTangentData.cpp | 170 ++++++++---------- .../GraphData/MeshVertexTangentData.h | 57 +++--- .../SceneAPI/SceneData/Rules/TangentsRule.cpp | 40 +++-- .../SceneAPI/SceneData/Rules/TangentsRule.h | 9 +- .../GraphData/GraphDataBehaviorTests.cpp | 8 +- .../TangentGenerateComponent.cpp | 50 +++--- .../TangentGenerateComponent.h | 7 +- .../BlendShapeMikkTGenerator.cpp | 56 ++++-- .../BlendShapeMikkTGenerator.h | 5 +- .../TangentGenerators/MikkTGenerator.cpp | 21 ++- .../TangentGenerators/MikkTGenerator.h | 4 +- 17 files changed, 385 insertions(+), 371 deletions(-) diff --git a/Code/Tools/SceneAPI/SceneBuilder/Importers/AssImpBitangentStreamImporter.cpp b/Code/Tools/SceneAPI/SceneBuilder/Importers/AssImpBitangentStreamImporter.cpp index 8379f4be7d..51e0147599 100644 --- a/Code/Tools/SceneAPI/SceneBuilder/Importers/AssImpBitangentStreamImporter.cpp +++ b/Code/Tools/SceneAPI/SceneBuilder/Importers/AssImpBitangentStreamImporter.cpp @@ -87,7 +87,7 @@ namespace AZ // AssImp only has one bitangentStream per mesh. bitangentStream->SetBitangentSetIndex(0); - bitangentStream->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene); + bitangentStream->SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene); bitangentStream->ReserveContainerSpace(vertexCount); for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex) { diff --git a/Code/Tools/SceneAPI/SceneBuilder/Importers/AssImpTangentStreamImporter.cpp b/Code/Tools/SceneAPI/SceneBuilder/Importers/AssImpTangentStreamImporter.cpp index 8a1079b0d2..6f1c364399 100644 --- a/Code/Tools/SceneAPI/SceneBuilder/Importers/AssImpTangentStreamImporter.cpp +++ b/Code/Tools/SceneAPI/SceneBuilder/Importers/AssImpTangentStreamImporter.cpp @@ -89,7 +89,7 @@ namespace AZ // AssImp only has one tangentStream per mesh. tangentStream->SetTangentSetIndex(0); - tangentStream->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene); + tangentStream->SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene); tangentStream->ReserveContainerSpace(vertexCount); for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex) { diff --git a/Code/Tools/SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexBitangentData.h b/Code/Tools/SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexBitangentData.h index d05042c534..027459460c 100644 --- a/Code/Tools/SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexBitangentData.h +++ b/Code/Tools/SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexBitangentData.h @@ -17,32 +17,24 @@ namespace AZ class Vector3; } -namespace AZ +namespace AZ::SceneAPI::DataTypes { - namespace SceneAPI + class IMeshVertexBitangentData + : public IGraphObject { - namespace DataTypes - { + public: + AZ_RTTI(IMeshVertexBitangentData, "{6C8F6109-B0BD-49D1-A998-4A4946557DF9}", IGraphObject); - class IMeshVertexBitangentData - : public IGraphObject - { - public: - AZ_RTTI(IMeshVertexBitangentData, "{6C8F6109-B0BD-49D1-A998-4A4946557DF9}", IGraphObject); + virtual ~IMeshVertexBitangentData() override = default; - virtual ~IMeshVertexBitangentData() override = default; + void CloneAttributesFrom([[maybe_unused]] const IGraphObject* sourceObject) override {} - void CloneAttributesFrom([[maybe_unused]] const IGraphObject* sourceObject) override {} - - virtual size_t GetCount() const = 0; - virtual const AZ::Vector3& GetBitangent(size_t index) const = 0; - virtual void SetBitangent(size_t vertexIndex, const AZ::Vector3& bitangent) = 0; - virtual void SetBitangentSetIndex(size_t setIndex) = 0; - virtual size_t GetBitangentSetIndex() const = 0; - virtual TangentSpace GetTangentSpace() const = 0; - virtual void SetTangentSpace(TangentSpace space) = 0; - }; - - } // DataTypes - } // SceneAPI -} // AZ + virtual size_t GetCount() const = 0; + virtual const AZ::Vector3& GetBitangent(size_t index) const = 0; + virtual void SetBitangent(size_t vertexIndex, const AZ::Vector3& bitangent) = 0; + virtual void SetBitangentSetIndex(size_t setIndex) = 0; + virtual size_t GetBitangentSetIndex() const = 0; + virtual TangentGenerationMethod GetGenerationMethod() const = 0; + virtual void SetGenerationMethod(TangentGenerationMethod method) = 0; + }; +} // AZ::SceneAPI::DataTypes diff --git a/Code/Tools/SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexTangentData.h b/Code/Tools/SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexTangentData.h index ffeeedf9fe..a51999c4b1 100644 --- a/Code/Tools/SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexTangentData.h +++ b/Code/Tools/SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexTangentData.h @@ -16,42 +16,36 @@ namespace AZ class Vector4; } -namespace AZ +namespace AZ::SceneAPI::DataTypes { - namespace SceneAPI + enum class TangentGenerationMethod { - namespace DataTypes - { - enum class TangentSpace - { - FromSourceScene = 0, - MikkT = 1 - }; + FromSourceScene = 0, + MikkT = 1 + }; - enum class BitangentMethod - { - UseFromTangentSpace = 0, - Orthogonal = 1 - }; + enum class MikkTSpaceMethod + { + TSpace = 0, + TSpaceBasic = 1 + }; - class IMeshVertexTangentData - : public IGraphObject - { - public: - AZ_RTTI(IMeshVertexTangentData, "{B24084FF-09B1-4EE5-BA5B-2D392E92ECC1}", IGraphObject); + class IMeshVertexTangentData + : public IGraphObject + { + public: + AZ_RTTI(IMeshVertexTangentData, "{B24084FF-09B1-4EE5-BA5B-2D392E92ECC1}", IGraphObject); - virtual ~IMeshVertexTangentData() override = default; + virtual ~IMeshVertexTangentData() override = default; - void CloneAttributesFrom([[maybe_unused]] const IGraphObject* sourceObject) override {} + void CloneAttributesFrom([[maybe_unused]] const IGraphObject* sourceObject) override {} - virtual size_t GetCount() const = 0; - virtual const AZ::Vector4& GetTangent(size_t index) const = 0; - virtual void SetTangent(size_t vertexIndex, const AZ::Vector4& tangent) = 0; - virtual void SetTangentSetIndex(size_t setIndex) = 0; - virtual size_t GetTangentSetIndex() const = 0; - virtual TangentSpace GetTangentSpace() const = 0; - virtual void SetTangentSpace(TangentSpace space) = 0; - }; - } // DataTypes - } // SceneAPI -} // AZ + virtual size_t GetCount() const = 0; + virtual const AZ::Vector4& GetTangent(size_t index) const = 0; + virtual void SetTangent(size_t vertexIndex, const AZ::Vector4& tangent) = 0; + virtual void SetTangentSetIndex(size_t setIndex) = 0; + virtual size_t GetTangentSetIndex() const = 0; + virtual TangentGenerationMethod GetGenerationMethod() const = 0; + virtual void SetGenerationMethod(TangentGenerationMethod method) = 0; + }; +} // AZ::SceneAPI::DataTypes diff --git a/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexBitangentData.cpp b/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexBitangentData.cpp index faa9bf457e..efd020f80a 100644 --- a/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexBitangentData.cpp +++ b/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexBitangentData.cpp @@ -10,110 +10,95 @@ #include #include -namespace AZ +namespace AZ::SceneData::GraphData { - namespace SceneData + void MeshVertexBitangentData::Reflect(ReflectContext* context) { - namespace GraphData + SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) { - void MeshVertexBitangentData::Reflect(ReflectContext* context) - { - SerializeContext* serializeContext = azrtti_cast(context); - if (serializeContext) - { - serializeContext->Class()->Version(2); - } + serializeContext->Class()->Version(2); + } - BehaviorContext* behaviorContext = azrtti_cast(context); - if (behaviorContext) - { - behaviorContext->Class() - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) - ->Attribute(AZ::Script::Attributes::Module, "scene") - ->Method("GetCount", &MeshVertexBitangentData::GetCount) - ->Method("GetBitangent", &MeshVertexBitangentData::GetBitangent) - ->Method("GetBitangentSetIndex", &MeshVertexBitangentData::GetBitangentSetIndex) - ->Method("GetTangentSpace", &MeshVertexBitangentData::GetTangentSpace) - ->Enum<(int)SceneAPI::DataTypes::TangentSpace::FromSourceScene>("FromSourceScene") - ->Enum<(int)SceneAPI::DataTypes::TangentSpace::MikkT>("MikkT"); - } - } + BehaviorContext* behaviorContext = azrtti_cast(context); + if (behaviorContext) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "scene") + ->Method("GetCount", &MeshVertexBitangentData::GetCount) + ->Method("GetBitangent", &MeshVertexBitangentData::GetBitangent) + ->Method("GetBitangentSetIndex", &MeshVertexBitangentData::GetBitangentSetIndex) + ->Method("GetGenerationMethod", &MeshVertexBitangentData::GetGenerationMethod) + ->Enum<(int)SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene>("FromSourceScene") + ->Enum<(int)SceneAPI::DataTypes::TangentGenerationMethod::MikkT>("MikkT"); + } + } - void MeshVertexBitangentData::CloneAttributesFrom(const IGraphObject* sourceObject) - { - IMeshVertexBitangentData::CloneAttributesFrom(sourceObject); - if (const auto* typedSource = azrtti_cast(sourceObject)) - { - SetTangentSpace(typedSource->GetTangentSpace()); - SetBitangentSetIndex(typedSource->GetBitangentSetIndex()); - } - } + void MeshVertexBitangentData::CloneAttributesFrom(const IGraphObject* sourceObject) + { + IMeshVertexBitangentData::CloneAttributesFrom(sourceObject); + if (const auto* typedSource = azrtti_cast(sourceObject)) + { + SetGenerationMethod(typedSource->GetGenerationMethod()); + SetBitangentSetIndex(typedSource->GetBitangentSetIndex()); + } + } - size_t MeshVertexBitangentData::GetCount() const - { - return m_bitangents.size(); - } + size_t MeshVertexBitangentData::GetCount() const + { + return m_bitangents.size(); + } + const AZ::Vector3& MeshVertexBitangentData::GetBitangent(size_t index) const + { + AZ_Assert(index < m_bitangents.size(), "Invalid index %i for mesh bitangents.", index); + return m_bitangents[index]; + } - const AZ::Vector3& MeshVertexBitangentData::GetBitangent(size_t index) const - { - AZ_Assert(index < m_bitangents.size(), "Invalid index %i for mesh bitangents.", index); - return m_bitangents[index]; - } + void MeshVertexBitangentData::ReserveContainerSpace(size_t numVerts) + { + m_bitangents.reserve(numVerts); + } + void MeshVertexBitangentData::Resize(size_t numVerts) + { + m_bitangents.resize(numVerts); + } - void MeshVertexBitangentData::ReserveContainerSpace(size_t numVerts) - { - m_bitangents.reserve(numVerts); - } + void MeshVertexBitangentData::AppendBitangent(const AZ::Vector3& bitangent) + { + m_bitangents.push_back(bitangent); + } + void MeshVertexBitangentData::SetBitangent(size_t vertexIndex, const AZ::Vector3& bitangent) + { + m_bitangents[vertexIndex] = bitangent; + } - void MeshVertexBitangentData::Resize(size_t numVerts) - { - m_bitangents.resize(numVerts); - } + void MeshVertexBitangentData::SetBitangentSetIndex(size_t setIndex) + { + m_setIndex = setIndex; + } + size_t MeshVertexBitangentData::GetBitangentSetIndex() const + { + return m_setIndex; + } - void MeshVertexBitangentData::AppendBitangent(const AZ::Vector3& bitangent) - { - m_bitangents.push_back(bitangent); - } + AZ::SceneAPI::DataTypes::TangentGenerationMethod MeshVertexBitangentData::GetGenerationMethod() const + { + return m_generationMethod; + } + void MeshVertexBitangentData::SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod method) + { + m_generationMethod = method; + } - void MeshVertexBitangentData::SetBitangent(size_t vertexIndex, const AZ::Vector3& bitangent) - { - m_bitangents[vertexIndex] = bitangent; - } - - - void MeshVertexBitangentData::SetBitangentSetIndex(size_t setIndex) - { - m_setIndex = setIndex; - } - - - size_t MeshVertexBitangentData::GetBitangentSetIndex() const - { - return m_setIndex; - } - - - AZ::SceneAPI::DataTypes::TangentSpace MeshVertexBitangentData::GetTangentSpace() const - { - return m_tangentSpace; - } - - - void MeshVertexBitangentData::SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace space) - { - m_tangentSpace = space; - } - - void MeshVertexBitangentData::GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const - { - output.Write("Bitangents", m_bitangents); - output.Write("TangentSpace", aznumeric_cast(m_tangentSpace)); - } - } // GraphData - } // SceneData -} // AZ + void MeshVertexBitangentData::GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const + { + output.Write("Bitangents", m_bitangents); + output.Write("GenerationMethod", aznumeric_cast(m_generationMethod)); + } +} // AZ::SceneData::GraphData diff --git a/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexBitangentData.h b/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexBitangentData.h index 9afb174f17..151f4c963b 100644 --- a/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexBitangentData.h +++ b/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexBitangentData.h @@ -10,51 +10,41 @@ #include #include - #include #include - -namespace AZ +namespace AZ::SceneData::GraphData { - namespace SceneData + class SCENE_DATA_CLASS MeshVertexBitangentData + : public AZ::SceneAPI::DataTypes::IMeshVertexBitangentData { - namespace GraphData - { + public: + AZ_RTTI(MeshVertexBitangentData, "{F56FB088-4C92-4453-AFE9-4E820F03FA90}", AZ::SceneAPI::DataTypes::IMeshVertexBitangentData); - class SCENE_DATA_CLASS MeshVertexBitangentData - : public AZ::SceneAPI::DataTypes::IMeshVertexBitangentData - { - public: - AZ_RTTI(MeshVertexBitangentData, "{F56FB088-4C92-4453-AFE9-4E820F03FA90}", AZ::SceneAPI::DataTypes::IMeshVertexBitangentData); + static void Reflect(ReflectContext* context); - static void Reflect(ReflectContext* context); + SCENE_DATA_API ~MeshVertexBitangentData() override = default; - SCENE_DATA_API ~MeshVertexBitangentData() override = default; + SCENE_DATA_API void CloneAttributesFrom(const IGraphObject* sourceObject) override; - SCENE_DATA_API void CloneAttributesFrom(const IGraphObject* sourceObject) override; + SCENE_DATA_API size_t GetCount() const override; + SCENE_DATA_API const AZ::Vector3& GetBitangent(size_t index) const override; + SCENE_DATA_API void SetBitangent(size_t vertexIndex, const AZ::Vector3& bitangent) override; - SCENE_DATA_API size_t GetCount() const override; - SCENE_DATA_API const AZ::Vector3& GetBitangent(size_t index) const override; - SCENE_DATA_API void SetBitangent(size_t vertexIndex, const AZ::Vector3& bitangent) override; + SCENE_DATA_API void SetBitangentSetIndex(size_t setIndex) override; + SCENE_DATA_API size_t GetBitangentSetIndex() const override; - SCENE_DATA_API void SetBitangentSetIndex(size_t setIndex) override; - SCENE_DATA_API size_t GetBitangentSetIndex() const override; + SCENE_DATA_API void Resize(size_t numVerts); + SCENE_DATA_API void ReserveContainerSpace(size_t numVerts); + SCENE_DATA_API void AppendBitangent(const AZ::Vector3& bitangent); - SCENE_DATA_API void Resize(size_t numVerts); - SCENE_DATA_API void ReserveContainerSpace(size_t numVerts); - SCENE_DATA_API void AppendBitangent(const AZ::Vector3& bitangent); + SCENE_DATA_API AZ::SceneAPI::DataTypes::TangentGenerationMethod GetGenerationMethod() const override; + SCENE_DATA_API void SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod method) override; - SCENE_DATA_API AZ::SceneAPI::DataTypes::TangentSpace GetTangentSpace() const override; - SCENE_DATA_API void SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace space) override; - - SCENE_DATA_API void GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const override; - protected: - AZStd::vector m_bitangents; - AZ::SceneAPI::DataTypes::TangentSpace m_tangentSpace = AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene; - size_t m_setIndex = 0; - }; - - } // GraphData - } // SceneData -} // AZ + SCENE_DATA_API void GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const override; + protected: + AZStd::vector m_bitangents; + AZ::SceneAPI::DataTypes::TangentGenerationMethod m_generationMethod = AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene; + size_t m_setIndex = 0; + }; +} // AZ::SceneData::GraphData diff --git a/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexTangentData.cpp b/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexTangentData.cpp index 9f27f4eb44..31ae04b19e 100644 --- a/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexTangentData.cpp +++ b/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexTangentData.cpp @@ -10,112 +10,96 @@ #include #include -namespace AZ +namespace AZ::SceneData::GraphData { - namespace SceneData + void MeshVertexTangentData::Reflect(ReflectContext* context) { - namespace GraphData + SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) { - void MeshVertexTangentData::Reflect(ReflectContext* context) - { - SerializeContext* serializeContext = azrtti_cast(context); - if (serializeContext) - { - serializeContext->Class()->Version(2); - } + serializeContext->Class()->Version(2); + } - BehaviorContext* behaviorContext = azrtti_cast(context); - if (behaviorContext) - { - behaviorContext->Class() - ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) - ->Attribute(AZ::Script::Attributes::Module, "scene") - ->Method("GetCount", &MeshVertexTangentData::GetCount) - ->Method("GetTangent", &MeshVertexTangentData::GetTangent) - ->Method("GetTangentSetIndex", &MeshVertexTangentData::GetTangentSetIndex) - ->Method("GetTangentSpace", &MeshVertexTangentData::GetTangentSpace) - ->Enum<(int)SceneAPI::DataTypes::TangentSpace::FromSourceScene>("FromSourceScene") - ->Enum<(int)SceneAPI::DataTypes::TangentSpace::MikkT>("MikkT"); - } - } + BehaviorContext* behaviorContext = azrtti_cast(context); + if (behaviorContext) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "scene") + ->Method("GetCount", &MeshVertexTangentData::GetCount) + ->Method("GetTangent", &MeshVertexTangentData::GetTangent) + ->Method("GetTangentSetIndex", &MeshVertexTangentData::GetTangentSetIndex) + ->Method("GetGenerationMethod", &MeshVertexTangentData::GetGenerationMethod) + ->Enum<(int)SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene>("FromSourceScene") + ->Enum<(int)SceneAPI::DataTypes::TangentGenerationMethod::MikkT>("MikkT"); + } + } - void MeshVertexTangentData::CloneAttributesFrom(const IGraphObject* sourceObject) - { - IMeshVertexTangentData::CloneAttributesFrom(sourceObject); - if (const auto* typedSource = azrtti_cast(sourceObject)) - { - SetTangentSpace(typedSource->GetTangentSpace()); - SetTangentSetIndex(typedSource->GetTangentSetIndex()); - } - } + void MeshVertexTangentData::CloneAttributesFrom(const IGraphObject* sourceObject) + { + IMeshVertexTangentData::CloneAttributesFrom(sourceObject); + if (const auto* typedSource = azrtti_cast(sourceObject)) + { + SetGenerationMethod(typedSource->GetGenerationMethod()); + SetTangentSetIndex(typedSource->GetTangentSetIndex()); + } + } - size_t MeshVertexTangentData::GetCount() const - { - return m_tangents.size(); - } + size_t MeshVertexTangentData::GetCount() const + { + return m_tangents.size(); + } + const AZ::Vector4& MeshVertexTangentData::GetTangent(size_t index) const + { + AZ_Assert(index < m_tangents.size(), "Invalid index %i for mesh tangents.", index); + return m_tangents[index]; + } - const AZ::Vector4& MeshVertexTangentData::GetTangent(size_t index) const - { - AZ_Assert(index < m_tangents.size(), "Invalid index %i for mesh tangents.", index); - return m_tangents[index]; - } + void MeshVertexTangentData::ReserveContainerSpace(size_t numVerts) + { + m_tangents.reserve(numVerts); + } + void MeshVertexTangentData::Resize(size_t numVerts) + { + m_tangents.resize(numVerts); + } - void MeshVertexTangentData::ReserveContainerSpace(size_t numVerts) - { - m_tangents.reserve(numVerts); - } + void MeshVertexTangentData::AppendTangent(const AZ::Vector4& tangent) + { + m_tangents.push_back(tangent); + } + void MeshVertexTangentData::GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const + { + output.Write("Tangents", m_tangents); + output.Write("GenerationMethod", aznumeric_cast(m_generationMethod)); + output.Write("SetIndex", aznumeric_cast(m_setIndex)); + } - void MeshVertexTangentData::Resize(size_t numVerts) - { - m_tangents.resize(numVerts); - } + void MeshVertexTangentData::SetTangent(size_t vertexIndex, const AZ::Vector4& tangent) + { + m_tangents[vertexIndex] = tangent; + } + void MeshVertexTangentData::SetTangentSetIndex(size_t setIndex) + { + m_setIndex = setIndex; + } - void MeshVertexTangentData::AppendTangent(const AZ::Vector4& tangent) - { - m_tangents.push_back(tangent); - } + size_t MeshVertexTangentData::GetTangentSetIndex() const + { + return m_setIndex; + } - void MeshVertexTangentData::GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const - { - output.Write("Tangents", m_tangents); - output.Write("TangentSpace", aznumeric_cast(m_tangentSpace)); - output.Write("SetIndex", aznumeric_cast(m_setIndex)); - } + AZ::SceneAPI::DataTypes::TangentGenerationMethod MeshVertexTangentData::GetGenerationMethod() const + { + return m_generationMethod; + } - - void MeshVertexTangentData::SetTangent(size_t vertexIndex, const AZ::Vector4& tangent) - { - m_tangents[vertexIndex] = tangent; - } - - - void MeshVertexTangentData::SetTangentSetIndex(size_t setIndex) - { - m_setIndex = setIndex; - } - - - size_t MeshVertexTangentData::GetTangentSetIndex() const - { - return m_setIndex; - } - - - AZ::SceneAPI::DataTypes::TangentSpace MeshVertexTangentData::GetTangentSpace() const - { - return m_tangentSpace; - } - - - void MeshVertexTangentData::SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace space) - { - m_tangentSpace = space; - } - - } // GraphData - } // SceneData -} // AZ + void MeshVertexTangentData::SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod method) + { + m_generationMethod = method; + } +} // AZ::SceneData::GraphData diff --git a/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexTangentData.h b/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexTangentData.h index a9d6023b70..47993c2281 100644 --- a/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexTangentData.h +++ b/Code/Tools/SceneAPI/SceneData/GraphData/MeshVertexTangentData.h @@ -14,46 +14,39 @@ #include #include -namespace AZ +namespace AZ::SceneData::GraphData { - namespace SceneData + class SCENE_DATA_CLASS MeshVertexTangentData + : public AZ::SceneAPI::DataTypes::IMeshVertexTangentData { - namespace GraphData - { + public: + AZ_RTTI(MeshVertexTangentData, "{C16F0F38-8F8F-45A2-A33B-F2758922A7C4}", AZ::SceneAPI::DataTypes::IMeshVertexTangentData); - class SCENE_DATA_CLASS MeshVertexTangentData - : public AZ::SceneAPI::DataTypes::IMeshVertexTangentData - { - public: - AZ_RTTI(MeshVertexTangentData, "{C16F0F38-8F8F-45A2-A33B-F2758922A7C4}", AZ::SceneAPI::DataTypes::IMeshVertexTangentData); + static void Reflect(ReflectContext* context); - static void Reflect(ReflectContext* context); + SCENE_DATA_API ~MeshVertexTangentData() override = default; - SCENE_DATA_API ~MeshVertexTangentData() override = default; + SCENE_DATA_API void CloneAttributesFrom(const IGraphObject* sourceObject) override; - SCENE_DATA_API void CloneAttributesFrom(const IGraphObject* sourceObject) override; + SCENE_DATA_API size_t GetCount() const override; + SCENE_DATA_API const AZ::Vector4& GetTangent(size_t index) const override; + SCENE_DATA_API void SetTangent(size_t vertexIndex, const AZ::Vector4& tangent) override; - SCENE_DATA_API size_t GetCount() const override; - SCENE_DATA_API const AZ::Vector4& GetTangent(size_t index) const override; - SCENE_DATA_API void SetTangent(size_t vertexIndex, const AZ::Vector4& tangent) override; + SCENE_DATA_API void SetTangentSetIndex(size_t setIndex) override; + SCENE_DATA_API size_t GetTangentSetIndex() const override; - SCENE_DATA_API void SetTangentSetIndex(size_t setIndex) override; - SCENE_DATA_API size_t GetTangentSetIndex() const override; + SCENE_DATA_API AZ::SceneAPI::DataTypes::TangentGenerationMethod GetGenerationMethod() const override; + SCENE_DATA_API void SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod method) override; - SCENE_DATA_API AZ::SceneAPI::DataTypes::TangentSpace GetTangentSpace() const override; - SCENE_DATA_API void SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace space) override; + SCENE_DATA_API void Resize(size_t numVerts); + SCENE_DATA_API void ReserveContainerSpace(size_t numVerts); + SCENE_DATA_API void AppendTangent(const AZ::Vector4& tangent); - SCENE_DATA_API void Resize(size_t numVerts); - SCENE_DATA_API void ReserveContainerSpace(size_t numVerts); - SCENE_DATA_API void AppendTangent(const AZ::Vector4& tangent); + SCENE_DATA_API void GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const override; - SCENE_DATA_API void GetDebugOutput(AZ::SceneAPI::Utilities::DebugOutput& output) const override; - protected: - AZStd::vector m_tangents; - AZ::SceneAPI::DataTypes::TangentSpace m_tangentSpace = AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene; - size_t m_setIndex = 0; - }; - - } // GraphData - } // SceneData -} // AZ + protected: + AZStd::vector m_tangents; + AZ::SceneAPI::DataTypes::TangentGenerationMethod m_generationMethod = AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene; + size_t m_setIndex = 0; + }; +} // AZ::SceneData::GraphData diff --git a/Code/Tools/SceneAPI/SceneData/Rules/TangentsRule.cpp b/Code/Tools/SceneAPI/SceneData/Rules/TangentsRule.cpp index 7b4be1dfdf..5dcc0b99f8 100644 --- a/Code/Tools/SceneAPI/SceneData/Rules/TangentsRule.cpp +++ b/Code/Tools/SceneAPI/SceneData/Rules/TangentsRule.cpp @@ -26,13 +26,22 @@ namespace AZ { TangentsRule::TangentsRule() : DataTypes::IRule() - , m_tangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::MikkT) { } - AZ::SceneAPI::DataTypes::TangentSpace TangentsRule::GetTangentSpace() const + AZ::SceneAPI::DataTypes::TangentGenerationMethod TangentsRule::GetGenerationMethod() const { - return m_tangentSpace; + return m_generationMethod; + } + + AZ::SceneAPI::DataTypes::MikkTSpaceMethod TangentsRule::GetMikkTSpaceMethod() const + { + return m_tSpaceMethod; + } + + AZ::Crc32 TangentsRule::GetSpaceMethodVisibility() const + { + return (m_generationMethod == AZ::SceneAPI::DataTypes::TangentGenerationMethod::MikkT) ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide; } void TangentsRule::Reflect(AZ::ReflectContext* context) @@ -43,20 +52,29 @@ namespace AZ return; } - serializeContext->Class()->Version(3) - ->Field("tangentSpace", &TangentsRule::m_tangentSpace); + serializeContext->Class()->Version(4) + ->Field("tangentSpace", &TangentsRule::m_generationMethod) + ->Field("tSpaceMethod", &TangentsRule::m_tSpaceMethod); AZ::EditContext* editContext = serializeContext->GetEditContext(); if (editContext) { editContext->Class("Tangents", "Specify how tangents are imported or generated.") ->ClassElement(Edit::ClassElements::EditorData, "") - ->Attribute("AutoExpand", true) - ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "") - ->DataElement(AZ::Edit::UIHandlers::ComboBox, &AZ::SceneAPI::SceneData::TangentsRule::m_tangentSpace, "Tangent space", "Specify the tangent space used for normal map baking. Choose 'From Fbx' to extract the tangents and bitangents directly from the Fbx file. When there is no tangents rule or the Fbx has no tangents stored inside it, the 'MikkT' option will be used with orthogonal tangents of unit length, so with the normalize option enabled, using the first UV set.") - ->EnumAttribute(AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene, "From Source Scene") - ->EnumAttribute(AZ::SceneAPI::DataTypes::TangentSpace::MikkT, "MikkT") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) + ->Attribute("AutoExpand", true) + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "") + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &AZ::SceneAPI::SceneData::TangentsRule::m_generationMethod, "Generation Method", "Specify the tangent generation method. Choose 'From Source Scene' to extract the tangents and bitangents directly from the source scene file. When there is no tangents rule or the source scene has no tangents stored inside it, the 'MikkT' option will be used.") + ->EnumAttribute(AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene, "From Source Scene") + ->EnumAttribute(AZ::SceneAPI::DataTypes::TangentGenerationMethod::MikkT, "MikkT") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &AZ::SceneAPI::SceneData::TangentsRule::m_tSpaceMethod, "TSpace Method", + "TSpace generates the tangents and bitangents with their true magnitudes which can be used for relief mapping effects. " + " It calculates the 'real' bitangent which may not be perpendicular to the tangent. " + "However, both, the tangent and bitangent are perpendicular to the vertex normal. " + "TSpaceBasic calculates unit vector tangents and bitangents at pixel/vertex level which are sufficient for basic normal mapping.") + ->EnumAttribute(AZ::SceneAPI::DataTypes::MikkTSpaceMethod::TSpace, "TSpace") + ->EnumAttribute(AZ::SceneAPI::DataTypes::MikkTSpaceMethod::TSpaceBasic, "TSpaceBasic") + ->Attribute(AZ::Edit::Attributes::Visibility, &TangentsRule::GetSpaceMethodVisibility); ; } } diff --git a/Code/Tools/SceneAPI/SceneData/Rules/TangentsRule.h b/Code/Tools/SceneAPI/SceneData/Rules/TangentsRule.h index b368fce88a..450b1331ed 100644 --- a/Code/Tools/SceneAPI/SceneData/Rules/TangentsRule.h +++ b/Code/Tools/SceneAPI/SceneData/Rules/TangentsRule.h @@ -45,12 +45,17 @@ namespace AZ SCENE_DATA_API TangentsRule(); SCENE_DATA_API ~TangentsRule() override = default; - SCENE_DATA_API AZ::SceneAPI::DataTypes::TangentSpace GetTangentSpace() const; + SCENE_DATA_API AZ::SceneAPI::DataTypes::TangentGenerationMethod GetGenerationMethod() const; + SCENE_DATA_API AZ::SceneAPI::DataTypes::MikkTSpaceMethod GetMikkTSpaceMethod() const; static void Reflect(ReflectContext* context); protected: - AZ::SceneAPI::DataTypes::TangentSpace m_tangentSpace; /**< Specifies how to handle tangents. Either generate them, or import them. */ + AZ::SceneAPI::DataTypes::TangentGenerationMethod m_generationMethod = AZ::SceneAPI::DataTypes::TangentGenerationMethod::MikkT; /**< Specifies how to handle tangents. Either generate them, or import them. */ + + // MikkT specific settings + AZ::Crc32 GetSpaceMethodVisibility() const; + AZ::SceneAPI::DataTypes::MikkTSpaceMethod m_tSpaceMethod = AZ::SceneAPI::DataTypes::MikkTSpaceMethod::TSpace; }; } // SceneData } // SceneAPI diff --git a/Code/Tools/SceneAPI/SceneData/Tests/GraphData/GraphDataBehaviorTests.cpp b/Code/Tools/SceneAPI/SceneData/Tests/GraphData/GraphDataBehaviorTests.cpp index bdcf690844..ab0fa79e62 100644 --- a/Code/Tools/SceneAPI/SceneData/Tests/GraphData/GraphDataBehaviorTests.cpp +++ b/Code/Tools/SceneAPI/SceneData/Tests/GraphData/GraphDataBehaviorTests.cpp @@ -84,7 +84,7 @@ namespace AZ auto* bitangentData = AZStd::any_cast(&data); bitangentData->AppendBitangent(AZ::Vector3{0.12f, 0.34f, 0.56f}); bitangentData->AppendBitangent(AZ::Vector3{0.77f, 0.88f, 0.99f}); - bitangentData->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene); + bitangentData->SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene); bitangentData->SetBitangentSetIndex(1); return true; } @@ -94,7 +94,7 @@ namespace AZ tangentData->AppendTangent(AZ::Vector4{0.12f, 0.34f, 0.56f, 0.78f}); tangentData->AppendTangent(AZ::Vector4{0.18f, 0.28f, 0.19f, 0.29f}); tangentData->AppendTangent(AZ::Vector4{0.21f, 0.43f, 0.65f, 0.87f}); - tangentData->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::MikkT); + tangentData->SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod::MikkT); tangentData->SetTangentSetIndex(2); return true; } @@ -318,7 +318,7 @@ namespace AZ ExpectExecute("TestExpectFloatEquals(bitangentData.y, 0.88)"); ExpectExecute("TestExpectFloatEquals(bitangentData.z, 0.99)"); ExpectExecute("TestExpectIntegerEquals(meshVertexBitangentData:GetBitangentSetIndex(), 1)"); - ExpectExecute("TestExpectTrue(meshVertexBitangentData:GetTangentSpace(), MeshVertexBitangentData.FromSourceScene)"); + ExpectExecute("TestExpectTrue(meshVertexBitangentData:GetGenerationMethod(), MeshVertexBitangentData.FromSourceScene)"); } TEST_F(GrapDatahBehaviorScriptTest, SceneGraph_MeshVertexTangentData_AccessWorks) @@ -337,7 +337,7 @@ namespace AZ ExpectExecute("TestExpectFloatEquals(tangentData.z, 0.19)"); ExpectExecute("TestExpectFloatEquals(tangentData.w, 0.29)"); ExpectExecute("TestExpectIntegerEquals(meshVertexTangentData:GetTangentSetIndex(), 2)"); - ExpectExecute("TestExpectTrue(meshVertexTangentData:GetTangentSpace(), MeshVertexTangentData.EMotionFX)"); + ExpectExecute("TestExpectTrue(meshVertexTangentData:GetGenerationMethod(), MeshVertexTangentData.EMotionFX)"); } TEST_F(GrapDatahBehaviorScriptTest, SceneGraph_AnimationData_AccessWorks) diff --git a/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerateComponent.cpp b/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerateComponent.cpp index c1c47e1955..fdf58f2502 100644 --- a/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerateComponent.cpp +++ b/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerateComponent.cpp @@ -15,8 +15,6 @@ #include #include -#include - #include #include #include @@ -52,7 +50,7 @@ namespace AZ::SceneGenerationComponents } } - AZ::SceneAPI::DataTypes::TangentSpace TangentGenerateComponent::GetTangentSpaceFromRule(const AZ::SceneAPI::Containers::Scene& scene) const + const AZ::SceneAPI::SceneData::TangentsRule* TangentGenerateComponent::GetTangentRule(const AZ::SceneAPI::Containers::Scene& scene) const { for (const auto& object : scene.GetManifest().GetValueStorage()) { @@ -62,12 +60,12 @@ namespace AZ::SceneGenerationComponents const AZ::SceneAPI::SceneData::TangentsRule* rule = group->GetRuleContainerConst().FindFirstByType().get(); if (rule) { - return rule->GetTangentSpace(); + return rule; } } } - return AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene; + return nullptr; } AZ::SceneAPI::Events::ProcessingResult TangentGenerateComponent::GenerateTangentData(TangentGenerateContext& context) @@ -189,8 +187,8 @@ namespace AZ::SceneGenerationComponents return true; // No fatal error } - // Check what tangent spaces we need. - const AZ::SceneAPI::DataTypes::TangentSpace ruleTangentSpace = GetTangentSpaceFromRule(scene); + const AZ::SceneAPI::SceneData::TangentsRule* tangentsRule = GetTangentRule(scene); + const AZ::SceneAPI::DataTypes::TangentGenerationMethod ruleGenerationMethod = tangentsRule ? tangentsRule->GetGenerationMethod() : AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene; // Find all blend shape data under the mesh. We need to generate the tangent and bitangent for blend shape as well. AZStd::vector blendShapes; @@ -208,12 +206,12 @@ namespace AZ::SceneGenerationComponents } // Check if we had tangents inside the source scene file. - AZ::SceneAPI::DataTypes::TangentSpace tangentSpace = ruleTangentSpace; + AZ::SceneAPI::DataTypes::TangentGenerationMethod generationMethod = ruleGenerationMethod; AZ::SceneAPI::DataTypes::IMeshVertexTangentData* tangentData = FindTangentData(graph, nodeIndex, uvSetIndex); AZ::SceneAPI::DataTypes::IMeshVertexBitangentData* bitangentData = FindBitangentData(graph, nodeIndex, uvSetIndex); // If all we need is import from the source scene, and we have tangent data from the source scene already, then skip generating. - if ((tangentSpace == AZ::SceneAPI::DataTypes::TangentSpace::FromSourceScene)) + if ((generationMethod == AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene)) { if (tangentData && bitangentData) { @@ -226,50 +224,56 @@ namespace AZ::SceneGenerationComponents // In case there are no tangents/bitangents while the user selected to use the source ones, default to MikkT. AZ_Warning(AZ::SceneAPI::Utilities::WarningWindow, false, "Cannot use source scene tangents as there are none in the asset for mesh '%s' for uv set %zu. Defaulting to generating tangents using MikkT.\n", scene.GetGraph().GetNodeName(nodeIndex).GetName(), uvSetIndex); - tangentSpace = AZ::SceneAPI::DataTypes::TangentSpace::MikkT; + generationMethod = AZ::SceneAPI::DataTypes::TangentGenerationMethod::MikkT; } } if (!tangentData) { if (!AZ::SceneGenerationComponents::TangentGenerateComponent::CreateTangentLayer(scene.GetManifest(), nodeIndex, meshData->GetVertexCount(), uvSetIndex, - tangentSpace, graph, &tangentData)) + generationMethod, graph, &tangentData)) { AZ_Error(AZ::SceneAPI::Utilities::ErrorWindow, false, "Failed to create tangents data set for mesh %s for uv set %zu.\n", scene.GetGraph().GetNodeName(nodeIndex).GetName(), uvSetIndex); continue; } } + AZ_Assert(tangentData == FindTangentData(graph, nodeIndex, uvSetIndex), "Used tangent data is not the same as the graph returns."); + if (!bitangentData) { if (!AZ::SceneGenerationComponents::TangentGenerateComponent::CreateBitangentLayer(scene.GetManifest(), nodeIndex, meshData->GetVertexCount(), uvSetIndex, - tangentSpace, graph, &bitangentData)) + generationMethod, graph, &bitangentData)) { AZ_Error(AZ::SceneAPI::Utilities::ErrorWindow, false, "Failed to create bitangents data set for mesh %s for uv set %zu.\n", scene.GetGraph().GetNodeName(nodeIndex).GetName(), uvSetIndex); continue; } } - tangentData->SetTangentSpace(tangentSpace); - bitangentData->SetTangentSpace(tangentSpace); + AZ_Assert(bitangentData == FindBitangentData(graph, nodeIndex, uvSetIndex), "Used bitangent data is not the same as the graph returns."); - switch (tangentSpace) + tangentData->SetGenerationMethod(generationMethod); + bitangentData->SetGenerationMethod(generationMethod); + + switch (generationMethod) { // Generate using MikkT space. - case AZ::SceneAPI::DataTypes::TangentSpace::MikkT: + case AZ::SceneAPI::DataTypes::TangentGenerationMethod::MikkT: { - allSuccess &= AZ::TangentGeneration::Mesh::MikkT::GenerateTangents(meshData, uvData, tangentData, bitangentData); + const AZ::SceneAPI::DataTypes::MikkTSpaceMethod tSpaceMethod = tangentsRule ? tangentsRule->GetMikkTSpaceMethod() : AZ::SceneAPI::DataTypes::MikkTSpaceMethod::TSpace; + + allSuccess &= AZ::TangentGeneration::Mesh::MikkT::GenerateTangents(meshData, uvData, tangentData, bitangentData, tSpaceMethod); for (AZ::SceneData::GraphData::BlendShapeData* blendShape : blendShapes) { - allSuccess &= AZ::TangentGeneration::BlendShape::MikkT::GenerateTangents(blendShape, uvSetIndex); + allSuccess &= AZ::TangentGeneration::BlendShape::MikkT::GenerateTangents(blendShape, uvSetIndex, tSpaceMethod); } } break; default: { - AZ_Assert(false, "Unknown tangent space selected (spaceID=%d) for UV set %d, cannot generate tangents!\n", static_cast(tangentSpace), uvSetIndex); + AZ_Assert(false, "Unknown tangent generation method selected (%d) for UV set %d, cannot generate tangents.\n", static_cast(generationMethod), uvSetIndex); allSuccess = false; } } @@ -339,7 +343,7 @@ namespace AZ::SceneGenerationComponents const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, size_t numVerts, size_t uvSetIndex, - AZ::SceneAPI::DataTypes::TangentSpace tangentSpace, + AZ::SceneAPI::DataTypes::TangentGenerationMethod generationMethod, AZ::SceneAPI::Containers::SceneGraph& graph, AZ::SceneAPI::DataTypes::IMeshVertexTangentData** outTangentData) { @@ -356,7 +360,7 @@ namespace AZ::SceneGenerationComponents } tangentData->SetTangentSetIndex(uvSetIndex); - tangentData->SetTangentSpace(tangentSpace); + tangentData->SetGenerationMethod(generationMethod); const AZStd::string tangentGeneratedName = AZStd::string::format("TangentSet_%zu", uvSetIndex); const AZStd::string tangentSetName = AZ::SceneAPI::DataTypes::Utilities::CreateUniqueName(tangentGeneratedName, manifest); @@ -394,7 +398,7 @@ namespace AZ::SceneGenerationComponents const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, size_t numVerts, size_t uvSetIndex, - AZ::SceneAPI::DataTypes::TangentSpace tangentSpace, + AZ::SceneAPI::DataTypes::TangentGenerationMethod generationMethod, AZ::SceneAPI::Containers::SceneGraph& graph, AZ::SceneAPI::DataTypes::IMeshVertexBitangentData** outBitangentData) { @@ -411,7 +415,7 @@ namespace AZ::SceneGenerationComponents } bitangentData->SetBitangentSetIndex(uvSetIndex); - bitangentData->SetTangentSpace(tangentSpace); + bitangentData->SetGenerationMethod(generationMethod); const AZStd::string bitangentGeneratedName = AZStd::string::format("BitangentSet_%zu", uvSetIndex); const AZStd::string bitangentSetName = AZ::SceneAPI::DataTypes::Utilities::CreateUniqueName(bitangentGeneratedName, manifest); diff --git a/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerateComponent.h b/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerateComponent.h index 06d9aded11..d2aa59b549 100644 --- a/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerateComponent.h +++ b/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerateComponent.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace AZ::SceneAPI::DataTypes { class IMeshData; } @@ -59,7 +60,7 @@ namespace AZ::SceneGenerationComponents AZStd::vector& outBlendShapes) const; bool GenerateTangentsForMesh(AZ::SceneAPI::Containers::Scene& scene, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, AZ::SceneAPI::DataTypes::IMeshData* meshData); void UpdateFbxTangentWValues(AZ::SceneAPI::Containers::SceneGraph& graph, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, const AZ::SceneAPI::DataTypes::IMeshData* meshData); - AZ::SceneAPI::DataTypes::TangentSpace GetTangentSpaceFromRule(const AZ::SceneAPI::Containers::Scene& scene) const; + const AZ::SceneAPI::SceneData::TangentsRule* GetTangentRule(const AZ::SceneAPI::Containers::Scene& scene) const; size_t CalcUvSetCount(AZ::SceneAPI::Containers::SceneGraph& graph, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex) const; AZ::SceneAPI::DataTypes::IMeshVertexUVData* FindUvData(AZ::SceneAPI::Containers::SceneGraph& graph, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, AZ::u64 uvSet) const; @@ -69,7 +70,7 @@ namespace AZ::SceneGenerationComponents const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, size_t numVerts, size_t uvSetIndex, - AZ::SceneAPI::DataTypes::TangentSpace tangentSpace, + AZ::SceneAPI::DataTypes::TangentGenerationMethod generationMethod, AZ::SceneAPI::Containers::SceneGraph& graph, AZ::SceneAPI::DataTypes::IMeshVertexTangentData** outTangentData); @@ -78,7 +79,7 @@ namespace AZ::SceneGenerationComponents const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex, size_t numVerts, size_t uvSetIndex, - AZ::SceneAPI::DataTypes::TangentSpace tangentSpace, + AZ::SceneAPI::DataTypes::TangentGenerationMethod generationMethod, AZ::SceneAPI::Containers::SceneGraph& graph, AZ::SceneAPI::DataTypes::IMeshVertexBitangentData** outBitangentData); }; diff --git a/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/BlendShapeMikkTGenerator.cpp b/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/BlendShapeMikkTGenerator.cpp index 4e60fd3d15..b472aa4e09 100644 --- a/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/BlendShapeMikkTGenerator.cpp +++ b/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/BlendShapeMikkTGenerator.cpp @@ -76,33 +76,47 @@ namespace AZ::TangentGeneration::BlendShape::MikkT const AZ::Vector4 tangentVec(tangent[0]*magS, tangent[1]*magS, tangent[2]*magS, flipSign); const AZ::Vector3 bitangentVec(bitangent[0]*magT, bitangent[1]*magT, bitangent[2]*magT); - // Set the tangent and bitangent back to the blendshape + // Set the tangent and bitangent back to the blend shape AZStd::vector& tangents = customData->m_blendShapeData->GetTangents(); AZStd::vector& bitangents = customData->m_blendShapeData->GetBitangents(); tangents[vertexIndex] = tangentVec; bitangents[vertexIndex] = bitangentVec; } - bool GenerateTangents(AZ::SceneData::GraphData::BlendShapeData* blendShapeData, size_t uvSetIndex) + void SetTSpaceBasic(const SMikkTSpaceContext* context, const float tangent[], const float signValue, const int face, const int vert) + { + MikktCustomData* customData = static_cast(context->m_pUserData); + const AZ::u32 vertexIndex = customData->m_blendShapeData->GetFaceVertexIndex(face, vert); + AZ::Vector3 tangentVec3(tangent[0], tangent[1], tangent[2]); + tangentVec3.NormalizeSafe(); + AZ::Vector3 normal = customData->m_blendShapeData->GetNormal(vertexIndex); + normal.NormalizeSafe(); + const AZ::Vector3 bitangent = normal.Cross(tangentVec3) * signValue; + + // Set the tangent and bitangent back to the blend shape + AZStd::vector& tangents = customData->m_blendShapeData->GetTangents(); + AZStd::vector& bitangents = customData->m_blendShapeData->GetBitangents(); + tangents[vertexIndex] = AZ::Vector4(tangentVec3.GetX(), tangentVec3.GetY(), tangentVec3.GetZ(), signValue); + bitangents[vertexIndex] = bitangent; + } + + bool GenerateTangents(AZ::SceneData::GraphData::BlendShapeData* blendShapeData, + size_t uvSetIndex, + AZ::SceneAPI::DataTypes::MikkTSpaceMethod tSpaceMethod) { // Create tangent and bitangent data sets and relate them to the given UV set. const AZStd::vector& uvSet = blendShapeData->GetUVs(uvSetIndex); if (uvSet.empty()) { - AZ_TracePrintf(AZ::SceneAPI::Utilities::ErrorWindow, "Cannot find UV data (set index=%d) to generate tangents and bitangents from in MikkT generator!\n", uvSetIndex); - return false; - } - - AZStd::vector& tangents = blendShapeData->GetTangents(); - AZStd::vector& bitangents = blendShapeData->GetBitangents(); - if (!tangents.empty() || !bitangents.empty()) - { - AZ_TracePrintf( - AZ::SceneAPI::Utilities::WarningWindow, "Cannot generate tangents and bitangents because existing tangent or bitangent data has been found.\n"); + AZ_Error(AZ::SceneAPI::Utilities::ErrorWindow, false, + "Cannot find UV data (set index=%d) to generate tangents and bitangents from in MikkT generator.\n", + uvSetIndex); return false; } // Pre-allocate the tangent and bitangent data. + AZStd::vector& tangents = blendShapeData->GetTangents(); + AZStd::vector& bitangents = blendShapeData->GetBitangents(); tangents.resize(blendShapeData->GetVertexCount()); bitangents.resize(blendShapeData->GetVertexCount()); @@ -114,10 +128,24 @@ namespace AZ::TangentGeneration::BlendShape::MikkT mikkInterface.m_getNormal = GetNormal; mikkInterface.m_getPosition = GetPosition; mikkInterface.m_getTexCoord = GetTexCoord; - mikkInterface.m_setTSpace = SetTSpace; - mikkInterface.m_setTSpaceBasic = nullptr; mikkInterface.m_getNumVerticesOfFace= GetNumVerticesOfFace; + switch (tSpaceMethod) + { + case AZ::SceneAPI::DataTypes::MikkTSpaceMethod::TSpaceBasic: + { + mikkInterface.m_setTSpace = nullptr; + mikkInterface.m_setTSpaceBasic = SetTSpaceBasic; + break; + } + default: + { + mikkInterface.m_setTSpace = SetTSpace; + mikkInterface.m_setTSpaceBasic = nullptr; + break; + } + } + // Set the MikkT custom data. MikktCustomData customData; customData.m_blendShapeData = blendShapeData; diff --git a/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/BlendShapeMikkTGenerator.h b/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/BlendShapeMikkTGenerator.h index cbb170375a..68d4835817 100644 --- a/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/BlendShapeMikkTGenerator.h +++ b/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/BlendShapeMikkTGenerator.h @@ -9,6 +9,7 @@ #pragma once #include +#include namespace AZ::SceneData::GraphData { @@ -24,5 +25,7 @@ namespace AZ::TangentGeneration::BlendShape::MikkT }; // The main generation method. - bool GenerateTangents(AZ::SceneData::GraphData::BlendShapeData* blendShapeData, size_t uvSetIndex); + bool GenerateTangents(AZ::SceneData::GraphData::BlendShapeData* blendShapeData, + size_t uvSetIndex, + AZ::SceneAPI::DataTypes::MikkTSpaceMethod tSpaceMethod = AZ::SceneAPI::DataTypes::MikkTSpaceMethod::TSpace); } // namespace AZ::TangentGeneration::MikkT diff --git a/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/MikkTGenerator.cpp b/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/MikkTGenerator.cpp index 3f736815e5..d3b694e852 100644 --- a/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/MikkTGenerator.cpp +++ b/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/MikkTGenerator.cpp @@ -108,7 +108,8 @@ namespace AZ::TangentGeneration::Mesh::MikkT bool GenerateTangents(const AZ::SceneAPI::DataTypes::IMeshData* meshData, const AZ::SceneAPI::DataTypes::IMeshVertexUVData* uvData, AZ::SceneAPI::DataTypes::IMeshVertexTangentData* outTangentData, - AZ::SceneAPI::DataTypes::IMeshVertexBitangentData* outBitangentData) + AZ::SceneAPI::DataTypes::IMeshVertexBitangentData* outBitangentData, + AZ::SceneAPI::DataTypes::MikkTSpaceMethod tSpaceMethod) { // Provide the MikkT interface. SMikkTSpaceInterface mikkInterface; @@ -116,10 +117,24 @@ namespace AZ::TangentGeneration::Mesh::MikkT mikkInterface.m_getNormal = GetNormal; mikkInterface.m_getPosition = GetPosition; mikkInterface.m_getTexCoord = GetTexCoord; - mikkInterface.m_setTSpace = SetTSpace; - mikkInterface.m_setTSpaceBasic = nullptr;//SetTSpaceBasic; mikkInterface.m_getNumVerticesOfFace= GetNumVerticesOfFace; + switch (tSpaceMethod) + { + case AZ::SceneAPI::DataTypes::MikkTSpaceMethod::TSpaceBasic: + { + mikkInterface.m_setTSpace = nullptr; + mikkInterface.m_setTSpaceBasic = SetTSpaceBasic; + break; + } + default: + { + mikkInterface.m_setTSpace = SetTSpace; + mikkInterface.m_setTSpaceBasic = nullptr; + break; + } + } + // Set the MikkT custom data. MikktCustomData customData; customData.m_meshData = meshData; diff --git a/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/MikkTGenerator.h b/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/MikkTGenerator.h index 4b1d8ebd72..4604a6e4c5 100644 --- a/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/MikkTGenerator.h +++ b/Gems/SceneProcessing/Code/Source/Generation/Components/TangentGenerator/TangentGenerators/MikkTGenerator.h @@ -8,6 +8,7 @@ #pragma once +#include #include namespace AZ::SceneAPI::DataTypes { class IMeshData; } @@ -28,5 +29,6 @@ namespace AZ::TangentGeneration::Mesh::MikkT bool GenerateTangents(const AZ::SceneAPI::DataTypes::IMeshData* meshData, const AZ::SceneAPI::DataTypes::IMeshVertexUVData* uvData, AZ::SceneAPI::DataTypes::IMeshVertexTangentData* outTangentData, - AZ::SceneAPI::DataTypes::IMeshVertexBitangentData* outBitangentData); + AZ::SceneAPI::DataTypes::IMeshVertexBitangentData* outBitangentData, + AZ::SceneAPI::DataTypes::MikkTSpaceMethod tSpaceMethod = AZ::SceneAPI::DataTypes::MikkTSpaceMethod::TSpace); } // namespace AZ::TangentGeneration::MikkT From 76a3195487e0c0a3f4cd9f01b089653b0336093d Mon Sep 17 00:00:00 2001 From: chcurran <82187351+carlitosan@users.noreply.github.com> Date: Tue, 27 Jul 2021 10:52:22 -0700 Subject: [PATCH 17/19] remove null data check from connected slots Signed-off-by: chcurran <82187351+carlitosan@users.noreply.github.com> --- .../Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp index 545500c57e..883e15cd29 100644 --- a/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp +++ b/Gems/ScriptCanvas/Code/Include/ScriptCanvas/Grammar/AbstractCodeModel.cpp @@ -4343,6 +4343,9 @@ namespace ScriptCanvas execution->AddInput({ &input, inputVariable, DebugDataSource::FromSelfSlot(input, inputVariable->m_datum.GetType()) }); } + + // Check for known null reads + CheckForKnownNullDereference(execution, execution->GetInput(execution->GetInputCount() - 1), input); } else { @@ -4374,9 +4377,6 @@ namespace ScriptCanvas return; } } - - // Check for known null reads - CheckForKnownNullDereference(execution, execution->GetInput(execution->GetInputCount() - 1), input); } bool AbstractCodeModel::ParseInputThisPointer(ExecutionTreePtr execution) From bfbe6fc95c48a08bdbac419d9ea7beae060a09fc Mon Sep 17 00:00:00 2001 From: jjjoness <82226755+jjjoness@users.noreply.github.com> Date: Tue, 27 Jul 2021 19:12:36 +0100 Subject: [PATCH 18/19] Corrected the text message (#2483) Signed-off-by: John Jones-Steele --- .../Code/Source/InputConfigurationComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/StartingPointInput/Code/Source/InputConfigurationComponent.cpp b/Gems/StartingPointInput/Code/Source/InputConfigurationComponent.cpp index 6739a54ddd..42fa05473c 100644 --- a/Gems/StartingPointInput/Code/Source/InputConfigurationComponent.cpp +++ b/Gems/StartingPointInput/Code/Source/InputConfigurationComponent.cpp @@ -62,7 +62,7 @@ namespace StartingPointInput ->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, true) ->Attribute("BrowseIcon", ":/stylesheet/img/UI20/browse-edit-select-files.svg") ->Attribute("EditButton", "") - ->Attribute("EditDescription", "Open in Input Bindings Editor") + ->Attribute("EditDescription", "Open in Asset Editor") ->DataElement(AZ::Edit::UIHandlers::SpinBox, &InputConfigurationComponent::m_localPlayerIndex, "Local player index", "The player index that this component will receive input from (0 based, -1 means all controllers).\n" "Will only work on platforms such as PC where the local user id corresponds to the local player index.\n" From 33c408f65424358c7bfb0c1db44081fea4c31d73 Mon Sep 17 00:00:00 2001 From: Ken Pruiksma Date: Tue, 27 Jul 2021 13:31:05 -0500 Subject: [PATCH 19/19] Expose shadow bias to component & feature processors. (#2406) * Expose shadow bias to component & feature processors. Shadow bias now works more consistently with various near / far shadow planes and caster positions. Bias now also affects esm shadows which helps eliminate acne in certain situations. Signed-off-by: Ken Pruiksma * Adding jira comment to light configuration serialization version. Improved comment on final adjustment to bias before its sent to the shader. Signed-off-by: Ken Pruiksma * Hooking up bias to behavior context. Signed-off-by: Ken Pruiksma --- .../Features/Shadow/ProjectedShadow.azsli | 13 +- .../DiskLightFeatureProcessorInterface.h | 2 + .../PointLightFeatureProcessorInterface.h | 2 + ...ProjectedShadowFeatureProcessorInterface.h | 2 + .../CoreLights/DiskLightFeatureProcessor.cpp | 5 + .../CoreLights/DiskLightFeatureProcessor.h | 1 + .../CoreLights/PointLightFeatureProcessor.cpp | 5 + .../CoreLights/PointLightFeatureProcessor.h | 1 + .../ProjectedShadowFeatureProcessor.cpp | 37 ++-- .../Shadows/ProjectedShadowFeatureProcessor.h | 2 + .../CommonFeatures/CoreLights/AreaLightBus.h | 6 + .../CoreLights/AreaLightComponentConfig.h | 1 + .../CoreLights/AreaLightComponentConfig.cpp | 3 +- .../AreaLightComponentController.cpp | 18 ++ .../CoreLights/AreaLightComponentController.h | 2 + .../Source/CoreLights/DiskLightDelegate.cpp | 8 + .../Source/CoreLights/DiskLightDelegate.h | 1 + .../CoreLights/EditorAreaLightComponent.cpp | 9 + .../Source/CoreLights/LightDelegateBase.h | 1 + .../CoreLights/LightDelegateInterface.h | 4 +- .../Source/CoreLights/SphereLightDelegate.cpp | 167 +++++++++--------- .../Source/CoreLights/SphereLightDelegate.h | 1 + 22 files changed, 186 insertions(+), 105 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli index 97862b6f6e..2fea4650b3 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli @@ -62,6 +62,7 @@ class ProjectedShadow float3 m_lightDirection; float3 m_normalVector; float3 m_shadowPosition; + float m_bias; }; float ProjectedShadow::GetVisibility( @@ -238,7 +239,7 @@ float ProjectedShadow::GetVisibilityEsm() } const float3 atlasPosition = GetAtlasPosition(m_shadowPosition.xy); const float depth = PerspectiveDepthToLinear( - m_shadowPosition.z, + m_shadowPosition.z - m_bias, coefficients); const float occluder = shadowmap.SampleLevel( PassSrg::LinearSampler, @@ -280,7 +281,7 @@ float ProjectedShadow::GetVisibilityEsmPcf() } const float3 atlasPosition = GetAtlasPosition(m_shadowPosition.xy); const float depth = PerspectiveDepthToLinear( - m_shadowPosition.z, + m_shadowPosition.z - m_bias, coefficients); const float occluder = shadowmap.SampleLevel( PassSrg::LinearSampler, @@ -346,7 +347,7 @@ float ProjectedShadow::SamplePcfBicubic() param.shadowPos = float3(atlasPosition.xy * ViewSrg::m_invShadowmapAtlasSize, atlasPosition.z); param.shadowMapSize = ViewSrg::m_shadowmapAtlasSize; param.invShadowMapSize = ViewSrg::m_invShadowmapAtlasSize; - param.comparisonValue = m_shadowPosition.z - ViewSrg::m_projectedShadows[m_shadowIndex].m_bias; + param.comparisonValue = m_shadowPosition.z - m_bias; param.samplerState = SceneSrg::m_hwPcfSampler; if (filteringSampleCount <= 4) @@ -384,8 +385,8 @@ bool ProjectedShadow::IsShadowed(float3 shadowPosition) PassSrg::LinearSampler, float3(atlasPosition.xy * invAtlasSize, atlasPosition.z), /*LOD=*/0).r; const float depthDiff = depthInShadowmap - shadowPosition.z; - float bias = ViewSrg::m_projectedShadows[m_shadowIndex].m_bias; - if (depthDiff < -bias) + + if (depthDiff < -m_bias) { return true; } @@ -428,6 +429,8 @@ void ProjectedShadow::SetShadowPosition() const float4x4 depthBiasMatrix = ViewSrg::m_projectedShadows[m_shadowIndex].m_depthBiasMatrix; float4 shadowPositionHomogeneous = mul(depthBiasMatrix, float4(m_worldPosition, 1)); m_shadowPosition = shadowPositionHomogeneous.xyz / shadowPositionHomogeneous.w; + + m_bias = ViewSrg::m_projectedShadows[m_shadowIndex].m_bias / shadowPositionHomogeneous.w; } float3 ProjectedShadow::GetAtlasPosition(float2 texturePosition) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h index 9248add6f5..e4986eee93 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h @@ -84,6 +84,8 @@ namespace AZ //! Sets if shadows are enabled virtual void SetShadowsEnabled(LightHandle handle, bool enabled) = 0; + //! Sets the shadow bias + virtual void SetShadowBias(LightHandle handle, float bias) = 0; //! Sets the shadowmap size (width and height) of the light. virtual void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) = 0; //! Specifies filter method of shadows. diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h index 8ad4fb89de..3383378dc7 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h @@ -66,6 +66,8 @@ namespace AZ virtual void SetShadowsEnabled(LightHandle handle, bool enabled) = 0; //! Sets the shadowmap size (width and height) of the light. virtual void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) = 0; + //! Sets the shadow bias + virtual void SetShadowBias(LightHandle handle, float bias) = 0; //! Specifies filter method of shadows. virtual void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) = 0; //! Specifies the width of boundary between shadowed area and lit area in radians. The degree ofshadowed gradually changes on diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h index c0cfff3dd5..6cbb0cfef1 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Shadows/ProjectedShadowFeatureProcessorInterface.h @@ -50,6 +50,8 @@ namespace AZ::Render virtual void SetFieldOfViewY(ShadowId id, float fieldOfView) = 0; //! Sets the maximum resolution of the shadow map virtual void SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size) = 0; + //! Sets the shadow bias + virtual void SetShadowBias(ShadowId id, float bias) = 0; //! Sets the shadowmap Pcf method. virtual void SetPcfMethod(ShadowId id, PcfMethod method) = 0; //! Sets the shadow filter method diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp index 54410544a7..55be9d232e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp @@ -308,6 +308,11 @@ namespace AZ AZStd::invoke(AZStd::forward(functor), m_shadowFeatureProcessor, shadowId, AZStd::forward(param)); } } + + void DiskLightFeatureProcessor::SetShadowBias(LightHandle handle, float bias) + { + SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowBias, bias); + } void DiskLightFeatureProcessor::SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h index 0ff8efb1b1..2e97ae1ded 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h @@ -50,6 +50,7 @@ namespace AZ void SetConstrainToConeLight(LightHandle handle, bool useCone) override; void SetConeAngles(LightHandle handle, float innerDegrees, float outerDegrees) override; void SetShadowsEnabled(LightHandle handle, bool enabled) override; + void SetShadowBias(LightHandle handle, float bias) override; void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) override; void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override; void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) override; diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp index 2b54b6ede1..9baa2ae1c2 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp @@ -277,6 +277,11 @@ namespace AZ } } } + + void PointLightFeatureProcessor::SetShadowBias(LightHandle handle, float bias) + { + SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowBias, bias); + } void PointLightFeatureProcessor::SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) { diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h index d7bb25c71c..b7b644da9e 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h @@ -47,6 +47,7 @@ namespace AZ void SetAttenuationRadius(LightHandle handle, float attenuationRadius) override; void SetBulbRadius(LightHandle handle, float bulbRadius) override; void SetShadowsEnabled(LightHandle handle, bool enabled) override; + void SetShadowBias(LightHandle handle, float bias) override; void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) override; void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override; void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) override; diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp index 10dcffcf74..03ee176fd0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp @@ -143,7 +143,15 @@ namespace AZ::Render shadowProperty.m_desc.m_fieldOfViewYRadians = fieldOfViewYRadians; UpdateShadowView(shadowProperty); } - + + void ProjectedShadowFeatureProcessor::SetShadowBias(ShadowId id, float bias) + { + AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowBias()."); + + ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id); + shadowProperty.m_bias = bias; + } + void ProjectedShadowFeatureProcessor::SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size) { AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowmapMaxResolution()."); @@ -265,23 +273,20 @@ namespace AZ::Render view->SetCameraTransform(Matrix3x4::CreateFromTransform(desc.m_transform)); ShadowData& shadowData = m_shadowData.GetElement(shadowProperty.m_shadowId.GetIndex()); - shadowData.m_bias = (nearDist / farDist) * 0.1f; + + // Adjust the manually set bias to a more appropriate range for the shader. Scale the bias by the + // near plane so that the bias appears consistent as other light properties change. + shadowData.m_bias = nearDist * shadowProperty.m_bias * 0.01f; FilterParameter& esmData = m_shadowData.GetElement(shadowProperty.m_shadowId.GetIndex()); - if (FilterMethodIsEsm(shadowData)) - { - // Set parameters to calculate linear depth if ESM is used. - m_filterParameterNeedsUpdate = true; - esmData.m_isEnabled = true; - esmData.m_n_f_n = nearDist / (farDist - nearDist); - esmData.m_n_f = nearDist - farDist; - esmData.m_f = farDist; - } - else - { - // Reset enabling flag if ESM is not used. - esmData.m_isEnabled = false; - } + + // Set parameters to calculate linear depth if ESM is used. + esmData.m_n_f_n = nearDist / (farDist - nearDist); + esmData.m_n_f = nearDist - farDist; + esmData.m_f = farDist; + + esmData.m_isEnabled = FilterMethodIsEsm(shadowData); + m_filterParameterNeedsUpdate = m_filterParameterNeedsUpdate || esmData.m_isEnabled; for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses) { diff --git a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h index 4da4e2ff1e..3dbf88addb 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h @@ -47,6 +47,7 @@ namespace AZ::Render void SetAspectRatio(ShadowId id, float aspectRatio) override; void SetFieldOfViewY(ShadowId id, float fieldOfViewYRadians) override; void SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size) override; + void SetShadowBias(ShadowId id, float bias) override; void SetPcfMethod(ShadowId id, PcfMethod method); void SetEsmExponent(ShadowId id, float exponent); void SetShadowFilterMethod(ShadowId id, ShadowFilterMethod method) override; @@ -79,6 +80,7 @@ namespace AZ::Render { ProjectedShadowDescriptor m_desc; RPI::ViewPtr m_shadowmapView; + float m_bias = 0.1f; ShadowId m_shadowId; }; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h index 9286db2817..06b00a6b84 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h @@ -101,6 +101,12 @@ namespace AZ //! Sets if shadows should be enabled. virtual void SetEnableShadow(bool enabled) = 0; + + //! Returns the shadow bias. + virtual float GetShadowBias() const = 0; + + //! Sets the shadow bias. + virtual void SetShadowBias(float bias) = 0; //! Returns the maximum width and height of shadowmap. virtual ShadowmapSize GetShadowmapMaxSize() const = 0; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h index 8a3bb337a2..b890b3e264 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h @@ -56,6 +56,7 @@ namespace AZ // Shadows (only used for supported shapes) bool m_enableShadow = false; + float m_bias = 0.1f; ShadowmapSize m_shadowmapMaxSize = ShadowmapSize::Size256; ShadowFilterMethod m_shadowFilterMethod = ShadowFilterMethod::None; PcfMethod m_pcfMethod = PcfMethod::Bicubic; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp index c442ee39ae..b9da5ccff4 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentConfig.cpp @@ -18,7 +18,7 @@ namespace AZ if (auto serializeContext = azrtti_cast(context)) { serializeContext->Class() - ->Version(6) // ATOM-15654 + ->Version(7) // ATOM-16034 ->Field("LightType", &AreaLightComponentConfig::m_lightType) ->Field("Color", &AreaLightComponentConfig::m_color) ->Field("IntensityMode", &AreaLightComponentConfig::m_intensityMode) @@ -33,6 +33,7 @@ namespace AZ ->Field("OuterShutterAngleDegrees", &AreaLightComponentConfig::m_outerShutterAngleDegrees) // Shadows ->Field("Enable Shadow", &AreaLightComponentConfig::m_enableShadow) + ->Field("Shadow Bias", &AreaLightComponentConfig::m_bias) ->Field("Shadowmap Max Size", &AreaLightComponentConfig::m_shadowmapMaxSize) ->Field("Shadow Filter Method", &AreaLightComponentConfig::m_shadowFilterMethod) ->Field("Softening Boundary Width", &AreaLightComponentConfig::m_boundaryWidthInDegrees) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp index a6bf66d20e..b90b145320 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.cpp @@ -68,6 +68,8 @@ namespace AZ::Render ->Event("GetEnableShadow", &AreaLightRequestBus::Events::GetEnableShadow) ->Event("SetEnableShadow", &AreaLightRequestBus::Events::SetEnableShadow) + ->Event("GetShadowBias", &AreaLightRequestBus::Events::GetShadowBias) + ->Event("SetShadowBias", &AreaLightRequestBus::Events::SetShadowBias) ->Event("GetShadowmapMaxSize", &AreaLightRequestBus::Events::GetShadowmapMaxSize) ->Event("SetShadowmapMaxSize", &AreaLightRequestBus::Events::SetShadowmapMaxSize) ->Event("GetShadowFilterMethod", &AreaLightRequestBus::Events::GetShadowFilterMethod) @@ -94,6 +96,7 @@ namespace AZ::Render ->VirtualProperty("OuterShutterAngle", "GetOuterShutterAngle", "SetOuterShutterAngle") ->VirtualProperty("ShadowsEnabled", "GetEnableShadow", "SetEnableShadow") + ->VirtualProperty("ShadowBias", "GetShadowBias", "SetShadowBias") ->VirtualProperty("ShadowmapMaxSize", "GetShadowmapMaxSize", "SetShadowmapMaxSize") ->VirtualProperty("ShadowFilterMethod", "GetShadowFilterMethod", "SetShadowFilterMethod") ->VirtualProperty("SofteningBoundaryWidthAngle", "GetSofteningBoundaryWidthAngle", "SetSofteningBoundaryWidthAngle") @@ -307,6 +310,7 @@ namespace AZ::Render m_lightShapeDelegate->SetEnableShadow(m_configuration.m_enableShadow); if (m_configuration.m_enableShadow) { + m_lightShapeDelegate->SetShadowBias(m_configuration.m_bias); m_lightShapeDelegate->SetShadowmapMaxSize(m_configuration.m_shadowmapMaxSize); m_lightShapeDelegate->SetShadowFilterMethod(m_configuration.m_shadowFilterMethod); m_lightShapeDelegate->SetSofteningBoundaryWidthAngle(m_configuration.m_boundaryWidthInDegrees); @@ -467,6 +471,20 @@ namespace AZ::Render m_lightShapeDelegate->SetEnableShadow(enabled); } } + + float AreaLightComponentController::GetShadowBias() const + { + return m_configuration.m_bias; + } + + void AreaLightComponentController::SetShadowBias(float bias) + { + m_configuration.m_bias = bias; + if (m_lightShapeDelegate) + { + m_lightShapeDelegate->SetShadowBias(bias); + } + } ShadowmapSize AreaLightComponentController::GetShadowmapMaxSize() const { diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h index 0a835a75d0..d290beb81d 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/AreaLightComponentController.h @@ -76,6 +76,8 @@ namespace AZ bool GetEnableShadow() const override; void SetEnableShadow(bool enabled) override; + float GetShadowBias() const override; + void SetShadowBias(float bias) override; ShadowmapSize GetShadowmapMaxSize() const override; void SetShadowmapMaxSize(ShadowmapSize size) override; ShadowFilterMethod GetShadowFilterMethod() const override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp index 06d0b39981..c6e4441d57 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp @@ -123,6 +123,14 @@ namespace AZ::Render } } + void DiskLightDelegate::SetShadowBias(float bias) + { + if (GetShadowsEnabled() && GetLightHandle().IsValid()) + { + GetFeatureProcessor()->SetShadowBias(GetLightHandle(), bias); + } + } + void DiskLightDelegate::SetShadowmapMaxSize(ShadowmapSize size) { if (GetShadowsEnabled() && GetLightHandle().IsValid()) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h index 53ddae669b..6931068635 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.h @@ -41,6 +41,7 @@ namespace AZ void SetShutterAngles(float innerAngleDegrees, float outerAngleDegrees) override; void SetEnableShadow(bool enabled) override; + void SetShadowBias(float bias) override; void SetShadowmapMaxSize(ShadowmapSize size) override; void SetShadowFilterMethod(ShadowFilterMethod method) override; void SetSofteningBoundaryWidthAngle(float widthInDegrees) override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp index b58ca6448a..e45f460f18 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/EditorAreaLightComponent.cpp @@ -131,6 +131,15 @@ namespace AZ ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShadowsDisabled) + ->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_bias, "Bias", "How deep in shadow a surface must be before being affected by it.") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->Attribute(Edit::Attributes::Min, 0.0f) + ->Attribute(Edit::Attributes::Max, 100.0f) + ->Attribute(Edit::Attributes::SoftMin, 0.0f) + ->Attribute(Edit::Attributes::SoftMax, 1.0f) + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows) + ->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShadowsDisabled) ->DataElement(Edit::UIHandlers::ComboBox, &AreaLightComponentConfig::m_shadowFilterMethod, "Shadow filter method", "Filtering method of edge-softening of shadows.\n" " None: no filtering\n" diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h index 18d88b72af..415878081c 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateBase.h @@ -53,6 +53,7 @@ namespace AZ void SetShutterAngles([[maybe_unused]]float innerAngleDegrees, [[maybe_unused]]float outerAngleDegrees) override {}; void SetEnableShadow(bool enabled) override { m_shadowsEnabled = enabled; }; + void SetShadowBias([[maybe_unused]] float bias) override {}; void SetShadowmapMaxSize([[maybe_unused]] ShadowmapSize size) override {}; void SetShadowFilterMethod([[maybe_unused]] ShadowFilterMethod method) override {}; void SetSofteningBoundaryWidthAngle([[maybe_unused]] float widthInDegrees) override {}; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h index 52f9958d40..f18c3ef9af 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/LightDelegateInterface.h @@ -67,8 +67,10 @@ namespace AZ // Shadows - //! Sets if shadows should be enabled + //! Sets if shadows should be enabled virtual void SetEnableShadow(bool enabled) = 0; + //! Sets the shadow bias + virtual void SetShadowBias(float bias) = 0; //! Sets the maximum resolution of the shadow map virtual void SetShadowmapMaxSize(ShadowmapSize size) = 0; //! Sets the filter method for the shadow diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp index 1da6847269..edf08ba8c9 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.cpp @@ -11,121 +11,124 @@ #include #include -namespace AZ +namespace AZ::Render { - namespace Render + SphereLightDelegate::SphereLightDelegate(LmbrCentral::SphereShapeComponentRequests* shapeBus, EntityId entityId, bool isVisible) + : LightDelegateBase(entityId, isVisible) + , m_shapeBus(shapeBus) { - SphereLightDelegate::SphereLightDelegate(LmbrCentral::SphereShapeComponentRequests* shapeBus, EntityId entityId, bool isVisible) - : LightDelegateBase(entityId, isVisible) - , m_shapeBus(shapeBus) - { - InitBase(entityId); - } + InitBase(entityId); + } - float SphereLightDelegate::CalculateAttenuationRadius(float lightThreshold) const - { - // Calculate the radius at which the irradiance will be equal to cutoffIntensity. - float intensity = GetPhotometricValue().GetCombinedIntensity(PhotometricUnit::Lumen); - return sqrt(intensity / lightThreshold); - } + float SphereLightDelegate::CalculateAttenuationRadius(float lightThreshold) const + { + // Calculate the radius at which the irradiance will be equal to cutoffIntensity. + float intensity = GetPhotometricValue().GetCombinedIntensity(PhotometricUnit::Lumen); + return sqrt(intensity / lightThreshold); + } - void SphereLightDelegate::HandleShapeChanged() + void SphereLightDelegate::HandleShapeChanged() + { + if (GetLightHandle().IsValid()) { - if (GetLightHandle().IsValid()) - { - GetFeatureProcessor()->SetPosition(GetLightHandle(), GetTransform().GetTranslation()); - GetFeatureProcessor()->SetBulbRadius(GetLightHandle(), GetRadius()); - } + GetFeatureProcessor()->SetPosition(GetLightHandle(), GetTransform().GetTranslation()); + GetFeatureProcessor()->SetBulbRadius(GetLightHandle(), GetRadius()); } + } - float SphereLightDelegate::GetSurfaceArea() const - { - float radius = GetRadius(); - return 4.0f * Constants::Pi * radius * radius; - } + float SphereLightDelegate::GetSurfaceArea() const + { + float radius = GetRadius(); + return 4.0f * Constants::Pi * radius * radius; + } - float SphereLightDelegate::GetRadius() const - { - return m_shapeBus->GetRadius() * GetTransform().GetUniformScale(); - } + float SphereLightDelegate::GetRadius() const + { + return m_shapeBus->GetRadius() * GetTransform().GetUniformScale(); + } - void SphereLightDelegate::DrawDebugDisplay(const Transform& transform, const Color& color, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const + void SphereLightDelegate::DrawDebugDisplay(const Transform& transform, const Color& color, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const + { + if (isSelected) { - if (isSelected) - { - debugDisplay.SetColor(color); + debugDisplay.SetColor(color); - // Draw a sphere for the attenuation radius - debugDisplay.DrawWireSphere(transform.GetTranslation(), GetConfig()->m_attenuationRadius); - } + // Draw a sphere for the attenuation radius + debugDisplay.DrawWireSphere(transform.GetTranslation(), GetConfig()->m_attenuationRadius); } + } - void SphereLightDelegate::SetEnableShadow(bool enabled) + void SphereLightDelegate::SetEnableShadow(bool enabled) + { + Base::SetEnableShadow(enabled); + + if (GetLightHandle().IsValid()) { - Base::SetEnableShadow(enabled); - - if (GetLightHandle().IsValid()) - { - GetFeatureProcessor()->SetShadowsEnabled(GetLightHandle(), enabled); - } + GetFeatureProcessor()->SetShadowsEnabled(GetLightHandle(), enabled); } - - void SphereLightDelegate::SetShadowmapMaxSize(ShadowmapSize size) + } + + void SphereLightDelegate::SetShadowBias(float bias) + { + if (GetShadowsEnabled() && GetLightHandle().IsValid()) { - if (GetShadowsEnabled() && GetLightHandle().IsValid()) - { - GetFeatureProcessor()->SetShadowmapMaxResolution(GetLightHandle(), size); - } + GetFeatureProcessor()->SetShadowBias(GetLightHandle(), bias); } + } - void SphereLightDelegate::SetShadowFilterMethod(ShadowFilterMethod method) + void SphereLightDelegate::SetShadowmapMaxSize(ShadowmapSize size) + { + if (GetShadowsEnabled() && GetLightHandle().IsValid()) { - if (GetShadowsEnabled() && GetLightHandle().IsValid()) - { - GetFeatureProcessor()->SetShadowFilterMethod(GetLightHandle(), method); - } + GetFeatureProcessor()->SetShadowmapMaxResolution(GetLightHandle(), size); } + } - void SphereLightDelegate::SetSofteningBoundaryWidthAngle(float widthInDegrees) + void SphereLightDelegate::SetShadowFilterMethod(ShadowFilterMethod method) + { + if (GetShadowsEnabled() && GetLightHandle().IsValid()) { - if (GetShadowsEnabled() && GetLightHandle().IsValid()) - { - GetFeatureProcessor()->SetSofteningBoundaryWidthAngle(GetLightHandle(), DegToRad(widthInDegrees)); - } + GetFeatureProcessor()->SetShadowFilterMethod(GetLightHandle(), method); } + } - void SphereLightDelegate::SetPredictionSampleCount(uint32_t count) + void SphereLightDelegate::SetSofteningBoundaryWidthAngle(float widthInDegrees) + { + if (GetShadowsEnabled() && GetLightHandle().IsValid()) { - if (GetShadowsEnabled() && GetLightHandle().IsValid()) - { - GetFeatureProcessor()->SetPredictionSampleCount(GetLightHandle(), count); - } + GetFeatureProcessor()->SetSofteningBoundaryWidthAngle(GetLightHandle(), DegToRad(widthInDegrees)); } + } - void SphereLightDelegate::SetFilteringSampleCount(uint32_t count) + void SphereLightDelegate::SetPredictionSampleCount(uint32_t count) + { + if (GetShadowsEnabled() && GetLightHandle().IsValid()) { - if (GetShadowsEnabled() && GetLightHandle().IsValid()) - { - GetFeatureProcessor()->SetFilteringSampleCount(GetLightHandle(), count); - } + GetFeatureProcessor()->SetPredictionSampleCount(GetLightHandle(), count); } + } - void SphereLightDelegate::SetPcfMethod(PcfMethod method) + void SphereLightDelegate::SetFilteringSampleCount(uint32_t count) + { + if (GetShadowsEnabled() && GetLightHandle().IsValid()) { - if (GetShadowsEnabled() && GetLightHandle().IsValid()) - { - GetFeatureProcessor()->SetPcfMethod(GetLightHandle(), method); - } + GetFeatureProcessor()->SetFilteringSampleCount(GetLightHandle(), count); } + } - void SphereLightDelegate::SetEsmExponent(float esmExponent) + void SphereLightDelegate::SetPcfMethod(PcfMethod method) + { + if (GetShadowsEnabled() && GetLightHandle().IsValid()) { - if (GetShadowsEnabled() && GetLightHandle().IsValid()) - { - GetFeatureProcessor()->SetEsmExponent(GetLightHandle(), esmExponent); - } + GetFeatureProcessor()->SetPcfMethod(GetLightHandle(), method); } + } - - } // namespace Render -} // namespace AZ + void SphereLightDelegate::SetEsmExponent(float esmExponent) + { + if (GetShadowsEnabled() && GetLightHandle().IsValid()) + { + GetFeatureProcessor()->SetEsmExponent(GetLightHandle(), esmExponent); + } + } +} // namespace AZ::Render diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h index 6dd872d693..984af56c17 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/SphereLightDelegate.h @@ -31,6 +31,7 @@ namespace AZ float GetSurfaceArea() const override; float GetEffectiveSolidAngle() const override { return PhotometricValue::OmnidirectionalSteradians; } void SetEnableShadow(bool enabled) override; + void SetShadowBias(float bias) override; void SetShadowmapMaxSize(ShadowmapSize size) override; void SetShadowFilterMethod(ShadowFilterMethod method) override; void SetSofteningBoundaryWidthAngle(float widthInDegrees) override;