Merge branch 'development' into profiler_capture_api

Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com>
This commit is contained in:
AMZN-ScottR
2021-10-28 12:19:50 -07:00
179 changed files with 25602 additions and 1191 deletions
@@ -9,7 +9,7 @@
#import <AppKit/NSEvent.h>
#include "EditorDefs.h"
#include "QtEditorApplication.h"
#include "QtEditorApplication_mac.h"
// AzFramework
#include <AzFramework/Input/Buses/Notifications/RawInputNotificationBus_Platform.h>
@@ -1788,6 +1788,11 @@ AZStd::string SandboxIntegrationManager::GetComponentEditorIcon(const AZ::Uuid&
return iconPath;
}
AZStd::string SandboxIntegrationManager::GetComponentTypeEditorIcon(const AZ::Uuid& componentType)
{
return GetComponentEditorIcon(componentType, nullptr);
}
AZStd::string SandboxIntegrationManager::GetComponentIconPath(const AZ::Uuid& componentType,
AZ::Crc32 componentIconAttrib, AZ::Component* component)
{
@@ -233,6 +233,7 @@ private:
}
AZStd::string GetComponentEditorIcon(const AZ::Uuid& componentType, AZ::Component* component) override;
AZStd::string GetComponentTypeEditorIcon(const AZ::Uuid& componentType) override;
AZStd::string GetComponentIconPath(const AZ::Uuid& componentType, AZ::Crc32 componentIconAttrib, AZ::Component* component) override;
//////////////////////////////////////////////////////////////////////////
@@ -139,7 +139,7 @@ ComponentDataModel::ComponentDataModel(QObject* parent)
if (element.m_elementId == AZ::Edit::ClassElements::EditorData)
{
AZStd::string iconPath;
EBUS_EVENT_RESULT(iconPath, AzToolsFramework::EditorRequests::Bus, GetComponentEditorIcon, classData->m_typeId, nullptr);
AzToolsFramework::EditorRequestBus::BroadcastResult(iconPath, &AzToolsFramework::EditorRequests::GetComponentTypeEditorIcon, classData->m_typeId);
if (!iconPath.empty())
{
m_componentIcons[classData->m_typeId] = QIcon(iconPath.c_str());
+1 -1
View File
@@ -408,7 +408,7 @@ CTrackViewNodesCtrl::CTrackViewNodesCtrl(QWidget* hParentWnd, CTrackViewDialog*
serializeContext->EnumerateDerived<AZ::Component>([this](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&) -> bool
{
AZStd::string iconPath;
EBUS_EVENT_RESULT(iconPath, AzToolsFramework::EditorRequests::Bus, GetComponentEditorIcon, classData->m_typeId, nullptr);
AzToolsFramework::EditorRequestBus::BroadcastResult(iconPath, &AzToolsFramework::EditorRequests::GetComponentTypeEditorIcon, classData->m_typeId);
if (!iconPath.empty())
{
m_componentTypeToIconMap[classData->m_typeId] = QIcon(iconPath.c_str());
@@ -215,11 +215,6 @@ namespace AZ
m_oldProjectPath = newProjectPath;
// Merge the project.json file into settings registry under ProjectSettingsRootKey path.
AZ::IO::FixedMaxPath projectMetadataFile{ AZ::SettingsRegistryMergeUtils::FindEngineRoot(m_registry) / newProjectPath };
projectMetadataFile /= "project.json";
m_registry.MergeSettingsFile(projectMetadataFile.Native(),
AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey);
// Update all the runtime file paths based on the new "project_path" value.
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry);
}
@@ -0,0 +1,239 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/DOM/DomVisitor.h>
namespace AZ::DOM
{
const char* VisitorError::CodeToString(VisitorErrorCode code)
{
switch (code)
{
case VisitorErrorCode::UnsupportedOperation:
return "operation not supported";
case VisitorErrorCode::InvalidData:
return "invalid data specified";
case VisitorErrorCode::InternalError:
return "internal error";
default:
return "unknown error";
}
}
VisitorError::VisitorError(VisitorErrorCode code)
: m_code(code)
{
}
VisitorError::VisitorError(VisitorErrorCode code, AZStd::string additionalInfo)
: m_code(code)
, m_additionalInfo(AZStd::move(additionalInfo))
{
}
VisitorErrorCode VisitorError::GetCode() const
{
return m_code;
}
const AZStd::string& VisitorError::GetAdditionalInfo() const
{
return m_additionalInfo;
}
AZStd::string VisitorError::FormatVisitorErrorMessage() const
{
if (m_additionalInfo.empty())
{
return AZStd::string::format("VisitorError: %s.", CodeToString(m_code));
}
return AZStd::string::format("VisitorError: %s. %s.", CodeToString(m_code), m_additionalInfo.c_str());
}
Visitor::Result Visitor::VisitorFailure(VisitorErrorCode code)
{
return AZ::Failure(VisitorError(code));
}
Visitor::Result Visitor::VisitorFailure(VisitorErrorCode code, AZStd::string additionalInfo)
{
return AZ::Failure(VisitorError(code, AZStd::move(additionalInfo)));
}
Visitor::Result Visitor::VisitorFailure(VisitorError error)
{
return AZ::Failure(error);
}
Visitor::Result Visitor::VisitorSuccess()
{
return AZ::Success();
}
Visitor::Result Visitor::Null()
{
return VisitorSuccess();
}
Visitor::Result Visitor::Bool([[maybe_unused]] bool value)
{
return VisitorSuccess();
}
Visitor::Result Visitor::Int64([[maybe_unused]] AZ::s64 value)
{
return VisitorSuccess();
}
Visitor::Result Visitor::Uint64([[maybe_unused]] AZ::u64 value)
{
return VisitorSuccess();
}
Visitor::Result Visitor::Double([[maybe_unused]] double value)
{
return VisitorSuccess();
}
Visitor::Result Visitor::String([[maybe_unused]] AZStd::string_view value, [[maybe_unused]] Lifetime lifetime)
{
return VisitorSuccess();
}
Visitor::Result Visitor::OpaqueValue([[maybe_unused]] const OpaqueType& value, [[maybe_unused]] Lifetime lifetime)
{
if (!SupportsOpaqueValues())
{
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Opaque values are not supported by this visitor");
}
return VisitorSuccess();
}
Visitor::Result Visitor::RawValue([[maybe_unused]] AZStd::string_view value, [[maybe_unused]] Lifetime lifetime)
{
if (!SupportsRawValues())
{
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Raw values are not supported by this visitor");
}
return VisitorSuccess();
}
Visitor::Result Visitor::StartObject()
{
if (!SupportsObjects())
{
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Objects are not supported by this visitor");
}
return VisitorSuccess();
}
Visitor::Result Visitor::EndObject([[maybe_unused]] AZ::u64 attributeCount)
{
if (!SupportsObjects())
{
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Objects are not supported by this visitor");
}
return VisitorSuccess();
}
Visitor::Result Visitor::Key([[maybe_unused]] AZ::Name key)
{
if (!SupportsObjects() && !SupportsNodes())
{
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Keys are not supported by this visitor");
}
return VisitorSuccess();
}
Visitor::Result Visitor::RawKey(AZStd::string_view key, [[maybe_unused]] Lifetime lifetime)
{
if (!SupportsRawKeys())
{
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Raw keys are not supported by this visitor");
}
return Key(AZ::Name(key));
}
Visitor::Result Visitor::StartArray()
{
if (!SupportsArrays())
{
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Arrays are not supported by this visitor");
}
return VisitorSuccess();
}
Visitor::Result Visitor::EndArray([[maybe_unused]] AZ::u64 elementCount)
{
if (!SupportsArrays())
{
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Arrays are not supported by this visitor");
}
return VisitorSuccess();
}
Visitor::Result Visitor::StartNode([[maybe_unused]] AZ::Name name)
{
if (!SupportsNodes())
{
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Nodes are not supported by this visitor");
}
return VisitorSuccess();
}
Visitor::Result Visitor::RawStartNode(AZStd::string_view name, [[maybe_unused]] Lifetime lifetime)
{
return StartNode(AZ::Name(name));
}
Visitor::Result Visitor::EndNode([[maybe_unused]] AZ::u64 attributeCount, [[maybe_unused]] AZ::u64 elementCount)
{
if (!SupportsNodes())
{
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Nodes are not supported by this visitor");
}
return VisitorSuccess();
}
VisitorFlags Visitor::GetVisitorFlags() const
{
// By default support raw keys (promoting them to AZ::Name) and support Array / Object / Node
// We leave Opaque type support and Raw Values to more specialized, implementation-specific cases
return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects | VisitorFlags::SupportsNodes;
}
bool Visitor::SupportsRawValues() const
{
return (GetVisitorFlags() & VisitorFlags::SupportsRawValues) != VisitorFlags::Null;
}
bool Visitor::SupportsRawKeys() const
{
return (GetVisitorFlags() & VisitorFlags::SupportsRawKeys) != VisitorFlags::Null;
}
bool Visitor::SupportsObjects() const
{
return (GetVisitorFlags() & VisitorFlags::SupportsObjects) != VisitorFlags::Null;
}
bool Visitor::SupportsArrays() const
{
return (GetVisitorFlags() & VisitorFlags::SupportsArrays) != VisitorFlags::Null;
}
bool Visitor::SupportsNodes() const
{
return (GetVisitorFlags() & VisitorFlags::SupportsNodes) != VisitorFlags::Null;
}
bool Visitor::SupportsOpaqueValues() const
{
return (GetVisitorFlags() & VisitorFlags::SupportsOpaqueValues) != VisitorFlags::Null;
}
} // namespace AZ::DOM
@@ -0,0 +1,237 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Name/Name.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/std/any.h>
#include <AzCore/std/string/string.h>
namespace AZ::DOM
{
//
// Lifetime enum
//
//! Specifies the period in which a reference value will still be alive and safe to read.
enum class Lifetime
{
//! Specifies that the value is safe to read and will remain so indefinitely.
//! This implies that the value will not be mutated for the duration of this storage.
Persistent,
//! Specifies that the value may change or be deallocated, and must be copied to be safely stored.
Temporary,
};
//
// VisitorErrorCode enum
//
//! Error code specifying the reason a Visitor operation failed.
enum class VisitorErrorCode
{
//! Set when a Visitor doesn't have an implementation for a given attribute type.
//! A pure-JSON serializer might reject a Node attribute, for example, and serialization visitors
//! can forbid non-serializable Opaque types.
UnsupportedOperation,
//! Set when a Visitor has received malformed or invalid data.
//! Potential sources include mismatching Begin/End call pairs or invalid attribute or element counts
//! being sent to End methods.
InvalidData,
//! The Visitor failed for some other reason not caused by invalid input.
//! If returning a custom error with this code, it's preferrable to also provide supplemental info
//! in the form of an explanatory string.
InternalError
};
//
// VisitorError class
//
//! Details of the reason for failure within a VisitorInterface operation.
class VisitorError final
{
public:
explicit VisitorError(VisitorErrorCode code);
VisitorError(VisitorErrorCode code, AZStd::string additionalInfo);
//! Gets the error code associated with this error.
VisitorErrorCode GetCode() const;
//! Gets a supplemental error info string from the error.
//! Returns an empty string if no additional information was provided to the error.
const AZStd::string& GetAdditionalInfo() const;
//! Provides a formatted, human-readable error description that can be used for logging purposes.
AZStd::string FormatVisitorErrorMessage() const;
//! Helper method, translates a VisitorErrorCode to a human readable string.
static const char* CodeToString(VisitorErrorCode code);
private:
VisitorErrorCode m_code;
AZStd::string m_additionalInfo;
};
//! A type alias for opaque DOM types that aren't meant to be serializable.
//! /see VisitorInterface::OpaqueValue
using OpaqueType = AZStd::any;
//
// VisitorFlags enum
//
//! Flags representning capabilities of a \ref Visitor.
enum class VisitorFlags : AZ::u16
{
//! No flags are set. This can be used in conjunction with bitwise operators to check a flag.
Null = 0,
//! If set, this Visitor interface supports raw strings in place of specific value types.
//! Visitors with this flag accept RawValue calls in lieu of more specific value calls such as Int64 or String.
SupportsRawValues = (1 << 1),
//! If set, this Visitor interface supports raw strings in place of Name types for keys and Node names.
//! Visitors with this flag accept RawKey and RawStartNode in lieu of Key and StartNode calls.
SupportsRawKeys = (1 << 2),
//! If set, this Visitor interface supports Object types described via BeginObject and EndObject.
SupportsObjects = (1 << 3),
//! If set, this Visitor interface supports Array types described via BeginArray and EndArray.
SupportsArrays = (1 << 4),
//! If set, this Visitor interface supports Node types described BeginNode and EndNode.
SupportsNodes = (1 << 4),
//! If set, this Visitor interface supports opaque values described via OpaqueValue.
SupportsOpaqueValues = (1 << 5),
};
AZ_DEFINE_ENUM_BITWISE_OPERATORS(VisitorFlags);
//
// Visitor class
//
//! An interface for performing operations on elements of a generic DOM (Document Object Model).
//! A Document Object Model is defined here as a tree structure comprised of one of the following values:
//! - Primitives: plain data types, including
//! - \ref Int64: 64 bit signed integer
//! - \ref Uint64: 64 bit unsigned integer
//! - \ref Bool: boolean value
//! - \ref Double: 64 bit double precision float
//! - \ref Null: sentinel "empty" type with no value representation
//! - \ref String: UTF8 encoded string
//! - \ref Object: an ordered container of key/value pairs where keys are AZ::Names and values may be any DOM type
//! (including Object)
//! - \ref Array: an ordered container of values, in which values are any DOM value type (including Array)
//! - \ref Node: a container
//! - \ref OpaqueValue: An arbitrary value stored in an AZStd::any. This is a non-serializable representation of an
//! entry useful for in-memory options. This is intended to be used as an intermediate value over the course of DOM
//! transformation and as a proxy to pass through types of which the DOM has no knowledge to other systems.
//!
//! Opaque values are rejected by the default VisitorInterface implementation.
//!
//! Care should be ensured that DOMs representing opaque types are only visited by consumers that understand them.
class Visitor
{
public:
virtual ~Visitor() = default;
//! The result of a Visitor operation.
//! A failure indicates a non-recoverable issue and signals that no further visit calls may be made in the
//! current state.
using Result = AZ::Outcome<void, VisitorError>;
//! Returns a set of flags representing the operations this Visitor supports.
//! The base implementation supports raw keys (\see VisitorFlags::SupportsRawKeys) and
//! arrays (\see VisitorFlags::SupportsArrays), objects (\see VisitorFlags::SupportsObjects), and
//! nodes (\see VisitorFlags::SupportsNodes).
//! Raw (\see VisitorFlags::SupportsRawValues) and opaque values (\see VisitorFlags::SupportsOpaqueValues)
//! are disallowed by default, as their handling is intended to be implementation-specific.
virtual VisitorFlags GetVisitorFlags() const;
//! /see VisitorFlags::SupportsRawValues
bool SupportsRawValues() const;
//! /see VisitorFlags::SupportsRawKeys
bool SupportsRawKeys() const;
//! /see VisitorFlags::SupportsObjects
bool SupportsObjects() const;
//! /see VisitorFlags::SupportsArrays
bool SupportsArrays() const;
//! /see VisitorFlags::SupportsNodes
bool SupportsNodes() const;
//! /see VisitorFlags::SupportsOpaqueValues
bool SupportsOpaqueValues() const;
//! Operates on an empty null value.
virtual Result Null();
//! Operates on a bool value.
virtual Result Bool(bool value);
//! Operates on a signed, 64 bit integer value.
virtual Result Int64(AZ::s64 value);
//! Operates on an unsigned, 64 bit integer value.
virtual Result Uint64(AZ::u64 value);
//! Operates on a double precision, 64 bit floating point value.
virtual Result Double(double value);
//! Operates on a string value. As strings are a reference type.
//! Storage semantics are provided to indicate where the value may be stored persistently or requires a copy.
virtual Result String(AZStd::string_view value, Lifetime lifetime);
//! Operates on an opaque value. As opaque values are a reference type, storage semantics are provided to
//! indicate where the value may be stored persistently or requires a copy.
//! The base implementation of OpaqueValue rejects the operation, as opaque values are meant for special
//! cases with specific implementations, not generic usage.
//! Storage semantics are provided to indicate where the value may be stored persistently or requires a copy.
virtual Result OpaqueValue(const OpaqueType& value, Lifetime lifetime);
//! Operates on a raw value encoded as a UTF-8 string that hasn't had its type deduced.
//! Visitors that support raw values (\see VisitorFlags::SupportsRawValues) may parse the raw value and
//! forward it to the corresponding value call or calls of their choice.
//! The base implementation of RawValue rejects the operation, as raw values are meant to be handled on
//! a per-implementation basis.
virtual Result RawValue(AZStd::string_view value, Lifetime lifetime);
//! Operates on an Object.
//! Callers may make any number of Key calls, followed by calls representing a value (including a nested
//! StartObject call) and then must call EndObject.
virtual Result StartObject();
//! Finishes operating on an Object.
//! Callers must provide the number of attributes that were provided to the object, i.e. the number of key
//! and value calls made within the direct context of this object (but not any nested objects / nodes).
virtual Result EndObject(AZ::u64 attributeCount);
//! Specifies a key for a key/value pair.
//! Key must be called subsequent to a call to \ref StartObject or \ref StartNode and immediately followed by
//! calls representing the key's associated value.
virtual Result Key(AZ::Name key);
//! Specifies a key for a key/value pair using a raw string instead of \ref AZ::Name.
//! \see Key
virtual Result RawKey(AZStd::string_view key, Lifetime lifetime);
//! Operates on an Array.
//! Callers may make any number of subsequent value calls to represent the elements of the array, and then must
//! call EndArray.
virtual Result StartArray();
//! Finishes operating on an Array.
//! Callers must provide the number of elements that were provided to the array, i.e. the number of value calls
//! made within the direct context of this array (but not any nested arrays / nodes).
virtual Result EndArray(AZ::u64 elementCount);
//! Operates on a Node.
//! Callers may make any number of Key calls followed by value calls or value calls not prefixed with a Key
//! call, and then must call EndNode. See \ref StartObject and \ref StartArray as Node types combine the
//! functionality of both structures into a named Node structure.
virtual Result StartNode(AZ::Name name);
//! Operates on a Node using a raw string instead of \ref AZ::Name.
//! \see StartNode
virtual Result RawStartNode(AZStd::string_view name, Lifetime lifetime);
//! Finishes operating on a Node.
//! Callers must provide both the number of attributes the were provided and the number of elements that were
//! provided to the node, attributes being values prefaced by a call to Key.
virtual Result EndNode(AZ::u64 attributeCount, AZ::u64 elementCount);
protected:
Visitor() = default;
//! Helper method, constructs a failure \ref Result with the specified code.
static Result VisitorFailure(VisitorErrorCode code);
//! Helper method, constructs a failure \ref Result with the specified code and supplemental info.
static Result VisitorFailure(VisitorErrorCode code, AZStd::string additionalInfo);
//! Helper method, constructs a failure \ref Result with the specified error.
static Result VisitorFailure(VisitorError error);
//! Helper method, constructs a success \ref Result.
static Result VisitorSuccess();
};
} // namespace AZ::DOM
@@ -634,12 +634,18 @@ namespace AZ::SettingsRegistryMergeUtils
}
// Project name - if it was set via merging project.json use that value, otherwise use the project path's folder name.
auto projectNameKey =
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey)
constexpr auto projectNameKey =
FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey)
+ "/project_name";
AZ::SettingsRegistryInterface::FixedValueString projectName;
if (!registry.Get(projectName, projectNameKey))
// Read the project name from the project.json file if it exists
if (AZ::IO::FixedMaxPath projectJsonPath = normalizedProjectPath / "project.json";
AZ::IO::SystemFile::Exists(projectJsonPath.c_str()))
{
registry.MergeSettingsFile(projectJsonPath.Native(),
AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey);
}
if (FixedValueString projectName; !registry.Get(projectName, projectNameKey))
{
projectName = path.Filename().Native();
registry.Set(projectNameKey, projectName);
@@ -125,6 +125,8 @@ set(FILES
Debug/TraceMessagesDrillerBus.h
Debug/TraceReflection.cpp
Debug/TraceReflection.h
DOM/DomVisitor.cpp
DOM/DomVisitor.h
Driller/DefaultStringPool.h
Driller/Driller.cpp
Driller/Driller.h
@@ -258,10 +258,17 @@ namespace AzFramework
////////////////////////////////////////////////////////////////////////////////////////////////
void XcbNativeWindow::SetWindowTitle(const AZStd::string& title)
{
// Set the title of both the window and the task bar by using
// a buffer to hold the title twice, separated by a null-terminator
auto doubleTitleSize = (title.size() + 1) * 2;
AZStd::string doubleTitle(doubleTitleSize, '\0');
azstrncpy(doubleTitle.data(), doubleTitleSize, title.c_str(), title.size());
azstrncpy(&doubleTitle.data()[title.size() + 1], title.size(), title.c_str(), title.size());
xcb_void_cookie_t xcbCheckResult;
xcbCheckResult = xcb_change_property(
m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, static_cast<uint32_t>(title.size()),
title.c_str());
m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, static_cast<uint32_t>(doubleTitle.size()),
doubleTitle.c_str());
AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set window title.");
}
@@ -82,7 +82,7 @@ namespace AzGameFramework
// Used the lowercase the platform name since the bootstrap.game.<config>.<platform>.setreg is being loaded
// from the asset cache root where all the files are in lowercased from regardless of the filesystem case-sensitivity
static constexpr char filename[] = "bootstrap.game." AZ_BUILD_CONFIGURATION_TYPE "." AZ_TRAIT_OS_PLATFORM_CODENAME_LOWER ".setreg";
static constexpr char filename[] = "bootstrap.game." AZ_BUILD_CONFIGURATION_TYPE ".setreg";
AZ::IO::FixedMaxPath cacheRootPath;
if (registry.Get(cacheRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
@@ -22,6 +22,7 @@ namespace AzQtComponents
, m_closeOnClick(true)
, m_ui(new Ui::ToastNotification())
, m_fadeAnimation(nullptr)
, m_configuration(toastConfiguration)
{
setProperty("HasNoWindowDecorations", true);
@@ -80,7 +81,13 @@ namespace AzQtComponents
}
ToastNotification::~ToastNotification()
{
{
}
bool ToastNotification::IsDuplicate(const ToastConfiguration& toastConfiguration)
{
return toastConfiguration.m_title == m_configuration.m_title
&& toastConfiguration.m_description == m_configuration.m_description;
}
void ToastNotification::paintEvent(QPaintEvent* event)
@@ -45,6 +45,8 @@ namespace AzQtComponents
void ShowToastAtPoint(const QPoint& screenPosition, const QPointF& anchorPoint);
void UpdatePosition(const QPoint& screenPosition, const QPointF& anchorPoint);
bool IsDuplicate(const ToastConfiguration& toastConfiguration);
// QDialog
void showEvent(QShowEvent* showEvent) override;
@@ -64,7 +66,7 @@ namespace AzQtComponents
private:
QPropertyAnimation* m_fadeAnimation;
ToastConfiguration m_configuration;
bool m_closeOnClick;
QTimer m_lifeSpan;
uint32_t m_borderRadius = 0;
@@ -826,6 +826,10 @@ namespace AzToolsFramework
/// Path will be empty if component should have no icon.
virtual AZStd::string GetComponentEditorIcon(const AZ::Uuid& /*componentType*/, AZ::Component* /*component*/) { return AZStd::string(); }
//! Return path to icon for component type.
//! Path will be empty if component type should have no icon.
virtual AZStd::string GetComponentTypeEditorIcon(const AZ::Uuid& /*componentType*/) { return AZStd::string(); }
/**
* Return the icon image path based on the component type and where it is used.
* \param componentType component type
@@ -43,6 +43,8 @@ namespace AzToolsFramework
};
//! Provides a bus to notify when the different editor modes are entered/exit.
//! @note The editor modes are not discrete states but rather each progression of mode retain the active the parent
//! mode that the new mode progressed from.
class ViewportEditorModeNotifications : public AZ::EBusTraits
{
public:
@@ -9,9 +9,27 @@
#include <AzToolsFramework/Application/EditorEntityManager.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
namespace AzToolsFramework
{
static bool AreEntitiesValidForDuplication(const EntityIdList& entityIds)
{
for (AZ::EntityId entityId : entityIds)
{
if (GetEntityById(entityId) == nullptr)
{
AZ_Error(
"Entity", false,
"Entity with id '%llu' is not found. This can happen when you try to duplicate the entity before it is created. Please "
"ensure entities are created before trying to duplicate them.",
static_cast<AZ::u64>(entityId));
return false;
}
}
return true;
}
void EditorEntityManager::Start()
{
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
@@ -62,7 +80,11 @@ namespace AzToolsFramework
EntityIdList selectedEntities;
ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequests::GetSelectedEntities);
m_prefabPublicInterface->DuplicateEntitiesInInstance(selectedEntities);
if (AreEntitiesValidForDuplication(selectedEntities))
{
m_prefabPublicInterface->DuplicateEntitiesInInstance(selectedEntities);
}
}
void EditorEntityManager::DuplicateEntityById(AZ::EntityId entityId)
@@ -72,7 +94,10 @@ namespace AzToolsFramework
void EditorEntityManager::DuplicateEntities(const EntityIdList& entities)
{
m_prefabPublicInterface->DuplicateEntitiesInInstance(entities);
if (AreEntitiesValidForDuplication(entities))
{
m_prefabPublicInterface->DuplicateEntitiesInInstance(entities);
}
}
}
@@ -34,5 +34,4 @@ namespace AzToolsFramework
private:
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
};
}
@@ -176,7 +176,7 @@ namespace AzToolsFramework
, public AZ::BehaviorEBusHandler
{
AZ_EBUS_BEHAVIOR_BINDER(ToolsApplicationNotificationBusHandler, "{7EB67956-FF86-461A-91E2-7B08279CFACF}", AZ::SystemAllocator,
EntityRegistered, EntityDeregistered);
EntityRegistered, EntityDeregistered, AfterEntitySelectionChanged);
void EntityRegistered(AZ::EntityId entityId) override
{
@@ -187,6 +187,11 @@ namespace AzToolsFramework
{
Call(FN_EntityDeregistered, entityId);
}
void AfterEntitySelectionChanged(const EntityIdList& newlySelectedEntities, const EntityIdList& newlyDeselectedEntities) override
{
Call(FN_AfterEntitySelectionChanged, newlySelectedEntities, newlyDeselectedEntities);
}
};
struct ViewPaneCallbackBusHandler final
@@ -410,6 +415,7 @@ namespace AzToolsFramework
->Handler<Internal::ToolsApplicationNotificationBusHandler>()
->Event("EntityRegistered", &ToolsApplicationEvents::EntityRegistered)
->Event("EntityDeregistered", &ToolsApplicationEvents::EntityDeregistered)
->Event("AfterEntitySelectionChanged", &ToolsApplicationEvents::AfterEntitySelectionChanged)
;
behaviorContext->Class<ViewPaneOptions>()
@@ -428,6 +434,7 @@ namespace AzToolsFramework
->Attribute(AZ::Script::Attributes::Module, "editor")
->Event("RegisterCustomViewPane", &EditorRequests::RegisterCustomViewPane)
->Event("UnregisterViewPane", &EditorRequests::UnregisterViewPane)
->Event("GetComponentTypeEditorIcon", &EditorRequests::GetComponentTypeEditorIcon)
;
behaviorContext->EBus<EditorEventsBus>("EditorEventBus")
@@ -1110,7 +1110,8 @@ namespace AzToolsFramework
// Select the duplicated entities/instances
auto selectionUndo = aznew SelectionCommand(duplicatedEntityAndInstanceIds, "Select Duplicated Entities/Instances");
selectionUndo->SetParent(undoBatch.GetUndoBatch());
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, duplicatedEntityAndInstanceIds);
ToolsApplicationRequestBus::Broadcast(
&ToolsApplicationRequestBus::Events::SetSelectedEntities, duplicatedEntityAndInstanceIds);
}
return AZ::Success(AZStd::move(duplicatedEntityAndInstanceIds));
@@ -90,7 +90,7 @@ namespace AzToolsFramework
}
AZStd::string componentIconPath;
EBUS_EVENT_RESULT(componentIconPath, AzToolsFramework::EditorRequests::Bus, GetComponentEditorIcon, componentClass->m_typeId, nullptr);
AzToolsFramework::EditorRequestBus::BroadcastResult(componentIconPath, &AzToolsFramework::EditorRequests::GetComponentTypeEditorIcon, componentClass->m_typeId);
componentIconTable[componentClass] = QString::fromUtf8(componentIconPath.c_str());
}
@@ -63,6 +63,12 @@ namespace AzToolsFramework
ToastId ToastNotificationsView::ShowToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration)
{
// reject duplicate messages
if (m_rejectDuplicates && DuplicateNotificationInQueue(toastConfiguration))
{
return ToastId();
}
ToastId toastId = CreateToastNotification(toastConfiguration);
m_queuedNotifications.emplace_back(toastId);
@@ -70,10 +76,28 @@ namespace AzToolsFramework
{
DisplayQueuedNotification();
}
else if (m_queuedNotifications.size() >= m_maxQueuedNotifications)
{
// hiding the active toast will cause the next toast to be displayed
HideToastNotification(m_activeNotification);
}
return toastId;
}
bool ToastNotificationsView::DuplicateNotificationInQueue(const AzQtComponents::ToastConfiguration& toastConfiguration)
{
for (auto iter : m_notifications)
{
if (iter.second && iter.second->IsDuplicate(toastConfiguration))
{
return true;
}
}
return false;
}
ToastId ToastNotificationsView::ShowToastAtCursor(const AzQtComponents::ToastConfiguration& toastConfiguration)
{
ToastId toastId = CreateToastNotification(toastConfiguration);
@@ -187,4 +211,14 @@ namespace AzToolsFramework
{
m_anchorPoint = anchorPoint;
}
void ToastNotificationsView::SetMaxQueuedNotifications(AZ::u32 maxQueuedNotifications)
{
m_maxQueuedNotifications = maxQueuedNotifications;
}
void ToastNotificationsView::SetRejectDuplicates(bool rejectDuplicates)
{
m_rejectDuplicates = rejectDuplicates;
}
}
@@ -52,10 +52,13 @@ namespace AzToolsFramework
void SetOffset(const QPoint& offset);
void SetAnchorPoint(const QPointF& anchorPoint);
void SetMaxQueuedNotifications(AZ::u32 maxQueuedNotifications);
void SetRejectDuplicates(bool rejectDuplicates);
private:
ToastId CreateToastNotification(const AzQtComponents::ToastConfiguration& toastConfiguration);
void DisplayQueuedNotification();
bool DuplicateNotificationInQueue(const AzQtComponents::ToastConfiguration& toastConfiguration);
QPoint GetGlobalPoint();
ToastId m_activeNotification;
@@ -64,5 +67,7 @@ namespace AzToolsFramework
QPoint m_offset = QPoint(10, 10);
QPointF m_anchorPoint = QPointF(1, 0);
AZ::u32 m_maxQueuedNotifications = 5;
bool m_rejectDuplicates = true;
};
} // AzToolsFramework
@@ -72,7 +72,7 @@ namespace AzToolsFramework
void EntityOutlinerTreeView::leaveEvent([[maybe_unused]] QEvent* event)
{
m_mousePosition = QPoint();
m_mousePosition = QPoint(-1, -1);
m_currentHoveredIndex = QModelIndex();
update();
}
@@ -200,7 +200,7 @@ namespace AzToolsFramework
const bool isEnabled = (this->model()->flags(index) & Qt::ItemIsEnabled);
const bool isSelected = selectionModel()->isSelected(index);
const bool isHovered = (index == indexAt(m_mousePosition)) && isEnabled;
const bool isHovered = (index == indexAt(m_mousePosition).siblingAtColumn(0)) && isEnabled;
// Paint the branch Selection/Hover Rect
PaintBranchSelectionHoverRect(painter, rect, isSelected, isHovered);
@@ -153,6 +153,9 @@ namespace AzToolsFramework
{
initEntityOutlinerWidgetResources();
m_editorEntityUiInterface = AZ::Interface<AzToolsFramework::EditorEntityUiInterface>::Get();
AZ_Assert(m_editorEntityUiInterface != nullptr, "EntityOutlinerWidget requires a EditorEntityUiInterface instance on Initialize.");
m_gui = new Ui::EntityOutlinerWidgetUI();
m_gui->setupUi(this);
@@ -282,12 +285,6 @@ namespace AzToolsFramework
m_listModel->Initialize();
m_editorEntityUiInterface = AZ::Interface<AzToolsFramework::EditorEntityUiInterface>::Get();
AZ_Assert(
m_editorEntityUiInterface != nullptr,
"EntityOutlinerWidget requires a EditorEntityUiInterface instance on Initialize.");
EditorPickModeNotificationBus::Handler::BusConnect(GetEntityContextId());
EntityHighlightMessages::Bus::Handler::BusConnect();
EntityOutlinerModelNotificationBus::Handler::BusConnect();
@@ -583,7 +583,7 @@ namespace AzToolsFramework
}
AZStd::string iconPath;
EBUS_EVENT_RESULT(iconPath, AzToolsFramework::EditorRequests::Bus, GetComponentEditorIcon, componentType, const_cast<AZ::Component*>(&componentInstance));
AzToolsFramework::EditorRequestBus::BroadcastResult(iconPath, &AzToolsFramework::EditorRequests::GetComponentEditorIcon, componentType, const_cast<AZ::Component*>(&componentInstance));
GetHeader()->SetIcon(QIcon(iconPath.c_str()));
bool isExpanded = true;
@@ -427,8 +427,8 @@ namespace AzToolsFramework::ViewportUi::Internal
void ViewportUiDisplay::PositionUiOverlayOverRenderViewport()
{
QPoint offset = m_renderOverlay->mapToGlobal(QPoint());
m_uiMainWindow.move(offset);
m_uiOverlay.setFixedSize(m_renderOverlay->width(), m_renderOverlay->height());
m_uiMainWindow.setGeometry(offset.x(), offset.y(), m_renderOverlay->width(), m_renderOverlay->height());
m_uiOverlay.setGeometry(m_uiMainWindow.rect());
UpdateUiOverlayGeometry();
}
@@ -8,6 +8,7 @@
#include <AzTest/AzTest.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/ViewportSelection/EditorPickEntitySelection.h>
@@ -187,10 +188,13 @@ namespace UnitTest
ASSERT_NE(m_viewportEditorModeTracker, nullptr);
m_viewportEditorModes = m_viewportEditorModeTracker->GetViewportEditorModes({AzToolsFramework::GetEntityContextId()});
ASSERT_NE(m_viewportEditorModes, nullptr);
m_focusModeInterface = AZ::Interface<AzToolsFramework::FocusModeInterface>::Get();
ASSERT_NE(m_focusModeInterface, nullptr);
}
ViewportEditorModeTrackerInterface* m_viewportEditorModeTracker = nullptr;
const ViewportEditorModesInterface* m_viewportEditorModes = nullptr;
AzToolsFramework::FocusModeInterface* m_focusModeInterface = nullptr;
};
TEST_F(ViewportEditorModesTestsFixture, NumberOfEditorModesIsEqualTo4)
@@ -522,32 +526,48 @@ namespace UnitTest
}
TEST_F(
ViewportEditorModeTrackerIntegrationTestFixture, EnteringComponentModeAfterInitialStateHasViewportEditorModesDefaultAndComponentModeActive)
ViewportEditorModeTrackerIntegrationTestFixture,
EnteringComponentModeAfterInitialStateHasViewportEditorModesDefaultAndComponentModeActive)
{
// When component mode is entered
AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
&AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode,
AZStd::vector<AzToolsFramework::ComponentModeFramework::EntityAndComponentModeBuilders>{});
bool inComponentMode = false;
AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::BroadcastResult(
inComponentMode, &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::InComponentMode);
// Expect to be in component mode
EXPECT_TRUE(inComponentMode);
EXPECT_TRUE(AzToolsFramework::ComponentModeFramework::InComponentMode());
// Expect the default and component viewport editor modes to be active
EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default));
EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Component));
// Do not expect the pick and focus viewport editor modes to be active
EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Pick));
EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Focus));
}
TEST_F(
ViewportEditorModeTrackerIntegrationTestFixture,
EnteringEditorPickEntitySelectionAfterInitialStateHasOnlyViewportEditorModePickModeActive)
ExitingComponentModeAfterEnteringFrominitialStateHasViewportEditorModesDefaultActive)
{
// When component mode is entered and exited
AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
&AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode,
AZStd::vector<AzToolsFramework::ComponentModeFramework::EntityAndComponentModeBuilders>{});
EXPECT_TRUE(AzToolsFramework::ComponentModeFramework::InComponentMode());
AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
&AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::EndComponentMode);
// Expect to not be in component mode
EXPECT_FALSE(AzToolsFramework::ComponentModeFramework::InComponentMode());
// Expect only the default viewport editor mode to be active
ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Default);
}
TEST_F(
ViewportEditorModeTrackerIntegrationTestFixture,
EnteringEditorPickEntitySelectionAfterInitialStateHasOnlyViewportEditorModePickActive)
{
// When entering pick mode
using AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus;
@@ -563,6 +583,96 @@ namespace UnitTest
ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Pick);
}
// FocusMode integration tests will follow (LYN-6995)
TEST_F(
ViewportEditorModeTrackerIntegrationTestFixture,
EnteringEditorDefaultEntitySelectionFromEditorPickEntitySelectionHasOnlyViewportEditorModeDefaultActive)
{
// When pick mode is entered and exited
using AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus;
EditorInteractionSystemViewportSelectionRequestBus::Event(
AzToolsFramework::GetEntityContextId(), &EditorInteractionSystemViewportSelectionRequestBus::Events::SetHandler,
[](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache,
[[maybe_unused]] AzToolsFramework::ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
{
return AZStd::make_unique<AzToolsFramework::EditorPickEntitySelection>(entityDataCache, viewportEditorModeTracker);
});
EditorInteractionSystemViewportSelectionRequestBus::Event(
AzToolsFramework::GetEntityContextId(), &EditorInteractionSystemViewportSelectionRequestBus::Events::SetHandler,
[](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache,
[[maybe_unused]] AzToolsFramework::ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
{
return AZStd::make_unique<AzToolsFramework::EditorDefaultSelection>(entityDataCache, viewportEditorModeTracker);
});
// Expect only the default viewport editor mode to be active
ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Default);
}
TEST_F(ViewportEditorModeTrackerIntegrationTestFixture, EnteringFocusModeAfterInitialStateHasViewportEditorModeDefaultAndPickActive)
{
// When entering focus mode
m_focusModeInterface->SetFocusRoot(AZ::EntityId{ 1 });
// Expect the default and focus viewport editor modes to be active
EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default));
EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Focus));
EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Pick));
EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Component));
}
TEST_F(
ViewportEditorModeTrackerIntegrationTestFixture,
ExitingFocusModeAfterEnteringFromInitialStateHasOnlyViewportEditorModeDefaultActive)
{
// When entering and leaving focus mode
m_focusModeInterface->SetFocusRoot(AZ::EntityId{ 1 });
m_focusModeInterface->SetFocusRoot(AZ::EntityId());
// Expect only the default mode to be active
ExpectOnlyModeActive(*m_viewportEditorModes, ViewportEditorMode::Default);
}
TEST_F(ViewportEditorModeTrackerIntegrationTestFixture, EnteringComponentModeFromFocusModeStateHasViewportEditorModeDefaultAndFocusAndComponentActive)
{
// When entering component mode from focus mode
m_focusModeInterface->SetFocusRoot(AZ::EntityId{ 1 });
AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
&AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode,
AZStd::vector<AzToolsFramework::ComponentModeFramework::EntityAndComponentModeBuilders>{});
// Expect to be in component mode
EXPECT_TRUE(AzToolsFramework::ComponentModeFramework::InComponentMode());
// Expect the default, focus and component viewport editor modes to be active
EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default));
EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Focus));
EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Component));
EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Pick));
}
TEST_F(
ViewportEditorModeTrackerIntegrationTestFixture,
ExitingComponentModeAfterEnteringFromFocusModeHasViewportEditorModeDefaultAndFocusActive)
{
// When entering and leaving component mode from focus mode
m_focusModeInterface->SetFocusRoot(AZ::EntityId{ 1 });
AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
&AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::BeginComponentMode,
AZStd::vector<AzToolsFramework::ComponentModeFramework::EntityAndComponentModeBuilders>{});
EXPECT_TRUE(AzToolsFramework::ComponentModeFramework::InComponentMode());
AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast(
&AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequests::EndComponentMode);
// Expect to not be in component mode
EXPECT_FALSE(AzToolsFramework::ComponentModeFramework::InComponentMode());
// Expect the default and focus viewport editor modes to be active
EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Default));
EXPECT_TRUE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Focus));
EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Component));
EXPECT_FALSE(m_viewportEditorModes->IsModeActive(ViewportEditorMode::Pick));
}
} // namespace UnitTest
@@ -10,12 +10,13 @@
#pragma once
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/AZStdContainers.inl>
#include <AzCore/std/string/regex.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/parallel/atomic.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/containers/bitset.h>
#include <AzFramework/Asset/AssetProcessorMessages.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
@@ -259,6 +259,11 @@ namespace AssetProcessor
scratchBuffer.reserve(512 * 1024); // Reserve 512kb to avoid repeatedly resizing the buffer;
AZStd::fixed_vector<AZStd::string_view, AzFramework::MaxPlatformCodeNames> platformCodes;
AzFramework::PlatformHelper::AppendPlatformCodeNames(platformCodes, request.m_platformInfo.m_identifier);
AZ_Assert(platformCodes.size() <= 1, "A one-to-one mapping of asset type platform identifier"
" to platform codename is required in the SettingsRegistryBuilder."
" The bootstrap.game is now only produced per build configuration and doesn't take into account"
" different platforms names");
const AZStd::string& assetPlatformIdentifier = request.m_jobDescription.GetPlatformIdentifier();
// Determines the suffix that will be used for the launcher based on processing server vs non-server assets
const char* launcherType = assetPlatformIdentifier != AzFramework::PlatformHelper::GetPlatformName(AzFramework::PlatformId::SERVER)
@@ -293,9 +298,10 @@ namespace AssetProcessor
outputBuffer.Reserve(512 * 1024); // Reserve 512kb to avoid repeatedly resizing the buffer;
SettingsExporter exporter(outputBuffer, excludes);
for (AZStd::string_view platform : platformCodes)
if (!platformCodes.empty())
{
AZ::u32 productSubID = static_cast<AZ::u32>(AZStd::hash<AZStd::string_view>{}(platform)); // Deliberately ignoring half the bits.
AZStd::string_view platform = platformCodes.front();
constexpr AZ::u32 productSubID = 0;
for (size_t i = 0; i < AZStd::size(specializations); ++i)
{
const AZ::SettingsRegistryInterface::Specializations& specialization = specializations[i];
@@ -337,7 +343,7 @@ namespace AssetProcessor
// The purpose of this section is to copy the Gem's SourcePaths from the Global Settings Registry
// the local SettingsRegistry. The reason this is needed is so that the call to
// `MergeSettingsToRegistry_GemRegistries` below is able to locate each gem's "<gem-root>/Registry" folder
// that will be merged into the bootstrap.game.<configuration>.<platform>.setreg file
// that will be merged into the bootstrap.game.<configuration>.setreg file
// This is used by the GameLauncher applications to read from a single merged .setreg file
// containing the settings needed to run a game/simulation without have access to the source code base registry
AZStd::vector<AzFramework::GemInfo> gemInfos;
@@ -408,8 +414,6 @@ namespace AssetProcessor
}
outputPath += specialization.GetSpecialization(0); // Append configuration
outputPath += '.';
outputPath += platform;
outputPath += ".setreg";
AZ::IO::SystemFile file;
@@ -281,7 +281,7 @@ namespace AssetProcessor
ExcludeAssetRecognizer excludeRecogniser;
excludeRecogniser.m_name = "backup";
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(".*\\/savebackup\\/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("(^|.+/)savebackup/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
config.AddExcludeRecognizer(excludeRecogniser);
}
@@ -123,7 +123,7 @@ namespace AssetProcessor
ExcludeAssetRecognizer excludeRecogniser;
excludeRecogniser.m_name = "backup";
// we are excluding all the files in the folder but not the folder itself
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(".*\\/subfolder2\\/aaa\\/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("(^|[^/]+/)aaa/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
m_platformConfig.get()->AddExcludeRecognizer(excludeRecogniser);
m_assetScanner.get()->StartScan();
@@ -144,7 +144,7 @@ namespace AssetProcessor
ExcludeAssetRecognizer excludeRecogniser;
excludeRecogniser.m_name = "backup";
// we are excluding the complete folder here
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(".*\\/subfolder2\\/aaa", AssetBuilderSDK::AssetBuilderPattern::Regex);
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("(^|[^/]+/)aaa", AssetBuilderSDK::AssetBuilderPattern::Regex);
m_platformConfig.get()->AddExcludeRecognizer(excludeRecogniser);
m_assetScanner.get()->StartScan();
@@ -405,6 +405,8 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularExcludes)
auto configRoot = AZ::IO::FileIOBase::GetInstance()->ResolvePath("@exefolder@/testdata/config_regular");
ASSERT_TRUE(configRoot);
UnitTestPlatformConfiguration config;
config.AddScanFolder(ScanFolderInfo("blahblah", "Blah ScanFolder", "sf2", true, true), true);
m_absorber.Clear();
ASSERT_TRUE(config.InitializeFromConfigFiles(configRoot->c_str(), testExeFolder->c_str(), EmptyDummyProjectName, false, false));
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
@@ -347,7 +347,7 @@ namespace AssetProcessor
ExcludeAssetRecognizer excludeRecogniser;
excludeRecogniser.m_name = "backup";
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(".*\\/savebackup\\/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
excludeRecogniser.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("(^|.+/)savebackup/.*", AssetBuilderSDK::AssetBuilderPattern::Regex);
config.AddExcludeRecognizer(excludeRecogniser);
AssetProcessorManager_Test apm(&config); // note, this will 'push' the scan folders in to the db.
@@ -1791,13 +1791,8 @@ namespace AssetProcessor
UNIT_TEST_EXPECT_TRUE((newfingerprintForPCAfterVersionChange != fingerprintForPC) || (newfingerprintForPCAfterVersionChange != newfingerprintForPC));//Fingerprints should be different
UNIT_TEST_EXPECT_TRUE((newfingerprintForANDROIDAfterVersionChange != fingerprintForANDROID) || (newfingerprintForANDROIDAfterVersionChange != newfingerprintForANDROID));//Fingerprints should be different
//------Test for Files which are excluded
processResults.clear();
absolutePath = AssetUtilities::NormalizeFilePath(tempPath.absoluteFilePath("subfolder3/savebackup/test.txt"));
QMetaObject::invokeMethod(&apm, "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, absolutePath));
UNIT_TEST_EXPECT_FALSE(BlockUntil(idling, 3000)); //Processing a file that will be excluded should not cause assetprocessor manager to emit the onBecameIdle signal because its state should not change
UNIT_TEST_EXPECT_TRUE(processResults.size() == 0);
// ------------- Test querying asset status -------------------
{
absolutePath = tempPath.absoluteFilePath("subfolder2/folder/ship.tiff");
@@ -1633,13 +1633,18 @@ namespace AssetProcessor
bool AssetProcessor::PlatformConfiguration::IsFileExcluded(QString fileName) const
{
for (const ExcludeAssetRecognizer& excludeRecognizer : m_excludeAssetRecognizers)
QString relPath, scanFolderName;
if (ConvertToRelativePath(fileName, relPath, scanFolderName))
{
if (excludeRecognizer.m_patternMatcher.MatchesPath(fileName.toUtf8().constData()))
for (const ExcludeAssetRecognizer& excludeRecognizer : m_excludeAssetRecognizers)
{
return true;
if (excludeRecognizer.m_patternMatcher.MatchesPath(relPath.toUtf8().constData()))
{
return true;
}
}
}
return false;
}
@@ -50,10 +50,10 @@
"order": 6000
},
"Exclude HoldFiles": {
"pattern": ".*\\\\/Levels\\\\/.*_hold\\\\/.*"
"pattern": "(^|.+/)Levels/.*_hold(/.*)?$"
},
"Exclude TempFiles": {
"pattern": ".*\\\\/\\\\$tmp[0-9]*_.*"
"pattern": "(^|.+/)\\\\$tmp[0-9]*_.*"
},
"RC i_caf": {
"glob": "*.i_caf",
@@ -74,10 +74,10 @@
"include": "test"
},
"Exclude HoldFiles": {
"pattern": ".*\\\\/Levels\\\\/.*_hold\\\\/.*"
"pattern": "(^|.+/)Levels/.*_hold(/.*)?$"
},
"Exclude TempFiles": {
"pattern": ".*\\\\/\\\\$tmp[0-9]*_.*"
"pattern": "(^|.+/)\\\\$tmp[0-9]*_.*"
},
"RC i_caf": {
"glob": "*.i_caf",
@@ -691,6 +691,12 @@ QProgressBar::chunk {
#gemRepoAddDialogInstructionTitleLabel {
font-size:14px;
font-weight:bold;
}
#gemRepoAddDialogWarningLabel {
font-size:12px;
font-style:italic;
}
#addGemRepoDialog #formFrame {
@@ -42,7 +42,9 @@ namespace O3DE::ProjectManager
m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel, m_downloadController);
vLayout->addWidget(m_headerWidget);
connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged);
connect(m_headerWidget, &GemCatalogHeaderWidget::OpenGemsRepo, this, &GemCatalogScreen::HandleOpenGemRepo);
connect(m_headerWidget, &GemCatalogHeaderWidget::AddGem, this, &GemCatalogScreen::OnAddGemClicked);
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->setMargin(0);
@@ -73,6 +75,7 @@ namespace O3DE::ProjectManager
m_notificationsView = AZStd::make_unique<AzToolsFramework::ToastNotificationsView>(this, AZ_CRC("GemCatalogNotificationsView"));
m_notificationsView->SetOffset(QPoint(10, 70));
m_notificationsView->SetMaxQueuedNotifications(1);
}
void GemCatalogScreen::ReinitForProject(const QString& projectPath)
@@ -94,48 +97,6 @@ namespace O3DE::ProjectManager
m_headerWidget->ReinitForProject();
connect(m_gemModel, &GemModel::dataChanged, m_filterWidget, &GemFilterWidget::ResetGemStatusFilter);
connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged);
connect(
m_headerWidget, &GemCatalogHeaderWidget::AddGem,
[&]()
{
EngineInfo engineInfo;
QString defaultPath;
AZ::Outcome<EngineInfo> engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
if (engineInfoResult.IsSuccess())
{
engineInfo = engineInfoResult.GetValue();
defaultPath = engineInfo.m_defaultGemsFolder;
}
if (defaultPath.isEmpty())
{
defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
}
QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Browse"), defaultPath));
if (!directory.isEmpty())
{
// register the gem to the o3de_manifest.json and to the project after the user confirms
// project creation/update
auto registerResult = PythonBindingsInterface::Get()->RegisterGem(directory);
if(!registerResult)
{
QMessageBox::critical(this, tr("Failed to add gem"), registerResult.GetError().c_str());
}
else
{
m_gemsToRegisterWithProject.insert(directory);
AZ::Outcome<GemInfo, void> gemInfoResult = PythonBindingsInterface::Get()->GetGemInfo(directory);
if (gemInfoResult)
{
m_gemModel->AddGem(gemInfoResult.GetValue<GemInfo>());
m_gemModel->UpdateGemDependencies();
}
}
}
});
// Select the first entry after everything got correctly sized
QTimer::singleShot(200, [=]{
@@ -144,6 +105,46 @@ namespace O3DE::ProjectManager
});
}
void GemCatalogScreen::OnAddGemClicked()
{
EngineInfo engineInfo;
QString defaultPath;
AZ::Outcome<EngineInfo> engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
if (engineInfoResult.IsSuccess())
{
engineInfo = engineInfoResult.GetValue();
defaultPath = engineInfo.m_defaultGemsFolder;
}
if (defaultPath.isEmpty())
{
defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
}
QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Browse"), defaultPath));
if (!directory.isEmpty())
{
// register the gem to the o3de_manifest.json and to the project after the user confirms
// project creation/update
auto registerResult = PythonBindingsInterface::Get()->RegisterGem(directory);
if(!registerResult)
{
QMessageBox::critical(this, tr("Failed to add gem"), registerResult.GetError().c_str());
}
else
{
m_gemsToRegisterWithProject.insert(directory);
AZ::Outcome<GemInfo, void> gemInfoResult = PythonBindingsInterface::Get()->GetGemInfo(directory);
if (gemInfoResult)
{
m_gemModel->AddGem(gemInfoResult.GetValue<GemInfo>());
m_gemModel->UpdateGemDependencies();
}
}
}
}
void GemCatalogScreen::OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies)
{
if (m_notificationsEnabled)
@@ -166,6 +167,10 @@ namespace O3DE::ProjectManager
{
notification += " " + tr("and") + " ";
}
if (added && GemModel::GetDownloadStatus(modelIndex) == GemInfo::DownloadStatus::NotDownloaded)
{
m_downloadController->AddGemDownload(GemModel::GetName(modelIndex));
}
}
if (numChangedDependencies == 1 )
@@ -174,7 +179,7 @@ namespace O3DE::ProjectManager
}
else if (numChangedDependencies > 1)
{
notification += QString("%d Gem ").arg(numChangedDependencies) + tr("dependencies");
notification += QString("%1 Gem ").arg(numChangedDependencies) + tr("dependencies");
}
notification += " " + (added ? tr("activated") : tr("deactivated"));
@@ -47,6 +47,7 @@ namespace O3DE::ProjectManager
public slots:
void OnGemStatusChanged(const QModelIndex& modelIndex, uint32_t numChangedDependencies);
void OnAddGemClicked();
protected:
void hideEvent(QHideEvent* event) override;
@@ -225,21 +225,22 @@ namespace O3DE::ProjectManager
QVector<QString> elementNames;
QVector<int> elementCounts;
const int totalGems = m_gemModel->rowCount();
const int selectedGemTotal = m_gemModel->TotalAddedGems();
const int selectedGemTotal = m_gemModel->GatherGemsToBeAdded(/*includeDependencies=*/true).size();
const int unselectedGemTotal = m_gemModel->GatherGemsToBeRemoved(/*includeDependencies=*/true).size();
const int enabledGemTotal = m_gemModel->TotalAddedGems(/*includeDependencies=*/true);
elementNames.push_back(GemSortFilterProxyModel::GetGemSelectedString(GemSortFilterProxyModel::GemSelected::Unselected));
elementCounts.push_back(totalGems - selectedGemTotal);
elementNames.push_back(GemSortFilterProxyModel::GetGemSelectedString(GemSortFilterProxyModel::GemSelected::Selected));
elementCounts.push_back(selectedGemTotal);
elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Inactive));
elementCounts.push_back(totalGems - enabledGemTotal);
elementNames.push_back(GemSortFilterProxyModel::GetGemSelectedString(GemSortFilterProxyModel::GemSelected::Unselected));
elementCounts.push_back(unselectedGemTotal);
elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Active));
elementCounts.push_back(enabledGemTotal);
elementNames.push_back(GemSortFilterProxyModel::GetGemActiveString(GemSortFilterProxyModel::GemActive::Inactive));
elementCounts.push_back(totalGems - enabledGemTotal);
bool wasCollapsed = false;
if (m_statusFilter)
{
@@ -262,44 +263,51 @@ namespace O3DE::ProjectManager
const QList<QAbstractButton*> buttons = m_statusFilter->GetButtonGroup()->buttons();
QAbstractButton* unselectedButton = buttons[0];
QAbstractButton* selectedButton = buttons[1];
unselectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Unselected);
selectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Selected);
QAbstractButton* selectedButton = buttons[0];
QAbstractButton* unselectedButton = buttons[1];
selectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Selected);
unselectedButton->setChecked(m_filterProxyModel->GetGemSelected() == GemSortFilterProxyModel::GemSelected::Unselected);
auto updateGemSelection = [=]([[maybe_unused]] bool checked)
{
if (unselectedButton->isChecked() && !selectedButton->isChecked())
{
m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Unselected);
}
else if (!unselectedButton->isChecked() && selectedButton->isChecked())
if (!unselectedButton->isChecked() && selectedButton->isChecked())
{
m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Selected);
}
else if (unselectedButton->isChecked() && !selectedButton->isChecked())
{
m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Unselected);
}
else
{
m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::NoFilter);
if (unselectedButton->isChecked() && selectedButton->isChecked())
{
m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Both);
}
else
{
m_filterProxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::NoFilter);
}
}
};
connect(unselectedButton, &QAbstractButton::toggled, this, updateGemSelection);
connect(selectedButton, &QAbstractButton::toggled, this, updateGemSelection);
QAbstractButton* inactiveButton = buttons[2];
QAbstractButton* activeButton = buttons[3];
inactiveButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Inactive);
activeButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Active);
QAbstractButton* activeButton = buttons[2];
QAbstractButton* inactiveButton = buttons[3];
activeButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Active);
inactiveButton->setChecked(m_filterProxyModel->GetGemActive() == GemSortFilterProxyModel::GemActive::Inactive);
auto updateGemActive = [=]([[maybe_unused]] bool checked)
{
if (inactiveButton->isChecked() && !activeButton->isChecked())
{
m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Inactive);
}
else if (!inactiveButton->isChecked() && activeButton->isChecked())
if (!inactiveButton->isChecked() && activeButton->isChecked())
{
m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Active);
}
else if (inactiveButton->isChecked() && !activeButton->isChecked())
{
m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Inactive);
}
else
{
m_filterProxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::NoFilter);
@@ -50,11 +50,26 @@ namespace O3DE::ProjectManager
}
}
// Gem selected
if (m_gemSelectedFilter != GemSelected::NoFilter)
// Gem selected
if (m_gemSelectedFilter == GemSelected::Selected)
{
const GemSelected sourceGemStatus = static_cast<GemSelected>(GemModel::IsAdded(sourceIndex));
if (m_gemSelectedFilter != sourceGemStatus)
if (!GemModel::NeedsToBeAdded(sourceIndex, true))
{
return false;
}
}
// Gem unselected
else if (m_gemSelectedFilter == GemSelected::Unselected)
{
if (!GemModel::NeedsToBeRemoved(sourceIndex, true))
{
return false;
}
}
// Gem selected or unselected
else if (m_gemSelectedFilter == GemSelected::Both)
{
if (!GemModel::NeedsToBeAdded(sourceIndex, true) && !GemModel::NeedsToBeRemoved(sourceIndex, true))
{
return false;
}
@@ -29,7 +29,8 @@ namespace O3DE::ProjectManager
{
NoFilter = -1,
Unselected,
Selected
Selected,
Both
};
enum class GemActive
{
@@ -27,6 +27,7 @@ namespace O3DE::ProjectManager
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->setContentsMargins(30, 30, 25, 10);
vLayout->setSpacing(0);
vLayout->setAlignment(Qt::AlignTop);
setLayout(vLayout);
QLabel* instructionTitleLabel = new QLabel(tr("Enter a valid path to add a new user repository"));
@@ -41,9 +42,18 @@ namespace O3DE::ProjectManager
vLayout->addWidget(instructionContextLabel);
m_repoPath = new FormFolderBrowseEditWidget(tr("Repository Path"), "", this);
m_repoPath->setFixedWidth(600);
m_repoPath->setFixedSize(QSize(600, 100));
vLayout->addWidget(m_repoPath);
vLayout->addSpacing(10);
QLabel* warningLabel = new QLabel(tr("Online repositories may contain files that could potentially harm your computer,"
" please ensure you understand the risks before downloading Gems from third-party sources."));
warningLabel->setObjectName("gemRepoAddDialogWarningLabel");
warningLabel->setWordWrap(true);
warningLabel->setAlignment(Qt::AlignLeft);
vLayout->addWidget(warningLabel);
vLayout->addSpacing(40);
QDialogButtonBox* dialogButtons = new QDialogButtonBox();
@@ -133,6 +133,11 @@ namespace O3DE::ProjectManager
return true;
}
else
{
// If we are already on this screen still notify we are on this screen to refresh it
newScreen->NotifyCurrentScreen();
}
}
return false;
@@ -187,6 +187,7 @@ namespace AZ
// Register utilities
AZ::SceneAPI::SceneCore::PatternMatcher::Reflect(context);
AZ::SceneAPI::Utilities::DebugSceneGraph::Reflect(context);
}
}
@@ -10,6 +10,7 @@
#include <AzCore/std/optional.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Serialization/Utils.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
@@ -20,6 +21,36 @@
namespace AZ::SceneAPI::Utilities
{
void DebugNode::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<DebugNode>()
->Field("Name", &DebugNode::m_name)
->Field("Path", &DebugNode::m_path)
->Field("Type", &DebugNode::m_type)
->Field("Data", &DebugNode::m_data);
}
}
void DebugSceneGraph::Reflect(AZ::ReflectContext* context)
{
DebugNode::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<DebugSceneGraph>()
->Field("Version", &DebugSceneGraph::m_version)
->Field("ProductName", &DebugSceneGraph::m_productName)
->Field("SceneName", &DebugSceneGraph::m_sceneName)
->Field("Nodes", &DebugSceneGraph::m_nodes);
}
}
void DebugOutput::Write(const char* name, const char* data)
{
m_output += AZStd::string::format("\t%s: %s\n", name, data);
@@ -38,21 +69,29 @@ namespace AZ::SceneAPI::Utilities
void DebugOutput::Write(const char* name, const AZStd::string& data)
{
Write(name, data.c_str());
AddToNode(name, data);
}
void DebugOutput::Write(const char* name, double data)
{
m_output += AZStd::string::format("\t%s: %f\n", name, data);
AddToNode(name, data);
}
void DebugOutput::Write(const char* name, uint64_t data)
{
m_output += AZStd::string::format("\t%s: %" PRIu64 "\n", name, data);
AddToNode(name, data);
}
void DebugOutput::Write(const char* name, int64_t data)
{
m_output += AZStd::string::format("\t%s: %" PRId64 "\n", name, data);
AddToNode(name, data);
}
void DebugOutput::Write(const char* name, const DataTypes::MatrixType& data)
@@ -63,6 +102,7 @@ namespace AZ::SceneAPI::Utilities
AZ::Vector3 translation{};
data.GetBasisAndTranslation(&basisX, &basisY, &basisZ, &translation);
m_pauseNodeData = true;
m_output += AZStd::string::format("\t%s:\n", name);
m_output += "\t";
@@ -76,16 +116,23 @@ namespace AZ::SceneAPI::Utilities
m_output += "\t";
Write("Transl", translation);
m_pauseNodeData = false;
AddToNode(name, data);
}
void DebugOutput::Write(const char* name, bool data)
{
m_output += AZStd::string::format("\t%s: %s\n", name, data ? "true" : "false");
AddToNode(name, data);
}
void DebugOutput::Write(const char* name, Vector3 data)
{
m_output += AZStd::string::format("\t%s: <% f, % f, % f>\n", name, data.GetX(), data.GetY(), data.GetZ());
AddToNode(name, data);
}
void DebugOutput::Write(const char* name, AZStd::optional<bool> data)
@@ -128,6 +175,11 @@ namespace AZ::SceneAPI::Utilities
return m_output;
}
DebugNode DebugOutput::GetDebugNode() const
{
return m_currentNode;
}
void WriteAndLog(AZ::IO::SystemFile& dbgFile, const char* strToWrite)
{
AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "%s", strToWrite);
@@ -137,7 +189,6 @@ namespace AZ::SceneAPI::Utilities
void DebugOutput::BuildDebugSceneGraph(const char* outputFolder, AZ::SceneAPI::Events::ExportProductList& productList, const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene, AZStd::string productName)
{
const int debugSceneGraphVersion = 1;
AZStd::string debugSceneFile;
AzFramework::StringFunc::Path::ConstructFull(outputFolder, productName.c_str(), debugSceneFile);
@@ -147,7 +198,7 @@ namespace AZ::SceneAPI::Utilities
if (dbgFile.Open(debugSceneFile.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
{
WriteAndLog(dbgFile, AZStd::string::format("ProductName: %s", productName.c_str()).c_str());
WriteAndLog(dbgFile, AZStd::string::format("debugSceneGraphVersion: %d", debugSceneGraphVersion).c_str());
WriteAndLog(dbgFile, AZStd::string::format("debugSceneGraphVersion: %d", SceneGraphVersion).c_str());
WriteAndLog(dbgFile, scene->GetName().c_str());
const AZ::SceneAPI::Containers::SceneGraph& sceneGraph = scene->GetGraph();
@@ -158,6 +209,11 @@ namespace AZ::SceneAPI::Utilities
AZ::SceneAPI::Containers::Views::BreadthFirst>(
sceneGraph, sceneGraph.GetRoot(), pairView.cbegin(), true);
DebugSceneGraph debugSceneGraph;
debugSceneGraph.m_version = SceneGraphVersion;
debugSceneGraph.m_productName = productName;
debugSceneGraph.m_sceneName = scene->GetName().c_str();
for (auto&& viewIt : view)
{
if (viewIt.second == nullptr)
@@ -170,20 +226,31 @@ namespace AZ::SceneAPI::Utilities
WriteAndLog(dbgFile, AZStd::string::format("Node Name: %s", viewIt.first.GetName()).c_str());
WriteAndLog(dbgFile, AZStd::string::format("Node Path: %s", viewIt.first.GetPath()).c_str());
WriteAndLog(dbgFile, AZStd::string::format("Node Type: %s", graphObject->RTTI_GetTypeName()).c_str());
AZ::SceneAPI::Utilities::DebugOutput debugOutput;
AZ::SceneAPI::Utilities::DebugOutput debugOutput(
DebugNode(viewIt.first.GetName(), viewIt.first.GetPath(), graphObject->RTTI_GetTypeName()));
viewIt.second->GetDebugOutput(debugOutput);
if (!debugOutput.GetOutput().empty())
{
WriteAndLog(dbgFile, debugOutput.GetOutput().c_str());
}
debugSceneGraph.m_nodes.push_back(debugOutput.GetDebugNode());
}
dbgFile.Close();
Utils::SaveObjectToFile((debugSceneFile + ".xml").c_str(), DataStream::StreamType::ST_XML, &debugSceneGraph);
static const AZ::Data::AssetType dbgSceneGraphAssetType("{07F289D1-4DC7-4C40-94B4-0A53BBCB9F0B}");
productList.AddProduct(productName, AZ::Uuid::CreateName(productName.c_str()), dbgSceneGraphAssetType,
AZStd::nullopt, AZStd::nullopt);
static const AZ::Data::AssetType dbgSceneGraphXmlAssetType("{51F37614-0D77-4F36-9AC6-7ED70A0AC868}");
productList.AddProduct(
(productName + ".xml"), AZ::Uuid::CreateName((productName + ".xml").c_str()), dbgSceneGraphXmlAssetType,
AZStd::nullopt, AZStd::nullopt);
}
}
}
@@ -15,6 +15,7 @@
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/optional.h>
#include <cinttypes>
#include <AzCore/std/any.h>
namespace AZ
{
@@ -34,15 +35,63 @@ namespace AZ
namespace AZ::SceneAPI::Utilities
{
constexpr int SceneGraphVersion = 1;
struct DebugNode
{
AZ_TYPE_INFO(DebugNode, "{490B9D4C-1847-46EB-BEBC-49812E104626}");
AZStd::string m_name;
AZStd::string m_path;
AZStd::string m_type;
DebugNode() = default;
DebugNode(AZStd::string name, AZStd::string path, AZStd::string type)
: m_name(AZStd::move(name)),
m_path(AZStd::move(path)),
m_type(AZStd::move(type))
{
}
static void Reflect(AZ::ReflectContext* context);
using DataItem = AZStd::pair<AZStd::string, AZStd::any>;
AZStd::vector<DataItem> m_data;
};
struct DebugSceneGraph
{
AZ_TYPE_INFO(DebugSceneGraph, "{375F6558-5709-409F-881E-8ED575D56C92}");
int m_version = SceneGraphVersion;
AZStd::string m_productName;
AZStd::string m_sceneName;
AZStd::vector<DebugNode> m_nodes;
static void Reflect(AZ::ReflectContext* context);
};
class DebugOutput
{
public:
DebugOutput(DebugNode node) : m_currentNode(AZStd::move(node)){}
template<typename T>
void Write(const char* name, const AZStd::vector<T>& data);
template<typename T>
void Write(const char* name, const AZStd::vector<AZStd::vector<T>>& data);
template<typename T>
void AddToNode(const char* name, const T& data)
{
if (!m_pauseNodeData)
{
m_currentNode.m_data.emplace_back(name, AZStd::make_any<AZStd::decay_t<T>>(data));
}
}
SCENE_CORE_API void Write(const char* name, const char* data);
SCENE_CORE_API void WriteArray(const char* name, const unsigned int* data, int size);
SCENE_CORE_API void Write(const char* name, const AZStd::string& data);
@@ -57,11 +106,15 @@ namespace AZ::SceneAPI::Utilities
SCENE_CORE_API void Write(const char* name, AZStd::optional<AZ::Vector3> data);
SCENE_CORE_API const AZStd::string& GetOutput() const;
SCENE_CORE_API DebugNode GetDebugNode() const;
SCENE_CORE_API static void BuildDebugSceneGraph(const char* outputFolder, AZ::SceneAPI::Events::ExportProductList& productList, const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene, AZStd::string productName);
protected:
AZStd::string m_output;
DebugSceneGraph m_graph;
DebugNode m_currentNode;
bool m_pauseNodeData = false; // If true, don't append any data to the DebugNode. Useful when a Write function calls other Write functions
};
}
@@ -13,7 +13,11 @@ namespace AZ::SceneAPI::Utilities
template <typename T>
void DebugOutput::Write(const char* name, const AZStd::vector<T>& data)
{
m_output += AZStd::string::format("\t%s: Count %zu. Hash: %zu\n", name, data.size(), AZStd::hash_range(data.begin(), data.end()));
size_t hash = AZStd::hash_range(data.begin(), data.end());
m_output += AZStd::string::format("\t%s: Count %zu. Hash: %zu\n", name, data.size(), hash);
AddToNode(AZStd::string::format("%s - Count", name).c_str(), data.size());
AddToNode(AZStd::string::format("%s - Hash", name).c_str(), hash);
}
template <typename T>
@@ -27,5 +31,9 @@ namespace AZ::SceneAPI::Utilities
}
m_output += AZStd::string::format("\t%s: Count %zu. Hash: %zu\n", name, data.size(), hash);
AddToNode(AZStd::string::format("%s - Count", name).c_str(), data.size());
AddToNode(AZStd::string::format("%s - Hash", name).c_str(), hash);
}
}