Merge remote-tracking branch 'upstream/development' into Atom/santorac/RemixableMaterialTypes4_Layering
Signed-off-by: santorac <55155825+santorac@users.noreply.github.com>
This commit is contained in:
@@ -61,6 +61,7 @@ class TestAutomation(TestAutomationBase):
|
||||
from . import Graph_HappyPath_ZoomInZoomOut as test_module
|
||||
self._run_test(request, workspace, editor, test_module)
|
||||
|
||||
@pytest.mark.xfail(reason="Test fails on nightly build builds, it needs to be fixed.")
|
||||
def test_NodePalette_HappyPath_CanSelectNode(self, request, workspace, editor, launcher_platform):
|
||||
from . import NodePalette_HappyPath_CanSelectNode as test_module
|
||||
self._run_test(request, workspace, editor, test_module)
|
||||
@@ -113,6 +114,7 @@ class TestAutomation(TestAutomationBase):
|
||||
from . import Debugger_HappyPath_TargetMultipleGraphs as test_module
|
||||
self._run_test(request, workspace, editor, test_module)
|
||||
|
||||
@pytest.mark.xfail(reason="Test fails on nightly build builds, it needs to be fixed.")
|
||||
@pytest.mark.parametrize("level", ["tmp_level"])
|
||||
def test_Debugger_HappyPath_TargetMultipleEntities(self, request, workspace, editor, launcher_platform, project, level):
|
||||
def teardown():
|
||||
@@ -174,6 +176,7 @@ class TestAutomation(TestAutomationBase):
|
||||
from . import ScriptEvents_ReturnSetType_Successfully as test_module
|
||||
self._run_test(request, workspace, editor, test_module)
|
||||
|
||||
@pytest.mark.xfail(reason="Test fails on nightly build builds, it needs to be fixed.")
|
||||
def test_NodeCategory_ExpandOnClick(self, request, workspace, editor, launcher_platform):
|
||||
from . import NodeCategory_ExpandOnClick as test_module
|
||||
self._run_test(request, workspace, editor, test_module)
|
||||
@@ -187,6 +190,7 @@ class TestAutomation(TestAutomationBase):
|
||||
from . import VariableManager_UnpinVariableType_Works as test_module
|
||||
self._run_test(request, workspace, editor, test_module)
|
||||
|
||||
@pytest.mark.xfail(reason="Test fails on nightly build builds, it needs to be fixed.")
|
||||
def test_Node_HappyPath_DuplicateNode(self, request, workspace, editor, launcher_platform):
|
||||
from . import Node_HappyPath_DuplicateNode as test_module
|
||||
self._run_test(request, workspace, editor, test_module)
|
||||
@@ -263,6 +267,7 @@ class TestScriptCanvasTests(object):
|
||||
timeout=60,
|
||||
)
|
||||
|
||||
@pytest.mark.xfail(reason="Test fails on nightly build builds, it needs to be fixed.")
|
||||
def test_VariableManager_Default_CreateDeleteVars(self, request, editor, launcher_platform):
|
||||
var_types = ["Boolean", "Color", "EntityID", "Number", "String", "Transform", "Vector2", "Vector3", "Vector4"]
|
||||
expected_lines = [f"Success: {var_type} variable is created" for var_type in var_types]
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace AzFramework::SurfaceData
|
||||
{
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<AZStd::fixed_vector<SurfaceTagWeight, Constants::MaxSurfaceWeights>>();
|
||||
serializeContext->Class<SurfacePoint>()
|
||||
->Field("m_position", &SurfacePoint::m_position)
|
||||
->Field("m_normal", &SurfacePoint::m_normal)
|
||||
@@ -46,6 +47,7 @@ namespace AzFramework::SurfaceData
|
||||
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<AZStd::fixed_vector<SurfaceTagWeight, Constants::MaxSurfaceWeights>>();
|
||||
behaviorContext->Class<SurfacePoint>("AzFramework::SurfaceData::SurfacePoint")
|
||||
->Attribute(AZ::Script::Attributes::Category, "SurfaceData")
|
||||
->Constructor()
|
||||
|
||||
@@ -10,13 +10,19 @@
|
||||
#include <AzCore/Math/Crc.h>
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/containers/fixed_vector.h>
|
||||
|
||||
namespace AzFramework::SurfaceData
|
||||
{
|
||||
namespace Constants
|
||||
{
|
||||
static constexpr const char* s_unassignedTagName = "(unassigned)";
|
||||
|
||||
//! The maximum number of surface weights that we can store.
|
||||
//! For performance reasons, we want to limit this so that we can preallocate the max size in advance.
|
||||
//! The current number is chosen to be higher than expected needs, but small enough to avoid being excessively wasteful.
|
||||
//! (Dynamic structures would end up taking more memory than what we're preallocating)
|
||||
static constexpr size_t MaxSurfaceWeights = 16;
|
||||
}
|
||||
|
||||
struct SurfaceTagWeight
|
||||
@@ -70,7 +76,7 @@ namespace AzFramework::SurfaceData
|
||||
}
|
||||
};
|
||||
|
||||
using SurfaceTagWeightList = AZStd::vector<SurfaceTagWeight>;
|
||||
using SurfaceTagWeightList = AZStd::fixed_vector<SurfaceTagWeight, Constants::MaxSurfaceWeights>;
|
||||
|
||||
struct SurfacePoint final
|
||||
{
|
||||
|
||||
+9
-1
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h>
|
||||
#include <AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx>
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
|
||||
@@ -117,9 +118,16 @@ namespace AzToolsFramework
|
||||
{
|
||||
}
|
||||
|
||||
bool EditorEntityUiHandlerBase::OnEntityDoubleClick([[maybe_unused]] AZ::EntityId entityId) const
|
||||
bool EditorEntityUiHandlerBase::OnOutlinerItemDoubleClick([[maybe_unused]] const QModelIndex& index) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::EntityId EditorEntityUiHandlerBase::GetEntityIdFromIndex(const QModelIndex& index)
|
||||
{
|
||||
QModelIndex firstColumnIndex = index.siblingAtColumn(EntityOutlinerListModel::ColumnName);
|
||||
|
||||
return AZ::EntityId(firstColumnIndex.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+12
-8
@@ -21,7 +21,6 @@ class QTreeView;
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! Defines a handler that can customize entity UI appearance and behavior in the Entity Outliner.
|
||||
//! This class is meant to be abstract, entities do not have a handler by default.
|
||||
class EditorEntityUiHandlerBase
|
||||
{
|
||||
protected:
|
||||
@@ -33,7 +32,7 @@ namespace AzToolsFramework
|
||||
public:
|
||||
EditorEntityUiHandlerId GetHandlerId();
|
||||
|
||||
// # Entity Outliner
|
||||
// # Entity Outliner Item
|
||||
|
||||
//! Returns the item info string that is appended to the item name in the Outliner.
|
||||
virtual QString GenerateItemInfoString(AZ::EntityId entityId) const;
|
||||
@@ -41,10 +40,12 @@ namespace AzToolsFramework
|
||||
virtual QString GenerateItemTooltip(AZ::EntityId entityId) const;
|
||||
//! Returns the item icon pixmap to display in the Outliner.
|
||||
virtual QIcon GenerateItemIcon(AZ::EntityId entityId) const;
|
||||
//! Returns whether the element's lock and visibility state should be accessible in the Outliner
|
||||
virtual bool CanToggleLockVisibility(AZ::EntityId entityId) const;
|
||||
//! Returns whether the element's name should be editable
|
||||
virtual bool CanRename(AZ::EntityId entityId) const;
|
||||
//! Returns whether the element's lock and visibility state should be accessible in the Outliner
|
||||
virtual bool CanToggleLockVisibility(AZ::EntityId entityId) const;
|
||||
|
||||
// Qt-specific painting functions
|
||||
|
||||
//! Paints the background of the item in the Outliner.
|
||||
virtual void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
||||
@@ -54,24 +55,27 @@ namespace AzToolsFramework
|
||||
//! Paints the background of the descendant branches of the item in the Outliner.
|
||||
virtual void PaintDescendantBranchBackground(QPainter* painter, const QTreeView* view, const QRect& rect,
|
||||
const QModelIndex& index, const QModelIndex& descendantIndex) const;
|
||||
|
||||
//! Paints visual elements on the foreground of the item in the Outliner.
|
||||
virtual void PaintItemForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
||||
//! Paints visual elements on the foreground of the descendants of the item in the Outliner.
|
||||
virtual void PaintDescendantForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
|
||||
const QModelIndex& descendantIndex) const;
|
||||
|
||||
// Outliner-specific interactions
|
||||
|
||||
//! Triggered when the entity is clicked in the Outliner.
|
||||
//! @return True if the click has been handled and should not be propagated, false otherwise.
|
||||
virtual bool OnOutlinerItemClick(const QPoint& position, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
||||
//! Triggered when the entity is double-clicked in the Outliner.
|
||||
//! @return True if the double-click has been handled and should not be propagated, false otherwise.
|
||||
virtual bool OnOutlinerItemDoubleClick(const QModelIndex& index) const;
|
||||
//! Triggered when an entity's children are expanded in the Outliner.
|
||||
virtual void OnOutlinerItemExpand(const QModelIndex& index) const;
|
||||
//! Triggered when an entity's children are collapsed in the Outliner.
|
||||
virtual void OnOutlinerItemCollapse(const QModelIndex& index) const;
|
||||
|
||||
//! Triggered when the entity is double clicked in the Outliner or in the Viewport.
|
||||
//! @return True if the double click has been handled and should not be propagated, false otherwise.
|
||||
virtual bool OnEntityDoubleClick(AZ::EntityId entityId) const;
|
||||
protected:
|
||||
static AZ::EntityId GetEntityIdFromIndex(const QModelIndex& index);
|
||||
|
||||
private:
|
||||
EditorEntityUiHandlerId m_handlerId = 0;
|
||||
|
||||
+1
-1
@@ -945,7 +945,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
if (AZ::EntityId entityId = GetEntityIdFromIndex(index); auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId))
|
||||
{
|
||||
entityUiHandler->OnEntityDoubleClick(entityId);
|
||||
entityUiHandler->OnOutlinerItemDoubleClick(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
QIcon LevelRootUiHandler::GenerateItemIcon(AZ::EntityId /*entityId*/) const
|
||||
QIcon LevelRootUiHandler::GenerateItemIcon([[maybe_unused]] AZ::EntityId entityId) const
|
||||
{
|
||||
return QIcon(m_levelRootIconPath);
|
||||
}
|
||||
@@ -62,17 +62,18 @@ namespace AzToolsFramework
|
||||
return infoString;
|
||||
}
|
||||
|
||||
bool LevelRootUiHandler::CanToggleLockVisibility(AZ::EntityId /*entityId*/) const
|
||||
bool LevelRootUiHandler::CanToggleLockVisibility([[maybe_unused]] AZ::EntityId entityId) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LevelRootUiHandler::CanRename(AZ::EntityId /*entityId*/) const
|
||||
bool LevelRootUiHandler::CanRename([[maybe_unused]] AZ::EntityId entityId) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void LevelRootUiHandler::PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& /*index*/) const
|
||||
void LevelRootUiHandler::PaintItemBackground(
|
||||
QPainter* painter, const QStyleOptionViewItem& option, [[maybe_unused]] const QModelIndex& index) const
|
||||
{
|
||||
if (!painter)
|
||||
{
|
||||
@@ -94,8 +95,10 @@ namespace AzToolsFramework
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
bool LevelRootUiHandler::OnEntityDoubleClick(AZ::EntityId entityId) const
|
||||
bool LevelRootUiHandler::OnOutlinerItemDoubleClick(const QModelIndex& index) const
|
||||
{
|
||||
AZ::EntityId entityId = GetEntityIdFromIndex(index);
|
||||
|
||||
if (auto prefabFocusPublicInterface = AZ::Interface<Prefab::PrefabFocusPublicInterface>::Get();
|
||||
!prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace AzToolsFramework
|
||||
bool CanToggleLockVisibility(AZ::EntityId entityId) const override;
|
||||
bool CanRename(AZ::EntityId entityId) const override;
|
||||
void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
bool OnEntityDoubleClick(AZ::EntityId entityId) const override;
|
||||
bool OnOutlinerItemDoubleClick(const QModelIndex& index) const override;
|
||||
|
||||
private:
|
||||
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
|
||||
AZ::EntityId entityId = GetEntityIdFromIndex(index);
|
||||
const bool isFirstColumn = index.column() == EntityOutlinerListModel::ColumnName;
|
||||
const bool isLastColumn = index.column() == EntityOutlinerListModel::ColumnLockToggle;
|
||||
QModelIndex firstColumnIndex = index.siblingAtColumn(EntityOutlinerListModel::ColumnName);
|
||||
@@ -183,7 +183,7 @@ namespace AzToolsFramework
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
|
||||
AZ::EntityId entityId = GetEntityIdFromIndex(index);
|
||||
|
||||
const QTreeView* outlinerTreeView(qobject_cast<const QTreeView*>(option.widget));
|
||||
const int ancestorLeft = outlinerTreeView->visualRect(index).left() + (m_prefabBorderThickness / 2) - 1;
|
||||
@@ -283,7 +283,7 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabUiHandler::PaintItemForeground(QPainter* painter, const QStyleOptionViewItem& option, [[maybe_unused]] const QModelIndex& index) const
|
||||
{
|
||||
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
|
||||
AZ::EntityId entityId = GetEntityIdFromIndex(index);
|
||||
const QPoint offset = QPoint(-18, 3);
|
||||
QModelIndex firstColumnIndex = index.siblingAtColumn(EntityOutlinerListModel::ColumnName);
|
||||
const int iconSize = 16;
|
||||
@@ -385,7 +385,7 @@ namespace AzToolsFramework
|
||||
|
||||
bool PrefabUiHandler::OnOutlinerItemClick(const QPoint& position, const QStyleOptionViewItem& option, const QModelIndex& index) const
|
||||
{
|
||||
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
|
||||
AZ::EntityId entityId = GetEntityIdFromIndex(index);
|
||||
const QPoint offset = QPoint(-18, 3);
|
||||
|
||||
if (m_prefabFocusPublicInterface->IsOwningPrefabInFocusHierarchy(entityId))
|
||||
@@ -411,7 +411,7 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabUiHandler::OnOutlinerItemCollapse(const QModelIndex& index) const
|
||||
{
|
||||
AZ::EntityId entityId(index.data(EntityOutlinerListModel::EntityIdRole).value<AZ::u64>());
|
||||
AZ::EntityId entityId = GetEntityIdFromIndex(index);
|
||||
|
||||
if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
{
|
||||
@@ -420,8 +420,10 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool PrefabUiHandler::OnEntityDoubleClick(AZ::EntityId entityId) const
|
||||
bool PrefabUiHandler::OnOutlinerItemDoubleClick(const QModelIndex& index) const
|
||||
{
|
||||
AZ::EntityId entityId = GetEntityIdFromIndex(index);
|
||||
|
||||
if (!m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
|
||||
{
|
||||
// Focus on this prefab
|
||||
|
||||
@@ -43,8 +43,8 @@ namespace AzToolsFramework
|
||||
const QModelIndex& index,
|
||||
const QModelIndex& descendantIndex) const override;
|
||||
bool OnOutlinerItemClick(const QPoint& position, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
bool OnOutlinerItemDoubleClick(const QModelIndex& index) const override;
|
||||
void OnOutlinerItemCollapse(const QModelIndex& index) const override;
|
||||
bool OnEntityDoubleClick(AZ::EntityId entityId) const override;
|
||||
|
||||
protected:
|
||||
Prefab::PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
|
||||
|
||||
+40
-2
@@ -102,7 +102,7 @@ namespace AzToolsFramework
|
||||
m_editButton->setAutoRaise(true);
|
||||
m_editButton->setIcon(QIcon(":/stylesheet/img/UI20/open-in-internal-app.svg"));
|
||||
m_editButton->setToolTip("Edit asset");
|
||||
m_editButton->setVisible(false);
|
||||
SetEditButtonVisible(false);
|
||||
|
||||
connect(m_editButton, &QToolButton::clicked, this, &PropertyAssetCtrl::OnEditButtonClicked);
|
||||
|
||||
@@ -961,12 +961,16 @@ namespace AzToolsFramework
|
||||
AzFramework::StringFunc::Path::GetFileName(assetPath.c_str(), m_defaultAssetHint);
|
||||
}
|
||||
m_browseEdit->setPlaceholderText((m_defaultAssetHint + m_DefaultSuffix).c_str());
|
||||
|
||||
UpdateEditButton();
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::UpdateAssetDisplay()
|
||||
{
|
||||
UpdateThumbnail();
|
||||
|
||||
UpdateEditButton();
|
||||
|
||||
if (m_currentAssetType == AZ::Data::s_invalidAssetType)
|
||||
{
|
||||
return;
|
||||
@@ -1109,7 +1113,9 @@ namespace AzToolsFramework
|
||||
|
||||
void PropertyAssetCtrl::SetEditButtonVisible(bool visible)
|
||||
{
|
||||
m_editButton->setVisible(visible);
|
||||
m_showEditButton = visible;
|
||||
m_editButton->setVisible(m_showEditButton);
|
||||
UpdateEditButton();
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::SetEditButtonIcon(const QIcon& icon)
|
||||
@@ -1205,6 +1211,15 @@ namespace AzToolsFramework
|
||||
m_thumbnail->ClearThumbnail();
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::UpdateEditButton()
|
||||
{
|
||||
// if Edit button is in use (shown), enable/disable it depending on the current asset id.
|
||||
if (m_showEditButton && m_disableEditButtonWhenNoAssetSelected)
|
||||
{
|
||||
m_editButton->setEnabled(GetCurrentAssetID().IsValid());
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::SetClearButtonEnabled(bool enable)
|
||||
{
|
||||
m_browseEdit->setClearButtonEnabled(enable);
|
||||
@@ -1236,6 +1251,17 @@ namespace AzToolsFramework
|
||||
return m_hideProductFilesInAssetPicker;
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::SetDisableEditButtonWhenNoAssetSelected(bool disableEditButtonWhenNoAssetSelected)
|
||||
{
|
||||
m_disableEditButtonWhenNoAssetSelected = disableEditButtonWhenNoAssetSelected;
|
||||
UpdateEditButton();
|
||||
}
|
||||
|
||||
bool PropertyAssetCtrl::GetDisableEditButtonWhenNoAssetSelected() const
|
||||
{
|
||||
return m_disableEditButtonWhenNoAssetSelected;
|
||||
}
|
||||
|
||||
void PropertyAssetCtrl::SetShowThumbnail(bool enable)
|
||||
{
|
||||
m_showThumbnail = enable;
|
||||
@@ -1349,6 +1375,12 @@ namespace AzToolsFramework
|
||||
GUI->SetEditButtonTooltip(tr(buttonTooltip.c_str()));
|
||||
}
|
||||
}
|
||||
else if (attrib == AZ_CRC_CE("DisableEditButtonWhenNoAssetSelected"))
|
||||
{
|
||||
bool disableEditButtonWhenNoAssetSelected = false;
|
||||
attrValue->Read<bool>(disableEditButtonWhenNoAssetSelected);
|
||||
GUI->SetDisableEditButtonWhenNoAssetSelected(disableEditButtonWhenNoAssetSelected);
|
||||
}
|
||||
else if (attrib == AZ::Edit::Attributes::DefaultAsset)
|
||||
{
|
||||
AZ::Data::AssetId assetId;
|
||||
@@ -1597,6 +1629,12 @@ namespace AzToolsFramework
|
||||
GUI->SetEditButtonTooltip(tr(buttonTooltip.c_str()));
|
||||
}
|
||||
}
|
||||
else if (attrib == AZ_CRC_CE("DisableEditButtonWhenNoAssetSelected"))
|
||||
{
|
||||
bool disableEditButtonWhenNoAssetSelected = false;
|
||||
attrValue->Read<bool>(disableEditButtonWhenNoAssetSelected);
|
||||
GUI->SetDisableEditButtonWhenNoAssetSelected(disableEditButtonWhenNoAssetSelected);
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleAssetPropertyHandlerDefault::WriteGUIValuesIntoProperty(size_t index, PropertyAssetCtrl* GUI, property_t& instance, InstanceDataNode* node)
|
||||
|
||||
+8
@@ -159,6 +159,10 @@ namespace AzToolsFramework
|
||||
//! By default the asset picker shows both on an AZ::Asset<> property. You can hide product assets with this flag.
|
||||
bool m_hideProductFilesInAssetPicker = false;
|
||||
|
||||
//! True to disable the edit button when there is no asset currently selected.
|
||||
bool m_disableEditButtonWhenNoAssetSelected = false;
|
||||
|
||||
bool m_showEditButton = false;
|
||||
bool m_showThumbnail = false;
|
||||
bool m_showThumbnailDropDownButton = false;
|
||||
EditCallbackType* m_thumbnailCallback = nullptr;
|
||||
@@ -220,6 +224,9 @@ namespace AzToolsFramework
|
||||
void SetHideProductFilesInAssetPicker(bool hide);
|
||||
bool GetHideProductFilesInAssetPicker() const;
|
||||
|
||||
void SetDisableEditButtonWhenNoAssetSelected(bool disableEditButtonWhenNoAssetSelected);
|
||||
bool GetDisableEditButtonWhenNoAssetSelected() const;
|
||||
|
||||
// Enable and configure a thumbnail widget that displays an asset preview and dropdown arrow for a dropdown menu
|
||||
void SetShowThumbnail(bool enable);
|
||||
bool GetShowThumbnail() const;
|
||||
@@ -250,6 +257,7 @@ namespace AzToolsFramework
|
||||
|
||||
private:
|
||||
void UpdateThumbnail();
|
||||
void UpdateEditButton();
|
||||
};
|
||||
|
||||
class AssetPropertyHandlerDefault
|
||||
|
||||
+10
@@ -154,6 +154,16 @@ namespace AzToolsFramework
|
||||
(void)debugName;
|
||||
}
|
||||
|
||||
// provides an option to specify reading parent element attributes.
|
||||
// This allows parent elements to override attributes of their children if needed.
|
||||
virtual void ConsumeParentAttribute(WidgetType* widget, AZ::u32 attrib, PropertyAttributeReader* attrValue, const char* debugName)
|
||||
{
|
||||
(void)widget;
|
||||
(void)attrib;
|
||||
(void)attrValue;
|
||||
(void)debugName;
|
||||
}
|
||||
|
||||
// override GetFirstInTabOrder, GetLastInTabOrder in your base class to define which widget gets focus first when pressing tab,
|
||||
// and also what widget is last.
|
||||
// for example, if your widget is a compound widget and contains, say, 5 buttons
|
||||
|
||||
+35
-10
@@ -40,7 +40,14 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
void* classInstance = parent->FirstInstance(); // pointer to the owner class so we can read member variables and functions
|
||||
auto consumeAttributes = [&](const auto& attributes, const char* name)
|
||||
|
||||
void* parentClassInstance = nullptr;
|
||||
if (InstanceDataNode* parentInstanceDataNode = parent->GetParent())
|
||||
{
|
||||
parentClassInstance = parentInstanceDataNode->FirstInstance();
|
||||
}
|
||||
|
||||
auto consumeAttributes = [this, classInstance, wid](const auto& attributes, const char* name)
|
||||
{
|
||||
for (size_t i = 0; i < attributes.size(); ++i)
|
||||
{
|
||||
@@ -50,25 +57,43 @@ namespace AzToolsFramework
|
||||
}
|
||||
};
|
||||
|
||||
auto consumeParentAttributes = [this, parentClassInstance, wid](const auto& attributes, const char* name)
|
||||
{
|
||||
if (parentClassInstance)
|
||||
{
|
||||
for (size_t i = 0; i < attributes.size(); ++i)
|
||||
{
|
||||
const auto& attrPair = attributes[i];
|
||||
PropertyAttributeReader reader(parentClassInstance, &*attrPair.second);
|
||||
ConsumeParentAttribute(wid, attrPair.first, &reader, name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const AZ::SerializeContext::ClassElement* element = dataNode->GetElementMetadata();
|
||||
if (element)
|
||||
{
|
||||
consumeAttributes(element->m_attributes, element->m_name);
|
||||
const AZ::Edit::ElementData* elementEdit = dataNode->GetElementEditMetadata();
|
||||
if (elementEdit)
|
||||
|
||||
if (const AZ::Edit::ElementData* elementEdit = dataNode->GetElementEditMetadata();
|
||||
elementEdit != nullptr)
|
||||
{
|
||||
consumeAttributes(elementEdit->m_attributes, elementEdit->m_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (dataNode->GetClassMetadata())
|
||||
{
|
||||
const AZ::Edit::ClassData* classEditData = dataNode->GetClassMetadata()->m_editData;
|
||||
if (classEditData)
|
||||
const AZ::SerializeContext::ClassElement* parentElement = parent != dataNode ?
|
||||
dataNode->GetElementMetadata() :
|
||||
nullptr;
|
||||
|
||||
if (parentElement != nullptr)
|
||||
{
|
||||
for (auto it = classEditData->m_elements.begin(); it != classEditData->m_elements.end(); ++it)
|
||||
// Reuse the current instance element name for the debug name
|
||||
consumeParentAttributes(parentElement->m_attributes, element->m_name);
|
||||
|
||||
if (const AZ::Edit::ElementData* elementEdit = parent->GetElementEditMetadata();
|
||||
elementEdit != nullptr)
|
||||
{
|
||||
consumeAttributes(it->m_attributes, it->m_name);
|
||||
consumeParentAttributes(elementEdit->m_attributes, elementEdit->m_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -66,6 +66,12 @@ namespace AzToolsFramework
|
||||
class GenericEnumPropertyComboBoxHandler
|
||||
: public GenericComboBoxHandler<ValueType>
|
||||
{
|
||||
virtual void ConsumeParentAttribute(GenericComboBoxCtrlBase* GUI, AZ::u32 attrib, PropertyAttributeReader* attrValue, const char* debugName) override
|
||||
{
|
||||
// Simply re-route to ConsumeAttribute since no special logic is needed.
|
||||
ConsumeAttribute(GUI, attrib, attrValue, debugName);
|
||||
}
|
||||
|
||||
virtual void ConsumeAttribute(GenericComboBoxCtrlBase* GUI, AZ::u32 attrib, PropertyAttributeReader* attrValue, const char* debugName) override
|
||||
{
|
||||
(void)debugName;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace UnitTest
|
||||
// When no containers are in the way, the function will just return the entityId of the entity that was clicked.
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
|
||||
|
||||
// Verify the correct entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
@@ -29,7 +29,7 @@ namespace UnitTest
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]); // Containers are closed by default
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
|
||||
|
||||
// Verify the correct entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
@@ -47,7 +47,7 @@ namespace UnitTest
|
||||
m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
|
||||
|
||||
// Verify the correct entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
@@ -65,7 +65,7 @@ namespace UnitTest
|
||||
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
|
||||
|
||||
// Verify the correct entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
@@ -85,7 +85,7 @@ namespace UnitTest
|
||||
m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], true);
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
|
||||
|
||||
// Verify the correct entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <Tests/FocusMode/EditorFocusModeFixture.h>
|
||||
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
|
||||
#include <Tests/BoundsTestComponent.h>
|
||||
@@ -93,10 +94,13 @@ namespace UnitTest
|
||||
entity->CreateComponent<UnitTest::BoundsTestComponent>();
|
||||
entity->Activate();
|
||||
|
||||
// Move the CarEntity so it's out of the way.
|
||||
AZ::TransformBus::Event(m_entityMap[CarEntityName], &AZ::TransformBus::Events::SetWorldTranslation, WorldCarEntityPosition);
|
||||
// Move the City so that it is in view
|
||||
AZ::TransformBus::Event(m_entityMap[CityEntityName], &AZ::TransformBus::Events::SetWorldTranslation, s_worldCityEntityPosition);
|
||||
|
||||
// Setup the camera so the Car entity is in view.
|
||||
// Move the CarEntity so that it's not overlapping with the rest
|
||||
AZ::TransformBus::Event(m_entityMap[CarEntityName], &AZ::TransformBus::Events::SetWorldTranslation, s_worldCarEntityPosition);
|
||||
|
||||
// Setup the camera so the entities is in view.
|
||||
AzFramework::SetCameraTransform(
|
||||
m_cameraState,
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
@@ -113,4 +117,5 @@ namespace UnitTest
|
||||
|
||||
return entity->GetId();
|
||||
}
|
||||
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
||||
|
||||
@@ -38,9 +37,6 @@ namespace UnitTest
|
||||
AzToolsFramework::EntityIdList GetSelectedEntities();
|
||||
|
||||
AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
AzFramework::CameraState m_cameraState;
|
||||
|
||||
inline static const AZ::Vector3 CameraPosition = AZ::Vector3(10.0f, 15.0f, 10.0f);
|
||||
|
||||
inline static const char* CityEntityName = "City";
|
||||
inline static const char* StreetEntityName = "Street";
|
||||
@@ -49,7 +45,11 @@ namespace UnitTest
|
||||
inline static const char* Passenger1EntityName = "Passenger1";
|
||||
inline static const char* Passenger2EntityName = "Passenger2";
|
||||
|
||||
inline static AZ::Vector3 WorldCarEntityPosition = AZ::Vector3(5.0f, 15.0f, 0.0f);
|
||||
AzFramework::CameraState m_cameraState;
|
||||
|
||||
inline static const AZ::Vector3 CameraPosition = AZ::Vector3(10.0f, 15.0f, 10.0f);
|
||||
inline static AZ::Vector3 s_worldCityEntityPosition = AZ::Vector3(5.0f, 10.0f, 0.0f);
|
||||
inline static AZ::Vector3 s_worldCarEntityPosition = AZ::Vector3(5.0f, 15.0f, 0.0f);
|
||||
};
|
||||
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -45,5 +45,20 @@ namespace UnitTest
|
||||
// Click the entity in the viewport
|
||||
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(carScreenPosition)->MouseLButtonDown()->MouseLButtonUp();
|
||||
}
|
||||
|
||||
void BoxSelectOnViewport()
|
||||
{
|
||||
// Calculate the position in screen space of where to begin and end the box select action
|
||||
const auto beginningPositionWorldBoxSelect = AzFramework::WorldToScreen(AZ::Vector3(-10.0f, 15.0f, 5.0f), m_cameraState);
|
||||
const auto endingPositionWorldBoxSelect = AzFramework::WorldToScreen(AZ::Vector3(10.0f, 15.0f, -5.0f), m_cameraState);
|
||||
|
||||
// Perform a box select in the viewport
|
||||
m_actionDispatcher->SetStickySelect(true)
|
||||
->CameraState(m_cameraState)
|
||||
->MousePosition(beginningPositionWorldBoxSelect)
|
||||
->MouseLButtonDown()
|
||||
->MousePosition(endingPositionWorldBoxSelect)
|
||||
->MouseLButtonUp();
|
||||
}
|
||||
};
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace UnitTest
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionSelectEntityWithFocusOnLevel)
|
||||
{
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
|
||||
|
||||
// Verify entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
@@ -27,7 +27,7 @@ namespace UnitTest
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
|
||||
|
||||
// Verify entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
@@ -41,7 +41,7 @@ namespace UnitTest
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
|
||||
|
||||
// Verify entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
@@ -55,7 +55,7 @@ namespace UnitTest
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[SportsCarEntityName]);
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
|
||||
|
||||
// Verify entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
@@ -68,10 +68,71 @@ namespace UnitTest
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger1EntityName]);
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
|
||||
ClickAtWorldPositionOnViewport(s_worldCarEntityPosition);
|
||||
|
||||
// Verify entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionBoxSelectWithFocusOnLevel)
|
||||
{
|
||||
// Do a box select that includes all entities in the fixture
|
||||
BoxSelectOnViewport();
|
||||
|
||||
// Entities are selected
|
||||
using ::testing::UnorderedElementsAre;
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_THAT(selectedEntitiesAfter,
|
||||
UnorderedElementsAre(
|
||||
m_entityMap[CityEntityName],
|
||||
m_entityMap[StreetEntityName],
|
||||
m_entityMap[CarEntityName],
|
||||
m_entityMap[Passenger1EntityName],
|
||||
m_entityMap[SportsCarEntityName],
|
||||
m_entityMap[Passenger2EntityName]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionBoxSelectWithFocusOnChild)
|
||||
{
|
||||
// Set the focus on the Passenger1 Entity (child of the entity)
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
|
||||
|
||||
// Do a box select that includes all entities in the fixture
|
||||
BoxSelectOnViewport();
|
||||
|
||||
// Entities are selected
|
||||
using ::testing::UnorderedElementsAre;
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_THAT(selectedEntitiesAfter,
|
||||
UnorderedElementsAre(
|
||||
m_entityMap[StreetEntityName],
|
||||
m_entityMap[CarEntityName],
|
||||
m_entityMap[Passenger1EntityName],
|
||||
m_entityMap[SportsCarEntityName],
|
||||
m_entityMap[Passenger2EntityName]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionBoxSelectWithFocusOnLeaf)
|
||||
{
|
||||
// Set the focus on the Passenger1 Entity (child of the entity)
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger1EntityName]);
|
||||
|
||||
// Do a box select that includes all entities in the fixture
|
||||
BoxSelectOnViewport();
|
||||
|
||||
// Entities are selected
|
||||
using ::testing::UnorderedElementsAre;
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_THAT(selectedEntitiesAfter,
|
||||
UnorderedElementsAre(
|
||||
m_entityMap[Passenger1EntityName]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace AZ
|
||||
// Register Shader Asset Builder
|
||||
AssetBuilderSDK::AssetBuilderDesc shaderAssetBuilderDescriptor;
|
||||
shaderAssetBuilderDescriptor.m_name = "Shader Asset Builder";
|
||||
shaderAssetBuilderDescriptor.m_version = 110; // Add "Definitions" field to shader asset to support convenient addition of preprocessor definitions
|
||||
shaderAssetBuilderDescriptor.m_version = 111; // Enable shader PDB generation globally if Atom/GraphicsDevMode settings registry key is set
|
||||
shaderAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern( AZStd::string::format("*.%s", RPI::ShaderSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
||||
shaderAssetBuilderDescriptor.m_busId = azrtti_typeid<ShaderAssetBuilder>();
|
||||
shaderAssetBuilderDescriptor.m_createJobFunction = AZStd::bind(&ShaderAssetBuilder::CreateJobs, &m_shaderAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
|
||||
@@ -318,10 +318,8 @@ namespace AZ
|
||||
RPI::RenderPipelineDescriptor renderPipelineDescriptor = *RPI::GetDataFromAnyAsset<RPI::RenderPipelineDescriptor>(pipelineAsset);
|
||||
renderPipelineDescriptor.m_name = AZStd::string::format("%s_%i", renderPipelineDescriptor.m_name.c_str(), viewportContext->GetId());
|
||||
|
||||
// Make sure non-msaa super variant is used for non-msaa pipeline
|
||||
bool isNonMsaaPipeline = (renderPipelineDescriptor.m_renderSettings.m_multisampleState.m_samples == 1);
|
||||
const char* supervariantName = isNonMsaaPipeline ? AZ::RPI::NoMsaaSupervariantName : "";
|
||||
AZ::RPI::ShaderSystemInterface::Get()->SetSupervariantName(AZ::Name(supervariantName));
|
||||
// The default pipeline determines the initial MSAA state for the application
|
||||
AZ::RPI::RPISystemInterface::Get()->SetApplicationMultisampleState(renderPipelineDescriptor.m_renderSettings.m_multisampleState);
|
||||
|
||||
if (!scene->GetRenderPipeline(AZ::Name(renderPipelineDescriptor.m_name)))
|
||||
{
|
||||
|
||||
@@ -34,11 +34,11 @@
|
||||
"Attachment": "Output"
|
||||
}
|
||||
},
|
||||
"MultisampleSource": {
|
||||
"Pass": "Pipeline"
|
||||
},
|
||||
"ImageDescriptor": {
|
||||
"Format": "D32_FLOAT_S8X24_UINT",
|
||||
"MultisampleState": {
|
||||
"samples": 4
|
||||
},
|
||||
"SharedQueueMask": "Graphics"
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -29,6 +29,24 @@
|
||||
"RootShaderVariantAssetFileName": "diffuseprobegridrender_null_0.azshadervariant"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "NoMSAA",
|
||||
"RootShaderVariantAssets":
|
||||
[
|
||||
{
|
||||
"APIName": "dx12",
|
||||
"RootShaderVariantAssetFileName": "diffuseprobegridrender-nomsaa_dx12_0.azshadervariant"
|
||||
},
|
||||
{
|
||||
"APIName": "vulkan",
|
||||
"RootShaderVariantAssetFileName": "diffuseprobegridrender-nomsaa_vulkan_0.azshadervariant"
|
||||
},
|
||||
{
|
||||
"APIName": "null",
|
||||
"RootShaderVariantAssetFileName": "diffuseprobegridrender-nomsaa_null_0.azshadervariant"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+16
-13
@@ -92,21 +92,24 @@ namespace AZ
|
||||
AZ_Error("DiffuseProbeGridFeatureProcessor", m_probeGridRenderData.m_srgLayout != nullptr, "Failed to find ObjectSrg layout");
|
||||
}
|
||||
|
||||
// initialize the buffer pools for the DiffuseProbeGrid visualization
|
||||
m_visualizationBufferPools = RHI::RayTracingBufferPools::CreateRHIRayTracingBufferPools();
|
||||
m_visualizationBufferPools->Init(device);
|
||||
|
||||
// load probe visualization model, the BLAS will be created in OnAssetReady()
|
||||
m_visualizationModelAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>(
|
||||
"Models/DiffuseProbeSphere.azmodel",
|
||||
AZ::RPI::AssetUtils::TraceLevel::Assert);
|
||||
|
||||
if (!m_visualizationModelAsset.IsReady())
|
||||
if (device->GetFeatures().m_rayTracing)
|
||||
{
|
||||
m_visualizationModelAsset.QueueLoad();
|
||||
}
|
||||
// initialize the buffer pools for the DiffuseProbeGrid visualization
|
||||
m_visualizationBufferPools = RHI::RayTracingBufferPools::CreateRHIRayTracingBufferPools();
|
||||
m_visualizationBufferPools->Init(device);
|
||||
|
||||
Data::AssetBus::MultiHandler::BusConnect(m_visualizationModelAsset.GetId());
|
||||
// load probe visualization model, the BLAS will be created in OnAssetReady()
|
||||
m_visualizationModelAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>(
|
||||
"Models/DiffuseProbeSphere.azmodel",
|
||||
AZ::RPI::AssetUtils::TraceLevel::Assert);
|
||||
|
||||
if (!m_visualizationModelAsset.IsReady())
|
||||
{
|
||||
m_visualizationModelAsset.QueueLoad();
|
||||
}
|
||||
|
||||
Data::AssetBus::MultiHandler::BusConnect(m_visualizationModelAsset.GetId());
|
||||
}
|
||||
|
||||
EnableSceneNotification();
|
||||
}
|
||||
|
||||
@@ -115,10 +115,6 @@ namespace AZ
|
||||
// all faces of the cubemap have been rendered, invoke the callback
|
||||
m_callback(m_environmentCubeMapPass->GetTextureData(), m_environmentCubeMapPass->GetTextureFormat());
|
||||
|
||||
// remove the pipeline
|
||||
m_scene->RemoveRenderPipeline(m_environmentCubeMapPipelineId);
|
||||
m_environmentCubeMapPass = nullptr;
|
||||
|
||||
// restore exposures
|
||||
sceneSrg->SetConstant(m_globalIblExposureConstantIndex, m_previousGlobalIblExposure);
|
||||
sceneSrg->SetConstant(m_skyBoxExposureConstantIndex, m_previousSkyBoxExposure);
|
||||
@@ -223,6 +219,16 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void ReflectionProbe::OnRenderEnd()
|
||||
{
|
||||
if (m_environmentCubeMapPass && m_environmentCubeMapPass->IsFinished())
|
||||
{
|
||||
// remove the cubemap pipeline
|
||||
// Note: this must be done here (not in Simulate) to avoid a race condition with other feature processors
|
||||
m_scene->RemoveRenderPipeline(m_environmentCubeMapPipelineId);
|
||||
m_environmentCubeMapPass = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ReflectionProbe::SetTransform(const AZ::Transform& transform)
|
||||
{
|
||||
@@ -282,7 +288,7 @@ namespace AZ
|
||||
|
||||
AZ::RPI::RenderPipelineDescriptor environmentCubeMapPipelineDesc;
|
||||
environmentCubeMapPipelineDesc.m_mainViewTagName = "MainCamera";
|
||||
environmentCubeMapPipelineDesc.m_renderSettings.m_multisampleState.m_samples = 4;
|
||||
environmentCubeMapPipelineDesc.m_renderSettings.m_multisampleState = RPI::RPISystemInterface::Get()->GetApplicationMultisampleState();
|
||||
environmentCubeMapPipelineDesc.m_renderSettings.m_size.m_width = RPI::EnvironmentCubeMapPass::CubeMapFaceSize;
|
||||
environmentCubeMapPipelineDesc.m_renderSettings.m_size.m_height = RPI::EnvironmentCubeMapPass::CubeMapFaceSize;
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ namespace AZ
|
||||
|
||||
void Init(RPI::Scene* scene, ReflectionRenderData* reflectionRenderData);
|
||||
void Simulate(uint32_t probeIndex);
|
||||
void OnRenderEnd();
|
||||
|
||||
const Vector3& GetPosition() const { return m_transform.GetTranslation(); }
|
||||
const AZ::Transform& GetTransform() const { return m_transform; }
|
||||
|
||||
+12
@@ -166,6 +166,18 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void ReflectionProbeFeatureProcessor::OnRenderEnd()
|
||||
{
|
||||
// call OnRenderEnd on all reflection probes
|
||||
for (uint32_t probeIndex = 0; probeIndex < m_reflectionProbes.size(); ++probeIndex)
|
||||
{
|
||||
AZStd::shared_ptr<ReflectionProbe>& reflectionProbe = m_reflectionProbes[probeIndex];
|
||||
AZ_Assert(reflectionProbe.use_count() > 1, "ReflectionProbe found with no corresponding owner, ensure that RemoveProbe() is called before releasing probe handles");
|
||||
|
||||
reflectionProbe->OnRenderEnd();
|
||||
}
|
||||
}
|
||||
|
||||
ReflectionProbeHandle ReflectionProbeFeatureProcessor::AddProbe(const AZ::Transform& transform, bool useParallaxCorrection)
|
||||
{
|
||||
AZStd::shared_ptr<ReflectionProbe> reflectionProbe = AZStd::make_shared<ReflectionProbe>();
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace AZ
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
void Simulate(const FeatureProcessor::SimulatePacket& packet) override;
|
||||
void OnRenderEnd() override;
|
||||
|
||||
// find the reflection probe volumes that contain the position
|
||||
using ReflectionProbeVector = AZStd::vector<AZStd::shared_ptr<ReflectionProbe>>;
|
||||
|
||||
@@ -42,6 +42,9 @@ namespace AZ
|
||||
|
||||
//! Returns if the current bakcend is a null renderer
|
||||
bool IsNullRenderer();
|
||||
|
||||
//! Returns true if the Atom/GraphicsDevMode settings registry key is set
|
||||
bool IsGraphicsDevModeEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,12 @@
|
||||
#include <Atom/RHI/RHIUtils.h>
|
||||
#include <Atom/RHI/RHISystemInterface.h>
|
||||
#include <Atom/RHI/Factory.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/CommandLine/CommandLine.h>
|
||||
|
||||
static constexpr char GraphicsDevModeSetting[] = "/Atom/GraphicsDevMode";
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RHI
|
||||
@@ -134,5 +137,16 @@ namespace AZ
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsGraphicsDevModeEnabled()
|
||||
{
|
||||
AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
bool graphicsDevMode = false;
|
||||
if (settingsRegistry)
|
||||
{
|
||||
settingsRegistry->Get(graphicsDevMode, GraphicsDevModeSetting);
|
||||
}
|
||||
return graphicsDevMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
#include <Atom/RHI.Edit/Utils.h>
|
||||
#include <Atom/RHI.Reflect/DX12/PipelineLayoutDescriptor.h>
|
||||
#include <Atom/RHI.Reflect/DX12/ShaderStageFunction.h>
|
||||
#include <Atom/RHI/RHIUtils.h>
|
||||
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
namespace AZ
|
||||
@@ -253,9 +253,11 @@ namespace AZ
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool graphicsDevMode = RHI::IsGraphicsDevModeEnabled();
|
||||
|
||||
// Compilation parameters
|
||||
AZStd::string params = shaderCompilerArguments.MakeAdditionalDxcCommandLineString();
|
||||
if (BuildHasDebugInfo(shaderCompilerArguments))
|
||||
if (graphicsDevMode || BuildHasDebugInfo(shaderCompilerArguments))
|
||||
{
|
||||
params += " -Zi"; // Generate debug information
|
||||
params += " -Zss"; // Compute Shader Hash considering source information
|
||||
@@ -284,7 +286,7 @@ namespace AZ
|
||||
// If we use the auto-name (hash), there is no way we can retrieve that name apart from listing the directory.
|
||||
// Instead, let's just generate that hash ourselves.
|
||||
AZStd::string symbolDatabaseFileCliArgument{" "}; // when not debug: still insert a space between 5.dxil and 7.hlsl-in
|
||||
if (BuildHasDebugInfo(shaderCompilerArguments))
|
||||
if (graphicsDevMode || BuildHasDebugInfo(shaderCompilerArguments))
|
||||
{
|
||||
// prepare .pdb filename:
|
||||
AZStd::string md5hex = RHI::ByteToHexString(md5);
|
||||
@@ -353,7 +355,7 @@ namespace AZ
|
||||
byProducts.m_dynamicBranchCount = ByProducts::UnknownDynamicBranchCount;
|
||||
}
|
||||
|
||||
if (BuildHasDebugInfo(shaderCompilerArguments))
|
||||
if (graphicsDevMode || BuildHasDebugInfo(shaderCompilerArguments))
|
||||
{
|
||||
byProducts.m_intermediatePaths.emplace(AZStd::move(objectCodeOutputFile));
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <Atom/RHI.Reflect/Vulkan/Base.h>
|
||||
#include <Atom/RHI.Reflect/Vulkan/ShaderStageFunction.h>
|
||||
#include <Atom/RHI/RHIUtils.h>
|
||||
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
@@ -281,7 +282,9 @@ namespace AZ
|
||||
args.m_destinationFolder = tempFolder.c_str();
|
||||
|
||||
const auto dxcInputFile = RHI::PrependFile(args); // Prepend header
|
||||
if (BuildHasDebugInfo(shaderCompilerArguments))
|
||||
const bool graphicsDevMode = RHI::IsGraphicsDevModeEnabled();
|
||||
|
||||
if (graphicsDevMode || BuildHasDebugInfo(shaderCompilerArguments))
|
||||
{
|
||||
// dump intermediate "true final HLSL" file (shadername.vulkan.shadersource.prepend)
|
||||
byProducts.m_intermediatePaths.insert(dxcInputFile);
|
||||
@@ -334,7 +337,7 @@ namespace AZ
|
||||
byProducts.m_dynamicBranchCount = ByProducts::UnknownDynamicBranchCount;
|
||||
}
|
||||
|
||||
if (BuildHasDebugInfo(shaderCompilerArguments))
|
||||
if (graphicsDevMode || BuildHasDebugInfo(shaderCompilerArguments))
|
||||
{
|
||||
byProducts.m_intermediatePaths.emplace(AZStd::move(objectCodeOutputFile));
|
||||
}
|
||||
|
||||
@@ -86,6 +86,8 @@ namespace AZ
|
||||
const RPISystemDescriptor& GetDescriptor() const override;
|
||||
Name GetRenderApiName() const override;
|
||||
uint64_t GetCurrentTick() const override;
|
||||
void SetApplicationMultisampleState(const RHI::MultisampleState& multisampleState) override;
|
||||
const RHI::MultisampleState& GetApplicationMultisampleState() const override;
|
||||
|
||||
// AZ::Debug::TraceMessageBus::Handler overrides...
|
||||
bool OnPreAssert(const char* fileName, int line, const char* func, const char* message) override;
|
||||
@@ -136,6 +138,9 @@ namespace AZ
|
||||
bool m_systemAssetsInitialized = false;
|
||||
|
||||
uint64_t m_renderTick = 0;
|
||||
|
||||
// Application multisample state
|
||||
RHI::MultisampleState m_multisampleState;
|
||||
};
|
||||
|
||||
} // namespace RPI
|
||||
|
||||
@@ -90,6 +90,10 @@ namespace AZ
|
||||
|
||||
//! Get the index of current render tick
|
||||
virtual uint64_t GetCurrentTick() const = 0;
|
||||
|
||||
//! Application multisample state
|
||||
virtual void SetApplicationMultisampleState(const RHI::MultisampleState& multisampleState) = 0;
|
||||
virtual const RHI::MultisampleState& GetApplicationMultisampleState() const = 0;
|
||||
};
|
||||
|
||||
} // namespace RPI
|
||||
|
||||
@@ -60,6 +60,11 @@ namespace AZ
|
||||
//! Same as above. Provided as a convenience when all arguments of the 'numthreads' attributes should be assigned to RHI::DispatchDirect::m_threadsPerGroup* variables.
|
||||
AZ::Outcome<void, AZStd::string> GetComputeShaderNumThreads(const Data::Asset<ShaderAsset>& shaderAsset, RHI::DispatchDirect& dispatchDirect);
|
||||
|
||||
//! Get single image pixel value from raw image data
|
||||
//! This assumes the imageData is not empty
|
||||
template<typename T>
|
||||
T GetImageDataPixelValue(AZStd::span<const uint8_t> imageData, const AZ::RHI::ImageDescriptor& imageDescriptor, uint32_t x, uint32_t y, uint32_t componentIndex = 0);
|
||||
|
||||
//! Get single image pixel value for specified mip and slice
|
||||
template<typename T>
|
||||
T GetSubImagePixelValue(const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset, uint32_t x, uint32_t y, uint32_t componentIndex = 0, uint32_t mip = 0, uint32_t slice = 0);
|
||||
|
||||
@@ -434,5 +434,29 @@ namespace AZ
|
||||
return m_renderTick;
|
||||
}
|
||||
|
||||
void RPISystem::SetApplicationMultisampleState(const RHI::MultisampleState& multisampleState)
|
||||
{
|
||||
m_multisampleState = multisampleState;
|
||||
|
||||
bool isNonMsaaPipeline = (m_multisampleState.m_samples == 1);
|
||||
const char* supervariantName = isNonMsaaPipeline ? AZ::RPI::NoMsaaSupervariantName : "";
|
||||
AZ::RPI::ShaderSystemInterface::Get()->SetSupervariantName(AZ::Name(supervariantName));
|
||||
|
||||
// reinitialize pipelines for all scenes
|
||||
for (auto& scene : m_scenes)
|
||||
{
|
||||
for (auto& renderPipeline : scene->GetRenderPipelines())
|
||||
{
|
||||
renderPipeline->GetRenderSettings().m_multisampleState = multisampleState;
|
||||
renderPipeline->SetPassNeedsRecreate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const RHI::MultisampleState& RPISystem::GetApplicationMultisampleState() const
|
||||
{
|
||||
return m_multisampleState;
|
||||
}
|
||||
|
||||
} //namespace RPI
|
||||
} //namespace AZ
|
||||
|
||||
@@ -232,16 +232,12 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T GetSubImagePixelValueInternal(const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset, uint32_t x, uint32_t y, uint32_t componentIndex, uint32_t mip, uint32_t slice)
|
||||
size_t GetImageDataIndex(const AZ::RHI::ImageDescriptor& imageDescriptor, uint32_t x, uint32_t y, uint32_t componentIndex)
|
||||
{
|
||||
AZStd::array values{ aznumeric_cast<T>(0) };
|
||||
auto width = imageDescriptor.m_size.m_width;
|
||||
const uint32_t numComponents = AZ::RHI::GetFormatComponentCount(imageDescriptor.m_format);
|
||||
|
||||
auto topLeft = AZStd::make_pair(x, y);
|
||||
auto bottomRight = AZStd::make_pair(x + 1, y + 1);
|
||||
GetSubImagePixelValues(imageAsset, topLeft, bottomRight, AZStd::span<T>(values), componentIndex, mip, slice);
|
||||
|
||||
return values[0];
|
||||
return (y * width + x) * numComponents + componentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,22 +443,60 @@ namespace AZ
|
||||
return GetComputeShaderNumThreads(shaderAsset, &dispatchDirect.m_threadsPerGroupX, &dispatchDirect.m_threadsPerGroupY, &dispatchDirect.m_threadsPerGroupZ);
|
||||
}
|
||||
|
||||
template<>
|
||||
float GetImageDataPixelValue<float>(AZStd::span<const uint8_t> imageData, const AZ::RHI::ImageDescriptor& imageDescriptor, uint32_t x, uint32_t y, uint32_t componentIndex)
|
||||
{
|
||||
size_t imageDataIndex = Internal::GetImageDataIndex(imageDescriptor, x, y, componentIndex);
|
||||
return Internal::RetrieveFloatValue(imageData.data(), imageDataIndex, imageDescriptor.m_format);
|
||||
}
|
||||
|
||||
template<>
|
||||
AZ::u32 GetImageDataPixelValue<AZ::u32>(AZStd::span<const uint8_t> imageData, const AZ::RHI::ImageDescriptor& imageDescriptor, uint32_t x, uint32_t y, uint32_t componentIndex)
|
||||
{
|
||||
size_t imageDataIndex = Internal::GetImageDataIndex(imageDescriptor, x, y, componentIndex);
|
||||
return Internal::RetrieveUintValue(imageData.data(), imageDataIndex, imageDescriptor.m_format);
|
||||
}
|
||||
|
||||
template<>
|
||||
AZ::s32 GetImageDataPixelValue<AZ::s32>(AZStd::span<const uint8_t> imageData, const AZ::RHI::ImageDescriptor& imageDescriptor, uint32_t x, uint32_t y, uint32_t componentIndex)
|
||||
{
|
||||
size_t imageDataIndex = Internal::GetImageDataIndex(imageDescriptor, x, y, componentIndex);
|
||||
return Internal::RetrieveIntValue(imageData.data(), imageDataIndex, imageDescriptor.m_format);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T GetSubImagePixelValueInternal(const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset, uint32_t x, uint32_t y, uint32_t componentIndex, uint32_t mip, uint32_t slice)
|
||||
{
|
||||
if (!imageAsset.IsReady())
|
||||
{
|
||||
return aznumeric_cast<T>(0);
|
||||
}
|
||||
|
||||
auto imageData = imageAsset->GetSubImageData(mip, slice);
|
||||
if (imageData.empty())
|
||||
{
|
||||
return aznumeric_cast<T>(0);
|
||||
}
|
||||
|
||||
return GetImageDataPixelValue<T>(imageData, imageAsset->GetImageDescriptor(), x, y, componentIndex);
|
||||
}
|
||||
|
||||
template<>
|
||||
float GetSubImagePixelValue<float>(const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset, uint32_t x, uint32_t y, uint32_t componentIndex, uint32_t mip, uint32_t slice)
|
||||
{
|
||||
return Internal::GetSubImagePixelValueInternal<float>(imageAsset, x, y, componentIndex, mip, slice);
|
||||
return GetSubImagePixelValueInternal<float>(imageAsset, x, y, componentIndex, mip, slice);
|
||||
}
|
||||
|
||||
template<>
|
||||
AZ::u32 GetSubImagePixelValue<AZ::u32>(const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset, uint32_t x, uint32_t y, uint32_t componentIndex, uint32_t mip, uint32_t slice)
|
||||
{
|
||||
return Internal::GetSubImagePixelValueInternal<AZ::u32>(imageAsset, x, y, componentIndex, mip, slice);
|
||||
return GetSubImagePixelValueInternal<AZ::u32>(imageAsset, x, y, componentIndex, mip, slice);
|
||||
}
|
||||
|
||||
template<>
|
||||
AZ::s32 GetSubImagePixelValue<AZ::s32>(const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset, uint32_t x, uint32_t y, uint32_t componentIndex, uint32_t mip, uint32_t slice)
|
||||
{
|
||||
return Internal::GetSubImagePixelValueInternal<AZ::s32>(imageAsset, x, y, componentIndex, mip, slice);
|
||||
return GetSubImagePixelValueInternal<AZ::s32>(imageAsset, x, y, componentIndex, mip, slice);
|
||||
}
|
||||
|
||||
bool GetSubImagePixelValues(const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset, AZStd::pair<uint32_t, uint32_t> topLeft, AZStd::pair<uint32_t, uint32_t> bottomRight, AZStd::span<float> outValues, uint32_t componentIndex, uint32_t mip, uint32_t slice)
|
||||
@@ -478,16 +512,14 @@ namespace AZ
|
||||
return false;
|
||||
}
|
||||
|
||||
const AZ::RHI::ImageDescriptor imageDescriptor = imageAsset->GetImageDescriptor();
|
||||
auto width = imageDescriptor.m_size.m_width;
|
||||
const uint32_t numComponents = AZ::RHI::GetFormatComponentCount(imageDescriptor.m_format);
|
||||
const AZ::RHI::ImageDescriptor& imageDescriptor = imageAsset->GetImageDescriptor();
|
||||
|
||||
size_t outValuesIndex = 0;
|
||||
for (uint32_t y = topLeft.second; y < bottomRight.second; ++y)
|
||||
{
|
||||
for (uint32_t x = topLeft.first; x < bottomRight.first; ++x)
|
||||
{
|
||||
size_t imageDataIndex = (y * width + x) * numComponents + componentIndex;
|
||||
size_t imageDataIndex = Internal::GetImageDataIndex(imageDescriptor, x, y, componentIndex);
|
||||
|
||||
auto& outValue = outValues[outValuesIndex++];
|
||||
outValue = Internal::RetrieveFloatValue(imageData.data(), imageDataIndex, imageDescriptor.m_format);
|
||||
@@ -510,16 +542,14 @@ namespace AZ
|
||||
return false;
|
||||
}
|
||||
|
||||
const AZ::RHI::ImageDescriptor imageDescriptor = imageAsset->GetImageDescriptor();
|
||||
auto width = imageDescriptor.m_size.m_width;
|
||||
const uint32_t numComponents = AZ::RHI::GetFormatComponentCount(imageDescriptor.m_format);
|
||||
const AZ::RHI::ImageDescriptor& imageDescriptor = imageAsset->GetImageDescriptor();
|
||||
|
||||
size_t outValuesIndex = 0;
|
||||
for (uint32_t y = topLeft.second; y < bottomRight.second; ++y)
|
||||
{
|
||||
for (uint32_t x = topLeft.first; x < bottomRight.first; ++x)
|
||||
{
|
||||
size_t imageDataIndex = (y * width + x) * numComponents + componentIndex;
|
||||
size_t imageDataIndex = Internal::GetImageDataIndex(imageDescriptor, x, y, componentIndex);
|
||||
|
||||
auto& outValue = outValues[outValuesIndex++];
|
||||
outValue = Internal::RetrieveUintValue(imageData.data(), imageDataIndex, imageDescriptor.m_format);
|
||||
@@ -542,16 +572,14 @@ namespace AZ
|
||||
return false;
|
||||
}
|
||||
|
||||
const AZ::RHI::ImageDescriptor imageDescriptor = imageAsset->GetImageDescriptor();
|
||||
auto width = imageDescriptor.m_size.m_width;
|
||||
const uint32_t numComponents = AZ::RHI::GetFormatComponentCount(imageDescriptor.m_format);
|
||||
const AZ::RHI::ImageDescriptor& imageDescriptor = imageAsset->GetImageDescriptor();
|
||||
|
||||
size_t outValuesIndex = 0;
|
||||
for (uint32_t y = topLeft.second; y < bottomRight.second; ++y)
|
||||
{
|
||||
for (uint32_t x = topLeft.first; x < bottomRight.first; ++x)
|
||||
{
|
||||
size_t imageDataIndex = (y * width + x) * numComponents + componentIndex;
|
||||
size_t imageDataIndex = Internal::GetImageDataIndex(imageDescriptor, x, y, componentIndex);
|
||||
|
||||
auto& outValue = outValues[outValuesIndex++];
|
||||
outValue = Internal::RetrieveIntValue(imageData.data(), imageDataIndex, imageDescriptor.m_format);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <AzCore/RTTI/ReflectContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -35,6 +36,15 @@ namespace AZ
|
||||
->Field("MaterialSlots", &ModelAsset::m_materialSlots)
|
||||
->Field("LodAssets", &ModelAsset::m_lodAssets)
|
||||
;
|
||||
|
||||
// Note: This class needs to have edit context reflection so PropertyAssetCtrl::OnEditButtonClicked
|
||||
// can open the asset with the preferred asset editor (Scene Settings).
|
||||
if (auto* editContext = serializeContext->GetEditContext())
|
||||
{
|
||||
editContext->Class<ModelAsset>("Model Asset", "")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,10 +63,8 @@ namespace AtomToolsFramework
|
||||
pipelineDesc.m_mainViewTagName = "MainCamera";
|
||||
pipelineDesc.m_name = pipelineName;
|
||||
pipelineDesc.m_rootPassTemplate = "ToolsPipelineRenderToTexture";
|
||||
pipelineDesc.m_renderSettings.m_multisampleState = AZ::RPI::RPISystemInterface::Get()->GetApplicationMultisampleState();
|
||||
|
||||
// We have to set the samples to 4 to match the pipeline passes' setting, otherwise it may lead to device lost issue
|
||||
// [GFX TODO] [ATOM-13551] Default value sand validation required to prevent pipeline crash and device lost
|
||||
pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 4;
|
||||
m_renderPipeline = AZ::RPI::RenderPipeline::CreateRenderPipeline(pipelineDesc);
|
||||
m_scene->AddRenderPipeline(m_renderPipeline);
|
||||
m_scene->Activate();
|
||||
|
||||
@@ -72,6 +72,9 @@ namespace AZ
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &MeshComponentConfig::m_modelAsset, "Mesh Asset", "Mesh asset reference")
|
||||
->Attribute(AZ_CRC_CE("EditButton"), "")
|
||||
->Attribute(AZ_CRC_CE("EditDescription"), "Open in Scene Settings")
|
||||
->Attribute(AZ_CRC_CE("DisableEditButtonWhenNoAssetSelected"), true)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &MeshComponentConfig::m_sortKey, "Sort Key", "Transparent meshes are drawn by sort key then depth. Used this to force certain transparent meshes to draw before or after others.")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, &MeshComponentConfig::IsAssetSet)
|
||||
->DataElement(AZ::Edit::UIHandlers::CheckBox, &MeshComponentConfig::m_excludeFromReflectionCubeMaps, "Exclude from reflection cubemaps", "Mesh will not be visible in baked reflection probe cubemaps")
|
||||
|
||||
@@ -98,6 +98,8 @@ namespace GradientSignal
|
||||
|
||||
void SetupDependencies();
|
||||
|
||||
void GetSubImageData();
|
||||
|
||||
// ImageGradientRequestBus overrides...
|
||||
AZStd::string GetImageAssetPath() const override;
|
||||
void SetImageAssetPath(const AZStd::string& assetPath) override;
|
||||
@@ -113,5 +115,6 @@ namespace GradientSignal
|
||||
LmbrCentral::DependencyMonitor m_dependencyMonitor;
|
||||
mutable AZStd::shared_mutex m_imageMutex;
|
||||
GradientTransform m_gradientTransform;
|
||||
AZStd::span<const uint8_t> m_imageData;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,6 +62,6 @@ namespace GradientSignal
|
||||
}
|
||||
};
|
||||
|
||||
float GetValueFromImageAsset(const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset, const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue);
|
||||
float GetValueFromImageAsset(AZStd::span<const uint8_t> imageData, const AZ::RHI::ImageDescriptor& imageDescriptor, const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue);
|
||||
|
||||
} // namespace GradientSignal
|
||||
|
||||
@@ -215,6 +215,16 @@ namespace GradientSignal
|
||||
m_dependencyMonitor.ConnectDependency(m_configuration.m_imageAsset.GetId());
|
||||
}
|
||||
|
||||
void ImageGradientComponent::GetSubImageData()
|
||||
{
|
||||
if (!m_configuration.m_imageAsset || !m_configuration.m_imageAsset.IsReady())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_imageData = m_configuration.m_imageAsset->GetSubImageData(0, 0);
|
||||
}
|
||||
|
||||
void ImageGradientComponent::Activate()
|
||||
{
|
||||
// This will immediately call OnGradientTransformChanged and initialize m_gradientTransform.
|
||||
@@ -224,10 +234,13 @@ namespace GradientSignal
|
||||
|
||||
ImageGradientRequestBus::Handler::BusConnect(GetEntityId());
|
||||
GradientRequestBus::Handler::BusConnect(GetEntityId());
|
||||
AZ::Data::AssetBus::Handler::BusConnect(m_configuration.m_imageAsset.GetId());
|
||||
|
||||
AZStd::unique_lock<decltype(m_imageMutex)> imageLock(m_imageMutex);
|
||||
// Invoke the QueueLoad before connecting to the AssetBus, so that
|
||||
// if the asset is already ready, then OnAssetReady will be triggered immediately
|
||||
m_imageData = AZStd::span<const uint8_t>();
|
||||
m_configuration.m_imageAsset.QueueLoad();
|
||||
|
||||
AZ::Data::AssetBus::Handler::BusConnect(m_configuration.m_imageAsset.GetId());
|
||||
}
|
||||
|
||||
void ImageGradientComponent::Deactivate()
|
||||
@@ -267,18 +280,24 @@ namespace GradientSignal
|
||||
{
|
||||
AZStd::unique_lock<decltype(m_imageMutex)> imageLock(m_imageMutex);
|
||||
m_configuration.m_imageAsset = asset;
|
||||
|
||||
GetSubImageData();
|
||||
}
|
||||
|
||||
void ImageGradientComponent::OnAssetMoved(AZ::Data::Asset<AZ::Data::AssetData> asset, [[maybe_unused]] void* oldDataPointer)
|
||||
{
|
||||
AZStd::unique_lock<decltype(m_imageMutex)> imageLock(m_imageMutex);
|
||||
m_configuration.m_imageAsset = asset;
|
||||
|
||||
GetSubImageData();
|
||||
}
|
||||
|
||||
void ImageGradientComponent::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
|
||||
{
|
||||
AZStd::unique_lock<decltype(m_imageMutex)> imageLock(m_imageMutex);
|
||||
m_configuration.m_imageAsset = asset;
|
||||
|
||||
GetSubImageData();
|
||||
}
|
||||
|
||||
void ImageGradientComponent::OnGradientTransformChanged(const GradientTransform& newTransform)
|
||||
@@ -292,6 +311,12 @@ namespace GradientSignal
|
||||
AZ::Vector3 uvw = sampleParams.m_position;
|
||||
bool wasPointRejected = false;
|
||||
|
||||
// Return immediately if our cached image data hasn't been retrieved yet
|
||||
if (m_imageData.empty())
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
{
|
||||
AZStd::shared_lock<decltype(m_imageMutex)> imageLock(m_imageMutex);
|
||||
|
||||
@@ -300,7 +325,7 @@ namespace GradientSignal
|
||||
if (!wasPointRejected)
|
||||
{
|
||||
return GetValueFromImageAsset(
|
||||
m_configuration.m_imageAsset, uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f);
|
||||
m_imageData, m_configuration.m_imageAsset->GetImageDescriptor(), uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,6 +340,12 @@ namespace GradientSignal
|
||||
return;
|
||||
}
|
||||
|
||||
// Return immediately if our cached image data hasn't been retrieved yet
|
||||
if (m_imageData.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::Vector3 uvw;
|
||||
bool wasPointRejected = false;
|
||||
|
||||
@@ -327,7 +358,7 @@ namespace GradientSignal
|
||||
if (!wasPointRejected)
|
||||
{
|
||||
outValues[index] = GetValueFromImageAsset(
|
||||
m_configuration.m_imageAsset, uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f);
|
||||
m_imageData, m_configuration.m_imageAsset->GetImageDescriptor(), uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -353,6 +384,10 @@ namespace GradientSignal
|
||||
|
||||
{
|
||||
AZStd::unique_lock<decltype(m_imageMutex)> imageLock(m_imageMutex);
|
||||
|
||||
// Clear our cached image data
|
||||
m_imageData = AZStd::span<const uint8_t>();
|
||||
|
||||
m_configuration.m_imageAsset = AZ::Data::AssetManager::Instance().FindOrCreateAsset(assetId, azrtti_typeid<AZ::RPI::StreamingImageAsset>(), m_configuration.m_imageAsset.GetAutoLoadBehavior());
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
|
||||
#include <LmbrCentral/Dependency/DependencyNotificationBus.h>
|
||||
#include <GradientSignal/Ebuses/GradientPreviewRequestBus.h>
|
||||
|
||||
namespace GradientSignal
|
||||
{
|
||||
@@ -61,6 +62,12 @@ namespace GradientSignal
|
||||
|
||||
void EditorGradientSurfaceDataComponent::Deactivate()
|
||||
{
|
||||
// Make sure any previews for this entity aren't currently trying to refresh. Otherwise, the preview job could call
|
||||
// back into our FilterFunc lambda below after the entity has already been destroyed.
|
||||
AZ::EntityId canceledEntity;
|
||||
GradientSignal::GradientPreviewRequestBus::EventResult(
|
||||
canceledEntity, GetEntityId(), &GradientSignal::GradientPreviewRequestBus::Events::CancelRefresh);
|
||||
|
||||
// If the preview shouldn't be active, use an invalid entityId
|
||||
m_gradientEntityId = AZ::EntityId();
|
||||
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
|
||||
|
||||
@@ -78,11 +78,10 @@ namespace GradientSignal
|
||||
return true;
|
||||
}
|
||||
|
||||
float GetValueFromImageAsset(const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& imageAsset, const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue)
|
||||
float GetValueFromImageAsset(AZStd::span<const uint8_t> imageData, const AZ::RHI::ImageDescriptor& imageDescriptor, const AZ::Vector3& uvw, float tilingX, float tilingY, float defaultValue)
|
||||
{
|
||||
if (imageAsset.IsReady())
|
||||
if (!imageData.empty())
|
||||
{
|
||||
auto imageDescriptor = imageAsset->GetImageDescriptor();
|
||||
auto width = imageDescriptor.m_size.m_width;
|
||||
auto height = imageDescriptor.m_size.m_height;
|
||||
|
||||
@@ -125,7 +124,7 @@ namespace GradientSignal
|
||||
// Flip the y because images are stored in reverse of our world axes
|
||||
y = (height - 1) - y;
|
||||
|
||||
return AZ::RPI::GetSubImagePixelValue<float>(imageAsset, x, y);
|
||||
return AZ::RPI::GetImageDataPixelValue<float>(imageData, imageDescriptor, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace UnitTest
|
||||
AZStd::unique_ptr<AZ::Entity> BuildTestSurfaceMaskGradient(float shapeHalfBounds);
|
||||
AZStd::unique_ptr<AZ::Entity> BuildTestSurfaceSlopeGradient(float shapeHalfBounds);
|
||||
|
||||
AZ::RPI::AssetHandlerPtrList m_assetHandlers;
|
||||
AZStd::fixed_vector<AZStd::unique_ptr<AZ::Data::AssetHandler>, 2> m_assetHandlers;
|
||||
};
|
||||
|
||||
struct GradientSignalTest
|
||||
|
||||
@@ -387,7 +387,9 @@ void HierarchyMenu::New_ElementFromSlice(HierarchyWidget* hierarchy,
|
||||
AZ::Vector2 viewportPosition(-1.0f,-1.0f); // indicates no viewport position specified
|
||||
if (optionalPos)
|
||||
{
|
||||
viewportPosition = QtHelpers::QPointFToVector2(*optionalPos);
|
||||
// Convert position to render viewport coords
|
||||
QPointF scaledPosition = *optionalPos * hierarchy->GetEditorWindow()->GetViewport()->WidgetToViewportFactor();
|
||||
viewportPosition = QtHelpers::QPointFToVector2(scaledPosition);
|
||||
}
|
||||
|
||||
SliceMenuHelpers::CreateInstantiateSliceMenu(hierarchy,
|
||||
@@ -405,7 +407,9 @@ void HierarchyMenu::New_ElementFromSlice(HierarchyWidget* hierarchy,
|
||||
AZ::Vector2 viewportPosition(-1.0f,-1.0f); // indicates no viewport position specified
|
||||
if (optionalPos)
|
||||
{
|
||||
viewportPosition = QtHelpers::QPointFToVector2(*optionalPos);
|
||||
// Convert position to render viewport coords
|
||||
QPointF scaledPosition = *optionalPos * hierarchy->GetEditorWindow()->GetViewport()->WidgetToViewportFactor();
|
||||
viewportPosition = QtHelpers::QPointFToVector2(scaledPosition);
|
||||
}
|
||||
hierarchy->GetEditorWindow()->GetSliceManager()->InstantiateSliceUsingBrowser(hierarchy, viewportPosition);
|
||||
}
|
||||
|
||||
@@ -1239,11 +1239,13 @@ void HierarchyWidget::AddElement(const QTreeWidgetItemRawPtrQList& selectedItems
|
||||
this,
|
||||
selectedItems,
|
||||
childIndex,
|
||||
[optionalPos](AZ::Entity* element)
|
||||
[this, optionalPos](AZ::Entity* element)
|
||||
{
|
||||
if (optionalPos)
|
||||
{
|
||||
EntityHelpers::MoveElementToGlobalPosition(element, *optionalPos);
|
||||
// Convert position to render viewport coords
|
||||
QPoint scaledPosition = *optionalPos * GetEditorWindow()->GetViewport()->WidgetToViewportFactor();
|
||||
EntityHelpers::MoveElementToGlobalPosition(element, scaledPosition);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -121,27 +121,27 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_googletest(
|
||||
NAME Gem::MotionMatching.Tests
|
||||
)
|
||||
endif()
|
||||
|
||||
# If we are a host platform we want to add tools test like editor tests here
|
||||
if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
ly_add_target(
|
||||
NAME MotionMatching.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
motionmatching_editor_tests_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Tests
|
||||
Source
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
Gem::MotionMatching.Editor
|
||||
)
|
||||
# If we are a host platform we want to add tools test like editor tests here
|
||||
if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
ly_add_target(
|
||||
NAME MotionMatching.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
motionmatching_editor_tests_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Tests
|
||||
Source
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
Gem::MotionMatching.Editor
|
||||
)
|
||||
|
||||
# Add MotionMatching.Editor.Tests to googletest
|
||||
ly_add_googletest(
|
||||
NAME Gem::MotionMatching.Editor.Tests
|
||||
)
|
||||
# Add MotionMatching.Editor.Tests to googletest
|
||||
ly_add_googletest(
|
||||
NAME Gem::MotionMatching.Editor.Tests
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -122,43 +122,41 @@ namespace Multiplayer
|
||||
{
|
||||
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
|
||||
NetworkEntityHandle entityHandle(entity, networkEntityTracker);
|
||||
if (entityHandle.GetNetBindComponent() == nullptr)
|
||||
if (entityHandle.GetNetBindComponent() != nullptr)
|
||||
{
|
||||
// Not a net-bound entity, terminate processing of this entity
|
||||
return;
|
||||
}
|
||||
|
||||
const AZ::Aabb currentBounds = entityBoundsUnion->GetEntityWorldBoundsUnion(entity->GetId());
|
||||
const AZ::Vector3 currentCenter = currentBounds.GetCenter();
|
||||
NetworkTransformComponent* networkTransform = entity->template FindComponent<NetworkTransformComponent>();
|
||||
if (debugDisplay)
|
||||
{
|
||||
debugDisplay->SetColor(AZ::Colors::White);
|
||||
debugDisplay->DrawWireBox(currentBounds.GetMin(), currentBounds.GetMax());
|
||||
}
|
||||
|
||||
if (networkTransform != nullptr)
|
||||
{
|
||||
// Get the rewound position for target host frame ID plus the one preceding it for potential lerp
|
||||
AZ::Vector3 rewindCenter = networkTransform->GetTranslation();
|
||||
const AZ::Vector3 rewindCenterPrevious = networkTransform->GetTranslationPrevious();
|
||||
const float blendFactor = GetNetworkTime()->GetHostBlendFactor();
|
||||
if (!AZ::IsClose(blendFactor, 1.0f) && !rewindCenter.IsClose(rewindCenterPrevious))
|
||||
{
|
||||
// If we have a blend factor, lerp the translation for accuracy
|
||||
rewindCenter = rewindCenterPrevious.Lerp(rewindCenter, blendFactor);
|
||||
}
|
||||
const AZ::Vector3 rewindOffset = rewindCenter - currentCenter; // Compute offset between rewound and current positions
|
||||
const AZ::Aabb rewoundAabb = currentBounds.GetTranslated(rewindOffset); // Apply offset to the entity aabb
|
||||
const AZ::Aabb currentBounds = entityBoundsUnion->GetEntityWorldBoundsUnion(entity->GetId());
|
||||
const AZ::Vector3 currentCenter = currentBounds.GetCenter();
|
||||
NetworkTransformComponent* networkTransform = entity->template FindComponent<NetworkTransformComponent>();
|
||||
if (debugDisplay)
|
||||
{
|
||||
debugDisplay->SetColor(AZ::Colors::Grey);
|
||||
debugDisplay->DrawWireBox(rewoundAabb.GetMin(), rewoundAabb.GetMax());
|
||||
debugDisplay->SetColor(AZ::Colors::White);
|
||||
debugDisplay->DrawWireBox(currentBounds.GetMin(), currentBounds.GetMax());
|
||||
}
|
||||
|
||||
if (AZ::ShapeIntersection::Overlaps(rewoundAabb, rewindVolume)) // Validate the rewound aabb intersects our rewind volume
|
||||
if (networkTransform != nullptr)
|
||||
{
|
||||
m_rewoundEntities.push_back(entityHandle);
|
||||
// Get the rewound position for target host frame ID plus the one preceding it for potential lerp
|
||||
AZ::Vector3 rewindCenter = networkTransform->GetTranslation();
|
||||
const AZ::Vector3 rewindCenterPrevious = networkTransform->GetTranslationPrevious();
|
||||
const float blendFactor = GetNetworkTime()->GetHostBlendFactor();
|
||||
if (!AZ::IsClose(blendFactor, 1.0f) && !rewindCenter.IsClose(rewindCenterPrevious))
|
||||
{
|
||||
// If we have a blend factor, lerp the translation for accuracy
|
||||
rewindCenter = rewindCenterPrevious.Lerp(rewindCenter, blendFactor);
|
||||
}
|
||||
const AZ::Vector3 rewindOffset = rewindCenter - currentCenter; // Compute offset between rewound and current positions
|
||||
const AZ::Aabb rewoundAabb = currentBounds.GetTranslated(rewindOffset); // Apply offset to the entity aabb
|
||||
if (debugDisplay)
|
||||
{
|
||||
debugDisplay->SetColor(AZ::Colors::Grey);
|
||||
debugDisplay->DrawWireBox(rewoundAabb.GetMin(), rewoundAabb.GetMax());
|
||||
}
|
||||
|
||||
if (AZ::ShapeIntersection::Overlaps(rewoundAabb, rewindVolume)) // Validate the rewound aabb intersects our rewind volume
|
||||
{
|
||||
m_rewoundEntities.push_back(entityHandle);
|
||||
entityHandle.GetNetBindComponent()->NotifySyncRewindState();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace PhysX
|
||||
"Specifies the PhysX mesh collider asset for this PhysX collider component.")
|
||||
->Attribute(AZ_CRC_CE("EditButton"), "")
|
||||
->Attribute(AZ_CRC_CE("EditDescription"), "Open in Scene Settings")
|
||||
->Attribute(AZ_CRC_CE("DisableEditButtonWhenNoAssetSelected"), true)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyAssetShapeConfig::m_configuration, "Configuration",
|
||||
"PhysX mesh asset collider configuration.")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly);
|
||||
|
||||
+3
-5
@@ -5,7 +5,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <ScriptCanvas/Asset/RuntimeAsset.h>
|
||||
#include <ScriptCanvas/Components/EditorScriptCanvasComponentSerializer.h>
|
||||
@@ -17,7 +17,7 @@ namespace AZ
|
||||
|
||||
JsonSerializationResult::Result EditorScriptCanvasComponentSerializer::Load
|
||||
( void* outputValue
|
||||
, const Uuid& outputValueTypeId
|
||||
, [[maybe_unused]] const Uuid& outputValueTypeId
|
||||
, const rapidjson::Value& inputValue
|
||||
, JsonDeserializerContext& context)
|
||||
{
|
||||
@@ -32,9 +32,7 @@ namespace AZ
|
||||
JsonSerializationResult::ResultCode result = BaseJsonSerializer::Load(outputValue
|
||||
, azrtti_typeid<AzToolsFramework::Components::EditorComponentBase>(), inputValue, context);
|
||||
|
||||
// load child data one by one...
|
||||
result.Combine(BaseJsonSerializer::Load(outputValue, outputValueTypeId, inputValue, context));
|
||||
|
||||
// load child data one by one
|
||||
if (result.GetProcessing() != JSR::Processing::Halted)
|
||||
{
|
||||
result.Combine(ContinueLoadingFromJsonObjectField
|
||||
|
||||
@@ -29,12 +29,6 @@ namespace SurfaceData
|
||||
class SurfaceTagWeights
|
||||
{
|
||||
public:
|
||||
//! The maximum number of surface weights that we can store.
|
||||
//! For performance reasons, we want to limit this so that we can preallocate the max size in advance.
|
||||
//! The current number is chosen to be higher than expected needs, but small enough to avoid being excessively wasteful.
|
||||
//! (Dynamic structures would end up taking more memory than what we're preallocating)
|
||||
static inline constexpr size_t MaxSurfaceWeights = 16;
|
||||
|
||||
SurfaceTagWeights() = default;
|
||||
|
||||
//! Construct a collection of SurfaceTagWeights from the given SurfaceTagWeightList.
|
||||
@@ -65,7 +59,7 @@ namespace SurfaceData
|
||||
// early-out once we pass the location for the entry instead of always searching every entry.
|
||||
if (weightItr->m_surfaceType > tag)
|
||||
{
|
||||
if (m_weights.size() != MaxSurfaceWeights)
|
||||
if (m_weights.size() != AzFramework::SurfaceData::Constants::MaxSurfaceWeights)
|
||||
{
|
||||
// We didn't find the surface type, so add the new entry in sorted order.
|
||||
m_weights.insert(weightItr, { tag, weight });
|
||||
@@ -85,7 +79,7 @@ namespace SurfaceData
|
||||
}
|
||||
|
||||
// We didn't find the surface weight, and the sort order for it is at the end, so add it to the back of the list.
|
||||
if (m_weights.size() != MaxSurfaceWeights)
|
||||
if (m_weights.size() != AzFramework::SurfaceData::Constants::MaxSurfaceWeights)
|
||||
{
|
||||
m_weights.emplace_back(tag, weight);
|
||||
}
|
||||
@@ -188,7 +182,7 @@ namespace SurfaceData
|
||||
//! @return The pointer to the tag that's found, or end() if it wasn't found.
|
||||
const AzFramework::SurfaceData::SurfaceTagWeight* FindTag(AZ::Crc32 tag) const;
|
||||
|
||||
AZStd::fixed_vector<AzFramework::SurfaceData::SurfaceTagWeight, MaxSurfaceWeights> m_weights;
|
||||
AZStd::fixed_vector<AzFramework::SurfaceData::SurfaceTagWeight, AzFramework::SurfaceData::Constants::MaxSurfaceWeights> m_weights;
|
||||
};
|
||||
|
||||
//! SurfacePointList stores a collection of surface point data, which consists of positions, normals, and surface tag weights.
|
||||
|
||||
@@ -19,7 +19,8 @@ namespace SurfaceData
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(SurfaceTag, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(SurfaceTag, "{67C8C6ED-F32A-443E-A777-1CAE48B22CD7}");
|
||||
AZ_TYPE_INFO(SurfaceTag, "{67C8C6ED-F32A-443E-A777-1CAE48B22CD7}");
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
SurfaceTag()
|
||||
@@ -47,6 +48,8 @@ namespace SurfaceData
|
||||
m_surfaceTagCrc = AZ::Crc32(value.data());
|
||||
}
|
||||
|
||||
AZStd::string GetDisplayName() const;
|
||||
|
||||
static AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> GetRegisteredTags();
|
||||
|
||||
private:
|
||||
@@ -54,8 +57,6 @@ namespace SurfaceData
|
||||
|
||||
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> BuildSelectableTagList() const;
|
||||
|
||||
AZStd::string GetDisplayName() const;
|
||||
|
||||
AZ::u32 m_surfaceTagCrc;
|
||||
};
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace SurfaceData
|
||||
AzFramework::SurfaceData::SurfaceTagWeightList SurfaceTagWeights::GetSurfaceTagWeightList() const
|
||||
{
|
||||
AzFramework::SurfaceData::SurfaceTagWeightList weights;
|
||||
weights.reserve(m_weights.size());
|
||||
|
||||
for (auto& weight : m_weights)
|
||||
{
|
||||
weights.emplace_back(weight);
|
||||
|
||||
@@ -275,7 +275,7 @@ namespace UnitTest
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Entity);
|
||||
|
||||
AZ::Crc32 tags[SurfaceData::SurfaceTagWeights::MaxSurfaceWeights];
|
||||
AZ::Crc32 tags[AzFramework::SurfaceData::Constants::MaxSurfaceWeights];
|
||||
AZ::SimpleLcgRandom randomGenerator(1234567);
|
||||
|
||||
// Declare this outside the loop so that we aren't benchmarking creation and destruction.
|
||||
@@ -316,7 +316,7 @@ namespace UnitTest
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Entity);
|
||||
|
||||
AZ::Crc32 tags[SurfaceData::SurfaceTagWeights::MaxSurfaceWeights];
|
||||
AZ::Crc32 tags[AzFramework::SurfaceData::Constants::MaxSurfaceWeights];
|
||||
AZ::SimpleLcgRandom randomGenerator(1234567);
|
||||
|
||||
// Declare this outside the loop so that we aren't benchmarking creation and destruction.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <AzFramework/Physics/PhysicsSystem.h>
|
||||
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
|
||||
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
void TerrainPhysicsSurfaceMaterialMapping::Reflect(AZ::ReflectContext* context)
|
||||
@@ -32,27 +33,10 @@ namespace Terrain
|
||||
->Version(1)
|
||||
->Field("Surface", &TerrainPhysicsSurfaceMaterialMapping::m_surfaceTag)
|
||||
->Field("Material", &TerrainPhysicsSurfaceMaterialMapping::m_materialId);
|
||||
|
||||
if (auto edit = serialize->GetEditContext())
|
||||
{
|
||||
edit->Class<TerrainPhysicsSurfaceMaterialMapping>(
|
||||
"Terrain Surface Material Mapping", "Mapping between a surface and a physics material.")
|
||||
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show)
|
||||
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::ComboBox, &TerrainPhysicsSurfaceMaterialMapping::m_surfaceTag, "Surface Tag",
|
||||
"Surface type to map to a physics material.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &TerrainPhysicsSurfaceMaterialMapping::m_materialId, "Material ID", "")
|
||||
->ElementAttribute(Physics::Attributes::MaterialLibraryAssetId, &TerrainPhysicsSurfaceMaterialMapping::GetMaterialLibraryId)
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::ShowProductAssetFileName, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AZ::Data::AssetId TerrainPhysicsSurfaceMaterialMapping::GetMaterialLibraryId()
|
||||
{
|
||||
if (const auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
|
||||
@@ -76,22 +60,6 @@ namespace Terrain
|
||||
->Field("DefaultMaterial", &TerrainPhysicsColliderConfig::m_defaultMaterialSelection)
|
||||
->Field("Mappings", &TerrainPhysicsColliderConfig::m_surfaceMaterialMappings)
|
||||
;
|
||||
|
||||
if (auto edit = serialize->GetEditContext())
|
||||
{
|
||||
edit->Class<TerrainPhysicsColliderConfig>(
|
||||
"Terrain Physics Collider Component",
|
||||
"Provides terrain data to a physics collider with configurable surface mappings.")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &TerrainPhysicsColliderConfig::m_defaultMaterialSelection,
|
||||
"Default Surface Physics Material", "Select a material to be used by unmapped surfaces by default")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &TerrainPhysicsColliderConfig::m_surfaceMaterialMappings,
|
||||
"Surface to Material Mappings", "Maps surfaces to physics materials")
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace LmbrCentral
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
class EditorSurfaceTagListProvider;
|
||||
|
||||
static const uint8_t InvalidSurfaceTagIndex = 0xFF;
|
||||
|
||||
struct TerrainPhysicsSurfaceMaterialMapping final
|
||||
@@ -33,12 +35,16 @@ namespace Terrain
|
||||
AZ_CLASS_ALLOCATOR(TerrainPhysicsSurfaceMaterialMapping, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(TerrainPhysicsSurfaceMaterialMapping, "{A88B5289-DFCD-4564-8395-E2177DFE5B18}");
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static AZ::Data::AssetId GetMaterialLibraryId();
|
||||
|
||||
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> BuildSelectableTagList() const;
|
||||
void SetTagListProvider(const EditorSurfaceTagListProvider* tagListProvider);
|
||||
|
||||
SurfaceData::SurfaceTag m_surfaceTag;
|
||||
Physics::MaterialId m_materialId;
|
||||
|
||||
private:
|
||||
static AZ::Data::AssetId GetMaterialLibraryId();
|
||||
const EditorSurfaceTagListProvider* m_tagListProvider = nullptr;
|
||||
};
|
||||
|
||||
class TerrainPhysicsColliderConfig
|
||||
@@ -48,6 +54,7 @@ namespace Terrain
|
||||
AZ_CLASS_ALLOCATOR(TerrainPhysicsColliderConfig, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(TerrainPhysicsColliderConfig, "{E9EADB8F-C3A5-4B9C-A62D-2DBC86B4CE59}", AZ::ComponentConfig);
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
Physics::MaterialSelection m_defaultMaterialSelection;
|
||||
AZStd::vector<TerrainPhysicsSurfaceMaterialMapping> m_surfaceMaterialMappings;
|
||||
};
|
||||
|
||||
@@ -26,26 +26,6 @@ namespace Terrain
|
||||
->Field("Gradient Entity", &TerrainSurfaceGradientMapping::m_gradientEntityId)
|
||||
->Field("Surface Tag", &TerrainSurfaceGradientMapping::m_surfaceTag)
|
||||
;
|
||||
|
||||
if (auto edit = serialize->GetEditContext())
|
||||
{
|
||||
edit->Class<TerrainSurfaceGradientMapping>("Terrain Surface Gradient Mapping", "Mapping between a gradient and a surface.")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show)
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &TerrainSurfaceGradientMapping::m_gradientEntityId,
|
||||
"Gradient Entity", "ID of Entity providing a gradient.")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
|
||||
->UIElement("GradientPreviewer", "Previewer")
|
||||
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
|
||||
->Attribute(AZ_CRC_CE("GradientEntity"), &TerrainSurfaceGradientMapping::m_gradientEntityId)
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &TerrainSurfaceGradientMapping::m_surfaceTag, "Surface Tag",
|
||||
"Surface type to map to this gradient.")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
@@ -71,21 +51,6 @@ namespace Terrain
|
||||
->Version(1)
|
||||
->Field("Mappings", &TerrainSurfaceGradientListConfig::m_gradientSurfaceMappings)
|
||||
;
|
||||
|
||||
AZ::EditContext* edit = serialize->GetEditContext();
|
||||
if (edit)
|
||||
{
|
||||
edit->Class<TerrainSurfaceGradientListConfig>(
|
||||
"Terrain Surface Gradient List Component", "Provide mapping between gradients and surfaces.")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show)
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &TerrainSurfaceGradientListConfig::m_gradientSurfaceMappings,
|
||||
"Gradient to Surface Mappings", "Maps Gradient Entities to Surfaces.")
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace LmbrCentral
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
class EditorSurfaceTagListProvider;
|
||||
|
||||
class TerrainSurfaceGradientMapping final
|
||||
{
|
||||
public:
|
||||
@@ -39,8 +41,14 @@ namespace Terrain
|
||||
{
|
||||
}
|
||||
|
||||
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> BuildSelectableTagList() const;
|
||||
void SetTagListProvider(const EditorSurfaceTagListProvider* tagListProvider);
|
||||
|
||||
AZ::EntityId m_gradientEntityId;
|
||||
SurfaceData::SurfaceTag m_surfaceTag;
|
||||
|
||||
private:
|
||||
const EditorSurfaceTagListProvider* m_tagListProvider = nullptr;
|
||||
};
|
||||
|
||||
class TerrainSurfaceGradientListConfig : public AZ::ComponentConfig
|
||||
|
||||
@@ -20,5 +20,85 @@ namespace Terrain
|
||||
&LmbrCentral::EditorWrappedComponentBaseVersionConverter<typename BaseClassType::WrappedComponentType,
|
||||
typename BaseClassType::WrappedConfigType, 1>
|
||||
);
|
||||
|
||||
if (auto serialize = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
if (auto edit = serialize->GetEditContext())
|
||||
{
|
||||
edit->Class<TerrainPhysicsSurfaceMaterialMapping>(
|
||||
"Terrain Surface Material Mapping", "Mapping between a surface and a physics material.")
|
||||
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show)
|
||||
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::ComboBox, &TerrainPhysicsSurfaceMaterialMapping::m_surfaceTag, "Surface Tag",
|
||||
"Surface type to map to a physics material.")
|
||||
->Attribute(AZ::Edit::Attributes::EnumValues, &TerrainPhysicsSurfaceMaterialMapping::BuildSelectableTagList)
|
||||
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &TerrainPhysicsSurfaceMaterialMapping::m_materialId, "Material ID", "")
|
||||
->ElementAttribute(Physics::Attributes::MaterialLibraryAssetId, &TerrainPhysicsSurfaceMaterialMapping::GetMaterialLibraryId)
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::ShowProductAssetFileName, true)
|
||||
;
|
||||
|
||||
edit->Class<TerrainPhysicsColliderConfig>(
|
||||
"Terrain Physics Collider Component",
|
||||
"Provides terrain data to a physics collider with configurable surface mappings.")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &TerrainPhysicsColliderConfig::m_defaultMaterialSelection,
|
||||
"Default Surface Physics Material", "Select a material to be used by unmapped surfaces by default")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &TerrainPhysicsColliderConfig::m_surfaceMaterialMappings,
|
||||
"Surface to Material Mappings", "Maps surfaces to physics materials")
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorTerrainPhysicsColliderComponent::Activate()
|
||||
{
|
||||
UpdateConfigurationTagProvider();
|
||||
BaseClassType::Activate();
|
||||
}
|
||||
|
||||
AZStd::unordered_set<SurfaceData::SurfaceTag> EditorTerrainPhysicsColliderComponent::GetSurfaceTagsInUse() const
|
||||
{
|
||||
AZStd::unordered_set<SurfaceData::SurfaceTag> tagsInUse;
|
||||
|
||||
for (const TerrainPhysicsSurfaceMaterialMapping& mapping : m_configuration.m_surfaceMaterialMappings)
|
||||
{
|
||||
tagsInUse.insert(mapping.m_surfaceTag);
|
||||
}
|
||||
|
||||
return AZStd::move(tagsInUse);
|
||||
}
|
||||
|
||||
AZ::u32 EditorTerrainPhysicsColliderComponent::ConfigurationChanged()
|
||||
{
|
||||
UpdateConfigurationTagProvider();
|
||||
return BaseClassType::ConfigurationChanged();
|
||||
}
|
||||
|
||||
void EditorTerrainPhysicsColliderComponent::UpdateConfigurationTagProvider()
|
||||
{
|
||||
for (TerrainPhysicsSurfaceMaterialMapping& mapping : m_configuration.m_surfaceMaterialMappings)
|
||||
{
|
||||
mapping.SetTagListProvider(this);
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> TerrainPhysicsSurfaceMaterialMapping::BuildSelectableTagList() const
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Entity);
|
||||
return AZStd::move(Terrain::BuildSelectableTagList(m_tagListProvider, m_surfaceTag));
|
||||
}
|
||||
|
||||
void TerrainPhysicsSurfaceMaterialMapping::SetTagListProvider(const EditorSurfaceTagListProvider* tagListProvider)
|
||||
{
|
||||
m_tagListProvider = tagListProvider;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,22 +11,34 @@
|
||||
#include <Components/TerrainPhysicsColliderComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
|
||||
#include <LmbrCentral/Component/EditorWrappedComponentBase.h>
|
||||
#include <EditorSurfaceTagListProvider.h>
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
class EditorTerrainPhysicsColliderComponent
|
||||
: public LmbrCentral::EditorWrappedComponentBase<TerrainPhysicsColliderComponent, TerrainPhysicsColliderConfig>
|
||||
, public EditorSurfaceTagListProvider
|
||||
{
|
||||
public:
|
||||
using BaseClassType = LmbrCentral::EditorWrappedComponentBase<TerrainPhysicsColliderComponent, TerrainPhysicsColliderConfig>;
|
||||
AZ_EDITOR_COMPONENT(EditorTerrainPhysicsColliderComponent, "{C43FAB8F-3968-46A6-920E-E84AEDED3DF5}", BaseClassType);
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
// AZ::Component interface implementation
|
||||
void Activate() override;
|
||||
|
||||
static constexpr auto s_categoryName = "Terrain";
|
||||
static constexpr auto s_componentName = "Terrain Physics Heightfield Collider";
|
||||
static constexpr auto s_componentDescription = "Provides terrain data to a physics collider in the form of a heightfield and surface->material mapping.";
|
||||
static constexpr auto s_icon = "Editor/Icons/Components/TerrainPhysicsCollider.svg";
|
||||
static constexpr auto s_viewportIcon = "Editor/Icons/Components/Viewport/TerrainPhysicsCollider.svg";
|
||||
static constexpr auto s_helpUrl = "";
|
||||
|
||||
private:
|
||||
// EditorSurfaceTagListProvider interface implementation
|
||||
AZStd::unordered_set<SurfaceData::SurfaceTag> GetSurfaceTagsInUse() const override;
|
||||
|
||||
AZ::u32 ConfigurationChanged() override;
|
||||
void UpdateConfigurationTagProvider();
|
||||
};
|
||||
}
|
||||
|
||||
+78
@@ -19,5 +19,83 @@ namespace Terrain
|
||||
&LmbrCentral::EditorWrappedComponentBaseVersionConverter<typename BaseClassType::WrappedComponentType,
|
||||
typename BaseClassType::WrappedConfigType, 1>
|
||||
);
|
||||
|
||||
if (auto serialize = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
if (auto edit = serialize->GetEditContext())
|
||||
{
|
||||
edit->Class<TerrainSurfaceGradientMapping>("Terrain Surface Gradient Mapping", "Mapping between a gradient and a surface.")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show)
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &TerrainSurfaceGradientMapping::m_gradientEntityId,
|
||||
"Gradient Entity", "ID of Entity providing a gradient.")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
|
||||
->UIElement("GradientPreviewer", "Previewer")
|
||||
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
|
||||
->Attribute(AZ_CRC_CE("GradientEntity"), &TerrainSurfaceGradientMapping::m_gradientEntityId)
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &TerrainSurfaceGradientMapping::m_surfaceTag, "Surface Tag",
|
||||
"Surface type to map to this gradient.")
|
||||
->Attribute(AZ::Edit::Attributes::EnumValues, &TerrainSurfaceGradientMapping::BuildSelectableTagList)
|
||||
;
|
||||
|
||||
edit->Class<TerrainSurfaceGradientListConfig>(
|
||||
"Terrain Surface Gradient List Component", "Provide mapping between gradients and surfaces.")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show)
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &TerrainSurfaceGradientListConfig::m_gradientSurfaceMappings,
|
||||
"Gradient to Surface Mappings", "Maps Gradient Entities to Surfaces.")
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorTerrainSurfaceGradientListComponent::Activate()
|
||||
{
|
||||
UpdateConfigurationTagProvider();
|
||||
BaseClassType::Activate();
|
||||
}
|
||||
|
||||
AZ::u32 EditorTerrainSurfaceGradientListComponent::ConfigurationChanged()
|
||||
{
|
||||
UpdateConfigurationTagProvider();
|
||||
return BaseClassType::ConfigurationChanged();
|
||||
}
|
||||
|
||||
void EditorTerrainSurfaceGradientListComponent::UpdateConfigurationTagProvider()
|
||||
{
|
||||
for (TerrainSurfaceGradientMapping& mapping : m_configuration.m_gradientSurfaceMappings)
|
||||
{
|
||||
mapping.SetTagListProvider(this);
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::unordered_set<SurfaceData::SurfaceTag> EditorTerrainSurfaceGradientListComponent::GetSurfaceTagsInUse() const
|
||||
{
|
||||
AZStd::unordered_set<SurfaceData::SurfaceTag> tagsInUse;
|
||||
|
||||
for (const TerrainSurfaceGradientMapping& mapping : m_configuration.m_gradientSurfaceMappings)
|
||||
{
|
||||
tagsInUse.insert(mapping.m_surfaceTag);
|
||||
}
|
||||
|
||||
return AZStd::move(tagsInUse);
|
||||
}
|
||||
|
||||
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> TerrainSurfaceGradientMapping::BuildSelectableTagList() const
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Entity);
|
||||
return AZStd::move(Terrain::BuildSelectableTagList(m_tagListProvider, m_surfaceTag));
|
||||
}
|
||||
|
||||
void TerrainSurfaceGradientMapping::SetTagListProvider(const EditorSurfaceTagListProvider* tagListProvider)
|
||||
{
|
||||
m_tagListProvider = tagListProvider;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,22 +11,34 @@
|
||||
#include <Components/TerrainSurfaceGradientListComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
|
||||
#include <LmbrCentral/Component/EditorWrappedComponentBase.h>
|
||||
#include <EditorSurfaceTagListProvider.h>
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
class EditorTerrainSurfaceGradientListComponent
|
||||
: public LmbrCentral::EditorWrappedComponentBase<TerrainSurfaceGradientListComponent, TerrainSurfaceGradientListConfig>
|
||||
, public EditorSurfaceTagListProvider
|
||||
{
|
||||
public:
|
||||
using BaseClassType = LmbrCentral::EditorWrappedComponentBase<TerrainSurfaceGradientListComponent, TerrainSurfaceGradientListConfig>;
|
||||
AZ_EDITOR_COMPONENT(EditorTerrainSurfaceGradientListComponent, "{49831E91-A11F-4EFF-A824-6D85C284B934}", BaseClassType);
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
// AZ::Component interface implementation
|
||||
void Activate() override;
|
||||
|
||||
static constexpr const char* const s_categoryName = "Terrain";
|
||||
static constexpr const char* const s_componentName = "Terrain Surface Gradient List";
|
||||
static constexpr const char* const s_componentDescription = "Provides a mapping between gradients and surface tags for use by the terrain system.";
|
||||
static constexpr const char* const s_icon = "Editor/Icons/Components/TerrainSurfaceGradientList.svg";
|
||||
static constexpr const char* const s_viewportIcon = "Editor/Icons/Components/Viewport/TerrainSurfaceGradientList.svg";
|
||||
static constexpr const char* const s_helpUrl = "https://o3de.org/docs/user-guide/components/reference/terrain/surface-gradient-list/";
|
||||
|
||||
private:
|
||||
// EditorSurfaceTagListProvider interface implementation
|
||||
AZStd::unordered_set<SurfaceData::SurfaceTag> GetSurfaceTagsInUse() const override;
|
||||
|
||||
AZ::u32 ConfigurationChanged() override;
|
||||
void UpdateConfigurationTagProvider();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 <EditorSurfaceTagListProvider.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> BuildSelectableTagList(const EditorSurfaceTagListProvider* tagListProvider,
|
||||
const SurfaceData::SurfaceTag& currentTag)
|
||||
{
|
||||
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> availableTags = SurfaceData::SurfaceTag::GetRegisteredTags();
|
||||
|
||||
AZStd::unordered_set<SurfaceData::SurfaceTag> tagsInUse;
|
||||
|
||||
if (tagListProvider)
|
||||
{
|
||||
tagsInUse = AZStd::move(tagListProvider->GetSurfaceTagsInUse());
|
||||
|
||||
// Filter out all tags in use from the list of registered tags
|
||||
AZStd::erase_if(availableTags, [&tagsInUse](const auto& tag)-> bool
|
||||
{
|
||||
return tagsInUse.contains(SurfaceData::SurfaceTag(tag.first));
|
||||
});
|
||||
}
|
||||
|
||||
// Insert the current tag back if it was removed via tagsInUse
|
||||
availableTags.emplace_back(AZ::u32(currentTag), currentTag.GetDisplayName());
|
||||
|
||||
// Sorting for consistency
|
||||
AZStd::sort(availableTags.begin(), availableTags.end(), [](const auto& lhs, const auto& rhs) {return lhs.second < rhs.second; });
|
||||
|
||||
return AZStd::move(availableTags);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
#include <SurfaceData/SurfaceTag.h>
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
//! Interface for a class providing information about surface tags available for selecting in Editor components.
|
||||
class EditorSurfaceTagListProvider
|
||||
{
|
||||
public:
|
||||
//! Returns a set of all surface tags currently in use that won't be available for selecting.
|
||||
virtual AZStd::unordered_set<SurfaceData::SurfaceTag> GetSurfaceTagsInUse() const = 0;
|
||||
};
|
||||
|
||||
//! Returns a list of available tags to be selected in the component.
|
||||
AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> BuildSelectableTagList(
|
||||
const EditorSurfaceTagListProvider* tagListProvider,
|
||||
const SurfaceData::SurfaceTag& currentTag);
|
||||
}
|
||||
+170
-63
@@ -8,21 +8,19 @@
|
||||
|
||||
#include <TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h>
|
||||
|
||||
#include <AzCore/Asset/AssetManagerBus.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/Asset/AssetManagerBus.h>
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Math/Aabb.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
|
||||
#include <GradientSignal/Ebuses/GradientRequestBus.h>
|
||||
#include <SurfaceData/SurfaceDataProviderRequestBus.h>
|
||||
|
||||
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
|
||||
|
||||
#include <GradientSignal/Ebuses/GradientRequestBus.h>
|
||||
#include <SurfaceData/SurfaceDataProviderRequestBus.h>
|
||||
#include <TerrainSystem/TerrainSystemBus.h>
|
||||
|
||||
namespace Terrain
|
||||
@@ -38,15 +36,14 @@ namespace Terrain
|
||||
|
||||
if (auto edit = serialize->GetEditContext())
|
||||
{
|
||||
edit->Class<TerrainSurfaceMaterialMapping>("Terrain Surface Gradient Mapping", "Mapping between a surface and a material.")
|
||||
edit->Class<TerrainSurfaceMaterialMapping>("Terrain surface gradient mapping", "Mapping between a surface and a material.")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show)
|
||||
|
||||
->DataElement(AZ::Edit::UIHandlers::ComboBox, &TerrainSurfaceMaterialMapping::m_surfaceTag, "Surface Tag", "Surface type to map to a material.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &TerrainSurfaceMaterialMapping::m_materialAsset, "Material Asset", "")
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::ShowProductAssetFileName, true)
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
|
||||
->DataElement(AZ::Edit::UIHandlers::ComboBox, &TerrainSurfaceMaterialMapping::m_surfaceTag, "Surface tag", "Surface type to map to a material.")
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &TerrainSurfaceMaterialMapping::m_materialAsset, "Material asset", "")
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::ShowProductAssetFileName, true)
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -60,7 +57,8 @@ namespace Terrain
|
||||
if (serialize)
|
||||
{
|
||||
serialize->Class<TerrainSurfaceMaterialsListConfig, AZ::ComponentConfig>()
|
||||
->Version(1)
|
||||
->Version(2)
|
||||
->Field("DefaultMaterial", &TerrainSurfaceMaterialsListConfig::m_defaultSurfaceMaterial)
|
||||
->Field("Mappings", &TerrainSurfaceMaterialsListConfig::m_surfaceMaterials);
|
||||
|
||||
AZ::EditContext* edit = serialize->GetEditContext();
|
||||
@@ -68,10 +66,13 @@ namespace Terrain
|
||||
{
|
||||
edit->Class<TerrainSurfaceMaterialsListConfig>(
|
||||
"Terrain Surface Material List Component", "Provide mapping between surfaces and render materials.")
|
||||
->SetDynamicEditDataProvider(&TerrainSurfaceMaterialsListConfig::GetDynamicData)
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show)
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show)
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &TerrainSurfaceMaterialsListConfig::m_defaultSurfaceMaterial,
|
||||
"Default Material", "The default material to fall back to where no other material surface mappings exist.")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &TerrainSurfaceMaterialsListConfig::m_surfaceMaterials,
|
||||
"Material Mappings", "Maps surfaces to materials.");
|
||||
@@ -79,6 +80,26 @@ namespace Terrain
|
||||
}
|
||||
}
|
||||
|
||||
TerrainSurfaceMaterialsListConfig::TerrainSurfaceMaterialsListConfig()
|
||||
{
|
||||
m_hideSurfaceTagData.m_attributes.push_back(
|
||||
{
|
||||
AZ::Edit::Attributes::Visibility,
|
||||
aznew AZ::Edit::AttributeData<AZ::Crc32>(AZ::Edit::PropertyVisibility::Hide)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const AZ::Edit::ElementData* TerrainSurfaceMaterialsListConfig::GetDynamicData(const void* handlerPtr, const void* elementPtr, const AZ::Uuid& )
|
||||
{
|
||||
const TerrainSurfaceMaterialsListConfig* owner = reinterpret_cast<const TerrainSurfaceMaterialsListConfig*>(handlerPtr);
|
||||
if (elementPtr == &owner->m_defaultSurfaceMaterial.m_surfaceTag)
|
||||
{
|
||||
return &owner->m_hideSurfaceTagData;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TerrainSurfaceMaterialsListComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
|
||||
{
|
||||
services.push_back(AZ_CRC_CE("TerrainMaterialProviderService"));
|
||||
@@ -116,15 +137,21 @@ namespace Terrain
|
||||
{
|
||||
m_cachedAabb = AZ::Aabb::CreateNull();
|
||||
|
||||
auto checkLoadMaterial = [&](TerrainSurfaceMaterialMapping& material)
|
||||
{
|
||||
if (material.m_materialAsset.GetId().IsValid())
|
||||
{
|
||||
material.m_active = false;
|
||||
material.m_materialAsset.QueueLoad();
|
||||
AZ::Data::AssetBus::MultiHandler::BusConnect(material.m_materialAsset.GetId());
|
||||
}
|
||||
};
|
||||
|
||||
// Set all the materials as inactive and start loading.
|
||||
checkLoadMaterial(m_configuration.m_defaultSurfaceMaterial);
|
||||
for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials)
|
||||
{
|
||||
if (surfaceMaterialMapping.m_materialAsset.GetId().IsValid())
|
||||
{
|
||||
surfaceMaterialMapping.m_active = false;
|
||||
surfaceMaterialMapping.m_materialAsset.QueueLoad();
|
||||
AZ::Data::AssetBus::MultiHandler::BusConnect(surfaceMaterialMapping.m_materialAsset.GetId());
|
||||
}
|
||||
checkLoadMaterial(surfaceMaterialMapping);
|
||||
}
|
||||
|
||||
// Announce initial shape using OnShapeChanged
|
||||
@@ -135,24 +162,35 @@ namespace Terrain
|
||||
{
|
||||
TerrainAreaMaterialRequestBus::Handler::BusDisconnect();
|
||||
|
||||
auto checkResetMaterial = [&](TerrainSurfaceMaterialMapping& material)
|
||||
{
|
||||
if (material.m_materialAsset.GetId().IsValid())
|
||||
{
|
||||
AZ::Data::AssetBus::MultiHandler::BusDisconnect(material.m_materialAsset.GetId());
|
||||
material.m_materialAsset.Release();
|
||||
material.m_materialInstance.reset();
|
||||
material.m_activeMaterialAssetId = AZ::Data::AssetId();
|
||||
}
|
||||
};
|
||||
|
||||
checkResetMaterial(m_configuration.m_defaultSurfaceMaterial);
|
||||
for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials)
|
||||
{
|
||||
if (surfaceMaterialMapping.m_materialAsset.GetId().IsValid())
|
||||
{
|
||||
AZ::Data::AssetBus::MultiHandler::BusDisconnect(surfaceMaterialMapping.m_materialAsset.GetId());
|
||||
surfaceMaterialMapping.m_materialAsset.Release();
|
||||
surfaceMaterialMapping.m_materialInstance.reset();
|
||||
surfaceMaterialMapping.m_activeMaterialAssetId = AZ::Data::AssetId();
|
||||
}
|
||||
checkResetMaterial(surfaceMaterialMapping);
|
||||
}
|
||||
|
||||
HandleMaterialStateChanges();
|
||||
}
|
||||
|
||||
int TerrainSurfaceMaterialsListComponent::CountMaterialIDInstances(AZ::Data::AssetId id) const
|
||||
int TerrainSurfaceMaterialsListComponent::CountMaterialIdInstances(AZ::Data::AssetId id) const
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
if (m_configuration.m_defaultSurfaceMaterial.m_activeMaterialAssetId == id)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
for (const auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials)
|
||||
{
|
||||
if (surfaceMaterialMapping.m_activeMaterialAssetId == id)
|
||||
@@ -169,28 +207,63 @@ namespace Terrain
|
||||
bool anyMaterialIsActive = false;
|
||||
bool anyMaterialWasAlreadyActive = false;
|
||||
|
||||
for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials)
|
||||
{
|
||||
const bool wasPreviouslyActive = surfaceMaterialMapping.m_active;
|
||||
const bool isNowActive = (surfaceMaterialMapping.m_materialInstance != nullptr);
|
||||
// Handle default material first
|
||||
auto& defaultMaterial = m_configuration.m_defaultSurfaceMaterial;
|
||||
|
||||
if (wasPreviouslyActive)
|
||||
{
|
||||
anyMaterialWasAlreadyActive = true;
|
||||
}
|
||||
const bool wasPreviouslyActive = defaultMaterial.m_active;
|
||||
defaultMaterial.m_active = (defaultMaterial.m_materialInstance != nullptr);
|
||||
|
||||
if (isNowActive)
|
||||
{
|
||||
anyMaterialIsActive = true;
|
||||
}
|
||||
|
||||
surfaceMaterialMapping.m_active = isNowActive;
|
||||
|
||||
if (!wasPreviouslyActive && !isNowActive)
|
||||
anyMaterialWasAlreadyActive = wasPreviouslyActive;
|
||||
anyMaterialIsActive = defaultMaterial.m_active;
|
||||
|
||||
if (!wasPreviouslyActive && !defaultMaterial.m_active)
|
||||
{
|
||||
// A material has been assigned but has not yet completed loading.
|
||||
}
|
||||
else if (!wasPreviouslyActive && isNowActive)
|
||||
else if (!wasPreviouslyActive && defaultMaterial.m_active)
|
||||
{
|
||||
TerrainAreaMaterialNotificationBus::Broadcast(
|
||||
&TerrainAreaMaterialNotificationBus::Events::OnTerrainDefaultSurfaceMaterialCreated, GetEntityId(),
|
||||
defaultMaterial.m_materialInstance);
|
||||
defaultMaterial.m_previousChangeId = defaultMaterial.m_materialInstance->GetCurrentChangeId();
|
||||
}
|
||||
else if (wasPreviouslyActive && !defaultMaterial.m_active)
|
||||
{
|
||||
// Don't disconnect from the AssetBus if this material is mapped more than once.
|
||||
if (CountMaterialIdInstances(defaultMaterial.m_activeMaterialAssetId) == 1)
|
||||
{
|
||||
AZ::Data::AssetBus::MultiHandler::BusDisconnect(defaultMaterial.m_activeMaterialAssetId);
|
||||
}
|
||||
defaultMaterial = {};
|
||||
|
||||
TerrainAreaMaterialNotificationBus::Broadcast(
|
||||
&TerrainAreaMaterialNotificationBus::Events::OnTerrainDefaultSurfaceMaterialDestroyed, GetEntityId());
|
||||
}
|
||||
else if (defaultMaterial.m_materialInstance->GetAssetId() != defaultMaterial.m_activeMaterialAssetId ||
|
||||
defaultMaterial.m_materialInstance->GetCurrentChangeId() != defaultMaterial.m_previousChangeId)
|
||||
{
|
||||
defaultMaterial.m_previousChangeId = defaultMaterial.m_materialInstance->GetCurrentChangeId();
|
||||
defaultMaterial.m_activeMaterialAssetId = defaultMaterial.m_materialInstance->GetAssetId();
|
||||
|
||||
TerrainAreaMaterialNotificationBus::Broadcast(
|
||||
&TerrainAreaMaterialNotificationBus::Events::OnTerrainDefaultSurfaceMaterialChanged, GetEntityId(), defaultMaterial.m_materialInstance);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials)
|
||||
{
|
||||
const bool wasPreviouslyActive = surfaceMaterialMapping.m_active;
|
||||
surfaceMaterialMapping.m_active = surfaceMaterialMapping.m_materialInstance != nullptr;
|
||||
|
||||
anyMaterialWasAlreadyActive = anyMaterialWasAlreadyActive || wasPreviouslyActive;
|
||||
anyMaterialIsActive = anyMaterialIsActive || surfaceMaterialMapping.m_active;
|
||||
|
||||
if (!wasPreviouslyActive && !surfaceMaterialMapping.m_active)
|
||||
{
|
||||
// A material has been assigned but has not yet completed loading.
|
||||
}
|
||||
else if (!wasPreviouslyActive && surfaceMaterialMapping.m_active)
|
||||
{
|
||||
// Remember the asset id so we can disconnect from the AssetBus if the material asset is removed.
|
||||
surfaceMaterialMapping.m_activeMaterialAssetId = surfaceMaterialMapping.m_materialAsset.GetId();
|
||||
@@ -199,27 +272,47 @@ namespace Terrain
|
||||
&TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingCreated, GetEntityId(),
|
||||
surfaceMaterialMapping.m_surfaceTag,
|
||||
surfaceMaterialMapping.m_materialInstance);
|
||||
|
||||
surfaceMaterialMapping.m_previousChangeId = surfaceMaterialMapping.m_materialInstance->GetCurrentChangeId();
|
||||
surfaceMaterialMapping.m_previousTag = surfaceMaterialMapping.m_surfaceTag;
|
||||
}
|
||||
else if (wasPreviouslyActive && !isNowActive)
|
||||
else if (wasPreviouslyActive && !surfaceMaterialMapping.m_active)
|
||||
{
|
||||
// Don't disconnect from the AssetBus if this material is mapped more than once.
|
||||
if (CountMaterialIDInstances(surfaceMaterialMapping.m_activeMaterialAssetId) == 1)
|
||||
if (CountMaterialIdInstances(surfaceMaterialMapping.m_activeMaterialAssetId) == 1)
|
||||
{
|
||||
AZ::Data::AssetBus::MultiHandler::BusDisconnect(surfaceMaterialMapping.m_activeMaterialAssetId);
|
||||
}
|
||||
|
||||
surfaceMaterialMapping.m_activeMaterialAssetId = AZ::Data::AssetId();
|
||||
surfaceMaterialMapping.m_activeMaterialAssetId = {};
|
||||
surfaceMaterialMapping.m_previousChangeId = AZ::RPI::Material::DEFAULT_CHANGE_ID;
|
||||
surfaceMaterialMapping.m_previousTag = {};
|
||||
|
||||
TerrainAreaMaterialNotificationBus::Broadcast(
|
||||
&TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingDestroyed, GetEntityId(),
|
||||
surfaceMaterialMapping.m_surfaceTag);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
TerrainAreaMaterialNotificationBus::Broadcast(
|
||||
&TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingChanged, GetEntityId(),
|
||||
surfaceMaterialMapping.m_surfaceTag,
|
||||
surfaceMaterialMapping.m_materialInstance);
|
||||
if (surfaceMaterialMapping.m_previousTag != surfaceMaterialMapping.m_surfaceTag)
|
||||
{
|
||||
TerrainAreaMaterialNotificationBus::Broadcast(
|
||||
&TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingTagChanged, GetEntityId(),
|
||||
surfaceMaterialMapping.m_previousTag,
|
||||
surfaceMaterialMapping.m_surfaceTag);
|
||||
surfaceMaterialMapping.m_previousTag = surfaceMaterialMapping.m_surfaceTag;
|
||||
}
|
||||
if (surfaceMaterialMapping.m_materialInstance->GetAssetId() != surfaceMaterialMapping.m_activeMaterialAssetId ||
|
||||
surfaceMaterialMapping.m_materialInstance->GetCurrentChangeId() != surfaceMaterialMapping.m_previousChangeId)
|
||||
{
|
||||
surfaceMaterialMapping.m_previousChangeId = surfaceMaterialMapping.m_materialInstance->GetCurrentChangeId();
|
||||
surfaceMaterialMapping.m_activeMaterialAssetId = surfaceMaterialMapping.m_materialInstance->GetAssetId();
|
||||
|
||||
TerrainAreaMaterialNotificationBus::Broadcast(
|
||||
&TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingMaterialChanged, GetEntityId(),
|
||||
surfaceMaterialMapping.m_surfaceTag,
|
||||
surfaceMaterialMapping.m_materialInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,14 +383,28 @@ namespace Terrain
|
||||
void TerrainSurfaceMaterialsListComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
|
||||
{
|
||||
// Find the missing material instance with the correct id.
|
||||
for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials)
|
||||
auto checkUpdateMaterialAsset = [](TerrainSurfaceMaterialMapping& mapping, const AZ::Data::Asset<AZ::Data::AssetData>& asset) -> bool
|
||||
{
|
||||
if (surfaceMaterialMapping.m_materialAsset.GetId() == asset.GetId() &&
|
||||
(!surfaceMaterialMapping.m_materialInstance ||
|
||||
surfaceMaterialMapping.m_materialInstance->GetAssetId() != surfaceMaterialMapping.m_materialAsset.GetId()))
|
||||
if (mapping.m_materialAsset.GetId() == asset.GetId() &&
|
||||
(!mapping.m_materialInstance || mapping.m_materialInstance->GetAssetId() != mapping.m_materialAsset.GetId()))
|
||||
{
|
||||
surfaceMaterialMapping.m_materialInstance = AZ::RPI::Material::FindOrCreate(surfaceMaterialMapping.m_materialAsset);
|
||||
surfaceMaterialMapping.m_materialAsset.Release();
|
||||
mapping.m_materialInstance = AZ::RPI::Material::FindOrCreate(mapping.m_materialAsset);
|
||||
mapping.m_materialAsset.Release();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// First check the default material
|
||||
if (!checkUpdateMaterialAsset(m_configuration.m_defaultSurfaceMaterial, asset))
|
||||
{
|
||||
// If the default materail wasn't updated, then check all the surface material mappings.
|
||||
for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials)
|
||||
{
|
||||
if (checkUpdateMaterialAsset(surfaceMaterialMapping, asset))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
HandleMaterialStateChanges();
|
||||
|
||||
+20
-7
@@ -8,15 +8,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Atom/Feature/Material/MaterialAssignment.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
|
||||
#include <Atom/Feature/Material/MaterialAssignment.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
|
||||
#include <LmbrCentral/Shape/ShapeComponentBus.h>
|
||||
|
||||
#include <SurfaceData/SurfaceDataTypes.h>
|
||||
#include <TerrainRenderer/TerrainAreaMaterialRequestBus.h>
|
||||
|
||||
|
||||
namespace LmbrCentral
|
||||
{
|
||||
template<typename, typename>
|
||||
@@ -27,16 +30,20 @@ namespace Terrain
|
||||
{
|
||||
struct TerrainSurfaceMaterialMapping final
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(TerrainSurfaceMaterialMapping, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(TerrainSurfaceMaterialMapping, "{37D2A586-CDDD-4FB7-A7D6-0B4CC575AB8C}");
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
SurfaceData::SurfaceTag m_surfaceTag;
|
||||
AZ::Data::AssetId m_activeMaterialAssetId;
|
||||
AZ::Data::Asset<AZ::RPI::MaterialAsset> m_materialAsset;
|
||||
AZ::Data::Instance<AZ::RPI::Material> m_materialInstance;
|
||||
|
||||
AZ::Data::AssetId m_activeMaterialAssetId;
|
||||
AZ::RPI::Material::ChangeId m_previousChangeId = AZ::RPI::Material::DEFAULT_CHANGE_ID;
|
||||
|
||||
// Surface tags not used by default material
|
||||
SurfaceData::SurfaceTag m_surfaceTag;
|
||||
SurfaceData::SurfaceTag m_previousTag;
|
||||
|
||||
bool m_active = false;
|
||||
};
|
||||
|
||||
@@ -47,7 +54,13 @@ namespace Terrain
|
||||
AZ_RTTI(TerrainSurfaceMaterialsListConfig, "{68A1CB1B-C835-4C3A-8D1C-08692E07711A}", AZ::ComponentConfig);
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
TerrainSurfaceMaterialsListConfig();
|
||||
|
||||
TerrainSurfaceMaterialMapping m_defaultSurfaceMaterial;
|
||||
AZStd::vector<TerrainSurfaceMaterialMapping> m_surfaceMaterials;
|
||||
private:
|
||||
static const AZ::Edit::ElementData* GetDynamicData(const void* handlerPtr, const void* elementPtr, const AZ::Uuid& elementType);
|
||||
AZ::Edit::ElementData m_hideSurfaceTagData;
|
||||
};
|
||||
|
||||
class TerrainSurfaceMaterialsListComponent
|
||||
@@ -78,7 +91,7 @@ namespace Terrain
|
||||
|
||||
private:
|
||||
void HandleMaterialStateChanges();
|
||||
int CountMaterialIDInstances(AZ::Data::AssetId id) const;
|
||||
int CountMaterialIdInstances(AZ::Data::AssetId id) const;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// ShapeComponentNotificationsBus
|
||||
|
||||
@@ -45,6 +45,27 @@ namespace Terrain
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! The default surface material has been assigned and loaded
|
||||
virtual void OnTerrainDefaultSurfaceMaterialCreated(
|
||||
[[maybe_unused]] AZ::EntityId entityId,
|
||||
[[maybe_unused]] AZ::Data::Instance<AZ::RPI::Material> material)
|
||||
{
|
||||
}
|
||||
|
||||
//! The default surface material has been unassigned
|
||||
virtual void OnTerrainDefaultSurfaceMaterialDestroyed(
|
||||
[[maybe_unused]] AZ::EntityId entityId)
|
||||
{
|
||||
}
|
||||
|
||||
//! The default surface material has been changed to a different material
|
||||
virtual void OnTerrainDefaultSurfaceMaterialChanged(
|
||||
[[maybe_unused]] AZ::EntityId entityId,
|
||||
[[maybe_unused]] AZ::Data::Instance<AZ::RPI::Material> newMaterial)
|
||||
{
|
||||
}
|
||||
|
||||
//! A loaded material mapped to a valid surface tag has been created
|
||||
virtual void OnTerrainSurfaceMaterialMappingCreated(
|
||||
[[maybe_unused]] AZ::EntityId entityId,
|
||||
[[maybe_unused]] SurfaceData::SurfaceTag surface,
|
||||
@@ -52,19 +73,30 @@ namespace Terrain
|
||||
{
|
||||
}
|
||||
|
||||
//! Either the material or surface tag was unassigned, making this mapping invalid
|
||||
virtual void OnTerrainSurfaceMaterialMappingDestroyed(
|
||||
[[maybe_unused]] AZ::EntityId entityId,
|
||||
[[maybe_unused]] SurfaceData::SurfaceTag surface)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void OnTerrainSurfaceMaterialMappingChanged(
|
||||
//! The surface tag has changed to tag for an existing material
|
||||
virtual void OnTerrainSurfaceMaterialMappingTagChanged(
|
||||
[[maybe_unused]] AZ::EntityId entityId,
|
||||
[[maybe_unused]] SurfaceData::SurfaceTag oldSurface,
|
||||
[[maybe_unused]] SurfaceData::SurfaceTag newSurface)
|
||||
{
|
||||
}
|
||||
|
||||
//! The material has changed for an existing surface tag
|
||||
virtual void OnTerrainSurfaceMaterialMappingMaterialChanged(
|
||||
[[maybe_unused]] AZ::EntityId entityId,
|
||||
[[maybe_unused]] SurfaceData::SurfaceTag surface,
|
||||
[[maybe_unused]] AZ::Data::Instance<AZ::RPI::Material> material)
|
||||
{
|
||||
}
|
||||
|
||||
//! The bounds of this set of surface material mappings has changed
|
||||
virtual void OnTerrainSurfaceMaterialMappingRegionChanged(
|
||||
[[maybe_unused]] AZ::EntityId entityId,
|
||||
[[maybe_unused]] const AZ::Aabb& oldRegion,
|
||||
|
||||
@@ -106,6 +106,8 @@ namespace Terrain
|
||||
return;
|
||||
}
|
||||
|
||||
InitializePassthroughDetailMaterial();
|
||||
|
||||
ClipmapBoundsDescriptor desc;
|
||||
desc.m_clipmapUpdateMultiple = 1;
|
||||
desc.m_clipToWorldScale = DetailTextureScale;
|
||||
@@ -260,20 +262,73 @@ namespace Terrain
|
||||
m_dirtyDetailRegion.AddAabb(dirtyRegion);
|
||||
}
|
||||
}
|
||||
|
||||
bool TerrainDetailMaterialManager::ForSurfaceTag(DetailMaterialListRegion& materialRegion,
|
||||
SurfaceData::SurfaceTag surfaceTag, DefaultMaterialSurfaceCallback callback)
|
||||
{
|
||||
for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces)
|
||||
{
|
||||
if (surface.m_surfaceTag == surfaceTag)
|
||||
{
|
||||
callback(surface);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TerrainDetailMaterialManager::OnTerrainDefaultSurfaceMaterialCreated(AZ::EntityId entityId, MaterialInstance material)
|
||||
{
|
||||
DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions);
|
||||
AZ_Error("TerrainDetailMaterialManager", materialRegion.m_defaultDetailMaterialId == InvalidDetailMaterailId,
|
||||
"Default detail material created but was already set for this region.");
|
||||
|
||||
materialRegion.m_defaultDetailMaterialId = CreateOrUpdateDetailMaterial(material);
|
||||
m_detailMaterials.GetData(materialRegion.m_defaultDetailMaterialId).refCount++;
|
||||
m_dirtyDetailRegion.AddAabb(materialRegion.m_region);
|
||||
}
|
||||
|
||||
void TerrainDetailMaterialManager::OnTerrainDefaultSurfaceMaterialDestroyed(AZ::EntityId entityId)
|
||||
{
|
||||
DetailMaterialListRegion* materialRegion = FindByEntityId(entityId, m_detailMaterialRegions);
|
||||
if (materialRegion == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "OnTerrainDefaultSurfaceMaterialDestroyed() called for region that doesn't exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
CheckDetailMaterialForDeletion(materialRegion->m_defaultDetailMaterialId);
|
||||
materialRegion->m_defaultDetailMaterialId = InvalidDetailMaterailId;
|
||||
}
|
||||
|
||||
void TerrainDetailMaterialManager::OnTerrainDefaultSurfaceMaterialChanged(AZ::EntityId entityId, MaterialInstance newMaterial)
|
||||
{
|
||||
DetailMaterialListRegion* materialRegion = FindByEntityId(entityId, m_detailMaterialRegions);
|
||||
if (materialRegion == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "OnTerrainDefaultSurfaceMaterialChanged() called for region that doesn't exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update existing entry or create a new material entry
|
||||
uint16_t materialId = CreateOrUpdateDetailMaterial(newMaterial);
|
||||
if (materialRegion->m_defaultDetailMaterialId != materialId)
|
||||
{
|
||||
++m_detailMaterials.GetData(materialId).refCount;
|
||||
CheckDetailMaterialForDeletion(materialRegion->m_defaultDetailMaterialId);
|
||||
materialRegion->m_defaultDetailMaterialId = materialId;
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingCreated(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material)
|
||||
{
|
||||
DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions);
|
||||
|
||||
// Validate that the surface tag is new
|
||||
for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces)
|
||||
ForSurfaceTag(materialRegion, surfaceTag, [](DetailMaterialSurface&)
|
||||
{
|
||||
if (surface.m_surfaceTag == surfaceTag)
|
||||
{
|
||||
AZ_Error(TerrainDetailMaterialManagerName, false, "Already have a surface material mapping for this surface tag.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
AZ_Error(TerrainDetailMaterialManagerName, false, "Already have a surface material mapping for this surface tag.");
|
||||
});
|
||||
|
||||
uint16_t detailMaterialId = CreateOrUpdateDetailMaterial(material);
|
||||
materialRegion.m_materialsForSurfaces.push_back({ surfaceTag, detailMaterialId });
|
||||
@@ -284,52 +339,71 @@ namespace Terrain
|
||||
void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingDestroyed(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag)
|
||||
{
|
||||
DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions);
|
||||
|
||||
for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces)
|
||||
|
||||
[[maybe_unused]] bool found = ForSurfaceTag(materialRegion, surfaceTag,
|
||||
[&](DetailMaterialSurface& surface)
|
||||
{
|
||||
if (surface.m_surfaceTag == surfaceTag)
|
||||
{
|
||||
CheckDetailMaterialForDeletion(surface.m_detailMaterialId);
|
||||
CheckDetailMaterialForDeletion(surface.m_detailMaterialId);
|
||||
|
||||
if (surface.m_surfaceTag != materialRegion.m_materialsForSurfaces.back().m_surfaceTag)
|
||||
{
|
||||
AZStd::swap(surface, materialRegion.m_materialsForSurfaces.back());
|
||||
}
|
||||
materialRegion.m_materialsForSurfaces.pop_back();
|
||||
m_dirtyDetailRegion.AddAabb(materialRegion.m_region);
|
||||
return;
|
||||
if (surface.m_surfaceTag != materialRegion.m_materialsForSurfaces.back().m_surfaceTag)
|
||||
{
|
||||
AZStd::swap(surface, materialRegion.m_materialsForSurfaces.back());
|
||||
}
|
||||
materialRegion.m_materialsForSurfaces.pop_back();
|
||||
m_dirtyDetailRegion.AddAabb(materialRegion.m_region);
|
||||
return;
|
||||
});
|
||||
|
||||
AZ_Error(TerrainDetailMaterialManagerName, found, "Could not find surface tag to destroy for OnTerrainSurfaceMaterialMappingDestroyed().");
|
||||
}
|
||||
|
||||
void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingMaterialChanged(
|
||||
AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material)
|
||||
{
|
||||
DetailMaterialListRegion* materialRegion = FindByEntityId(entityId, m_detailMaterialRegions);
|
||||
if (materialRegion == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "OnTerrainSurfaceMaterialMappingMaterialChanged() called for region that doesn't exist.");
|
||||
return;
|
||||
}
|
||||
AZ_Error(TerrainDetailMaterialManagerName, false, "Could not find surface tag to destroy for OnTerrainSurfaceMaterialMappingDestroyed().");
|
||||
|
||||
// Update existing entry or create a new material entry
|
||||
uint16_t materialId = CreateOrUpdateDetailMaterial(material);
|
||||
|
||||
[[maybe_unused]] bool found = ForSurfaceTag(*materialRegion, surfaceTag,
|
||||
[&](DetailMaterialSurface& surface)
|
||||
{
|
||||
if (surface.m_detailMaterialId != materialId)
|
||||
{
|
||||
// Updated material was a different asset than the old material, decrement ref count and
|
||||
// delete if no other surface tags are using it.
|
||||
++m_detailMaterials.GetData(materialId).refCount;
|
||||
CheckDetailMaterialForDeletion(surface.m_detailMaterialId);
|
||||
surface.m_detailMaterialId = materialId;
|
||||
}
|
||||
m_dirtyDetailRegion.AddAabb(materialRegion->m_region);
|
||||
});
|
||||
|
||||
AZ_Assert(found, "OnTerrainSurfaceMaterialMappingMaterialChanged() called for tag that doesn't exist.");
|
||||
}
|
||||
|
||||
void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingChanged(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material)
|
||||
void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingTagChanged(
|
||||
AZ::EntityId entityId, SurfaceData::SurfaceTag oldTag, SurfaceData::SurfaceTag newTag)
|
||||
{
|
||||
DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions);
|
||||
|
||||
bool found = false;
|
||||
uint16_t materialId = CreateOrUpdateDetailMaterial(material);
|
||||
for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces)
|
||||
DetailMaterialListRegion* materialRegion = FindByEntityId(entityId, m_detailMaterialRegions);
|
||||
if (materialRegion == nullptr)
|
||||
{
|
||||
if (surface.m_surfaceTag == surfaceTag)
|
||||
{
|
||||
found = true;
|
||||
if (surface.m_detailMaterialId != materialId)
|
||||
{
|
||||
++m_detailMaterials.GetData(materialId).refCount;
|
||||
CheckDetailMaterialForDeletion(surface.m_detailMaterialId);
|
||||
surface.m_detailMaterialId = materialId;
|
||||
}
|
||||
break;
|
||||
}
|
||||
AZ_Assert(false, "OnTerrainSurfaceMaterialMappingTagChanged() called for region that doesn't exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
|
||||
[[maybe_unused]] bool found = ForSurfaceTag(*materialRegion, oldTag,
|
||||
[&](DetailMaterialSurface& surface)
|
||||
{
|
||||
++m_detailMaterials.GetData(materialId).refCount;
|
||||
materialRegion.m_materialsForSurfaces.push_back({ surfaceTag, materialId });
|
||||
}
|
||||
m_dirtyDetailRegion.AddAabb(materialRegion.m_region);
|
||||
surface.m_surfaceTag = newTag;
|
||||
m_dirtyDetailRegion.AddAabb(materialRegion->m_region);
|
||||
});
|
||||
AZ_Assert(found, "OnTerrainSurfaceMaterialMappingTagChanged() called for tag that doesn't exist.");
|
||||
}
|
||||
|
||||
void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion)
|
||||
@@ -667,23 +741,38 @@ namespace Terrain
|
||||
bool isFirstMaterial = true;
|
||||
float firstWeight = 0.0f;
|
||||
AZ::Vector2 position(surfacePoint.m_position.GetX(), surfacePoint.m_position.GetY());
|
||||
const DetailMaterialListRegion* region = FindRegionForPosition(position);
|
||||
|
||||
if (region == nullptr)
|
||||
{
|
||||
pixels.at(index).m_material1 = m_passthroughMaterialId;
|
||||
++index;
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& surfaceTagWeight : surfacePoint.m_surfaceTags)
|
||||
{
|
||||
if (surfaceTagWeight.m_weight > 0.0f)
|
||||
{
|
||||
AZ::Crc32 surfaceType = surfaceTagWeight.m_surfaceType;
|
||||
uint16_t materialId = GetDetailMaterialForSurfaceTypeAndPosition(surfaceType, position);
|
||||
if (materialId != m_detailMaterials.NoFreeSlot && materialId < 255)
|
||||
uint16_t materialId = GetDetailMaterialForSurfaceType(*region, surfaceType);
|
||||
if (materialId < 255)
|
||||
{
|
||||
if (isFirstMaterial)
|
||||
{
|
||||
// First material is valid. Save its weight to calculate blend later
|
||||
pixels.at(index).m_material1 = aznumeric_cast<uint8_t>(materialId);
|
||||
firstWeight = surfaceTagWeight.m_weight;
|
||||
// m_blend only needs to be calculated is material 2 is found, otherwise the initial value of 0 is correct.
|
||||
isFirstMaterial = false;
|
||||
static constexpr float MaxValueBeforeRounding = 254.5f / 255.0f;
|
||||
if (firstWeight >= MaxValueBeforeRounding)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Second material is valid, weight is relative based on first material's weight.
|
||||
pixels.at(index).m_material2 = aznumeric_cast<uint8_t>(materialId);
|
||||
float totalWeight = firstWeight + surfaceTagWeight.m_weight;
|
||||
float blendWeight = 1.0f - (firstWeight / totalWeight);
|
||||
@@ -691,11 +780,37 @@ namespace Terrain
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue; // search for second material
|
||||
}
|
||||
else
|
||||
{
|
||||
break; // since the list is ordered, no other materials are in the list with positive weights.
|
||||
// No more valid materials in list since surfaceTagWeight is ordered.
|
||||
|
||||
uint8_t defaultMaterial = region->m_defaultDetailMaterialId == InvalidDetailMaterailId ? m_passthroughMaterialId :
|
||||
aznumeric_cast<uint8_t>(m_detailMaterials.GetData(region->m_defaultDetailMaterialId).m_detailMaterialBufferIndex);
|
||||
|
||||
if (isFirstMaterial)
|
||||
{
|
||||
// Only one material and it's the default material.
|
||||
pixels.at(index).m_material1 = defaultMaterial;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Second material is default, weight is exactly what the first material requested
|
||||
pixels.at(index).m_material2 = defaultMaterial;
|
||||
float blendWeight = 1.0f - AZStd::clamp<float>(firstWeight, 0.0f, 1.0f);
|
||||
pixels.at(index).m_blend = aznumeric_cast<uint8_t>(AZStd::round(blendWeight * 255.0f));
|
||||
}
|
||||
}
|
||||
|
||||
if (pixels.at(index).m_material1 == pixels.at(index).m_material2)
|
||||
{
|
||||
// If the materials are the same, then make the blend 100% on the first id so the shader
|
||||
// doesn't blend identical materials
|
||||
pixels.at(index).m_blend = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
++index;
|
||||
};
|
||||
@@ -723,23 +838,37 @@ namespace Terrain
|
||||
|
||||
m_detailTextureImage->UpdateImageContents(imageUpdateRequest);
|
||||
}
|
||||
|
||||
uint16_t TerrainDetailMaterialManager::GetDetailMaterialForSurfaceTypeAndPosition(AZ::Crc32 surfaceType, const AZ::Vector2& position)
|
||||
|
||||
uint16_t TerrainDetailMaterialManager::GetDetailMaterialForSurfaceType(const DetailMaterialListRegion& materialRegion, AZ::Crc32 surfaceType) const
|
||||
{
|
||||
for (const auto& materialSurface : materialRegion.m_materialsForSurfaces)
|
||||
{
|
||||
if (materialSurface.m_surfaceTag == surfaceType)
|
||||
{
|
||||
return m_detailMaterials.GetData(materialSurface.m_detailMaterialId).m_detailMaterialBufferIndex;
|
||||
}
|
||||
}
|
||||
return InvalidDetailMaterailId;
|
||||
}
|
||||
|
||||
auto TerrainDetailMaterialManager::FindRegionForPosition(const AZ::Vector2& position) const -> const DetailMaterialListRegion*
|
||||
{
|
||||
for (const auto& materialRegion : m_detailMaterialRegions.GetDataVector())
|
||||
{
|
||||
if (materialRegion.m_region.Contains(AZ::Vector3(position.GetX(), position.GetY(), 0.0f)))
|
||||
{
|
||||
for (const auto& materialSurface : materialRegion.m_materialsForSurfaces)
|
||||
{
|
||||
if (materialSurface.m_surfaceTag == surfaceType)
|
||||
{
|
||||
return m_detailMaterials.GetData(materialSurface.m_detailMaterialId).m_detailMaterialBufferIndex;
|
||||
}
|
||||
}
|
||||
return &materialRegion;
|
||||
}
|
||||
}
|
||||
return m_detailMaterials.NoFreeSlot;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TerrainDetailMaterialManager::InitializePassthroughDetailMaterial()
|
||||
{
|
||||
m_passthroughMaterialId = aznumeric_cast<uint8_t>(m_detailMaterialShaderData.Reserve());
|
||||
DetailMaterialShaderData& materialShaderData = m_detailMaterialShaderData.GetElement(m_passthroughMaterialId);
|
||||
// Material defaults to white (1.0, 1.0, 1.0), set the blend mode to multiply so it passes through to the macro material.
|
||||
materialShaderData.m_flags = DetailTextureFlags::BlendModeMultiply;
|
||||
}
|
||||
|
||||
auto TerrainDetailMaterialManager::FindByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector<DetailMaterialListRegion>& container)
|
||||
@@ -784,5 +913,5 @@ namespace Terrain
|
||||
}
|
||||
AZ_Assert(false, "Entity Id not found in container.")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Math/Aabb.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
#include <AzCore/std/function/function_template.h>
|
||||
|
||||
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
|
||||
|
||||
@@ -151,7 +152,11 @@ namespace Terrain
|
||||
AZ::EntityId m_entityId;
|
||||
AZ::Aabb m_region{AZ::Aabb::CreateNull()};
|
||||
AZStd::vector<DetailMaterialSurface> m_materialsForSurfaces;
|
||||
uint16_t m_defaultDetailMaterialId;
|
||||
};
|
||||
|
||||
using DetailMaterialContainer = AZ::Render::IndexedDataVector<DetailMaterialData>;
|
||||
static constexpr auto InvalidDetailMaterailId = DetailMaterialContainer::NoFreeSlot;
|
||||
|
||||
// System-level parameters
|
||||
static constexpr int32_t DetailTextureSize{ 1024 };
|
||||
@@ -162,9 +167,14 @@ namespace Terrain
|
||||
void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override;
|
||||
|
||||
// TerrainAreaMaterialNotificationBus overrides...
|
||||
void OnTerrainDefaultSurfaceMaterialCreated(AZ::EntityId entityId, AZ::Data::Instance<AZ::RPI::Material> material) override;
|
||||
void OnTerrainDefaultSurfaceMaterialDestroyed(AZ::EntityId entityId) override;
|
||||
void OnTerrainDefaultSurfaceMaterialChanged(AZ::EntityId entityId, AZ::Data::Instance<AZ::RPI::Material> newMaterial) override;
|
||||
void OnTerrainSurfaceMaterialMappingCreated(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) override;
|
||||
void OnTerrainSurfaceMaterialMappingDestroyed(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag) override;
|
||||
void OnTerrainSurfaceMaterialMappingChanged(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) override;
|
||||
void OnTerrainSurfaceMaterialMappingMaterialChanged(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) override;
|
||||
void OnTerrainSurfaceMaterialMappingTagChanged(
|
||||
AZ::EntityId entityId, SurfaceData::SurfaceTag oldSurfaceTag, SurfaceData::SurfaceTag newSurfaceTag) override;
|
||||
void OnTerrainSurfaceMaterialMappingRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) override;
|
||||
|
||||
//! Removes all images from all detail materials from the bindless image array
|
||||
@@ -186,22 +196,32 @@ namespace Terrain
|
||||
//! Updates the detail texture in a given area
|
||||
void UpdateDetailTexture(const AZ::Aabb& worldUpdateAabb, const Aabb2i& textureUpdateAabb);
|
||||
|
||||
//! Finds the detail material Id for a surface type and position
|
||||
uint16_t GetDetailMaterialForSurfaceTypeAndPosition(AZ::Crc32 surfaceType, const AZ::Vector2& position);
|
||||
//! Finds the detail material Id for a region and surface type
|
||||
uint16_t GetDetailMaterialForSurfaceType(const DetailMaterialListRegion& materialRegion, AZ::Crc32 surfaceType) const;
|
||||
|
||||
//! Finds a region for a position. Returns nullptr if none found.
|
||||
const DetailMaterialListRegion* FindRegionForPosition(const AZ::Vector2& position) const;
|
||||
|
||||
//! Initializes shader data for the default passthrough material which is used when no other detail material is found.
|
||||
void InitializePassthroughDetailMaterial();
|
||||
|
||||
using DefaultMaterialSurfaceCallback = AZStd::function<void(DetailMaterialSurface&)>;
|
||||
bool ForSurfaceTag(DetailMaterialListRegion& materialRegion,
|
||||
SurfaceData::SurfaceTag surfaceTag, DefaultMaterialSurfaceCallback callback);
|
||||
DetailMaterialListRegion* FindByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector<DetailMaterialListRegion>& container);
|
||||
DetailMaterialListRegion& FindOrCreateByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector<DetailMaterialListRegion>& container);
|
||||
void RemoveByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector<DetailMaterialListRegion>& container);
|
||||
|
||||
|
||||
AZStd::shared_ptr<AZ::Render::BindlessImageArrayHandler> m_bindlessImageHandler;
|
||||
|
||||
AZ::Data::Instance<AZ::RPI::AttachmentImage> m_detailTextureImage;
|
||||
|
||||
AZ::Render::IndexedDataVector<DetailMaterialData> m_detailMaterials;
|
||||
DetailMaterialContainer m_detailMaterials;
|
||||
AZ::Render::IndexedDataVector<DetailMaterialListRegion> m_detailMaterialRegions;
|
||||
AZ::Render::SparseVector<DetailMaterialShaderData> m_detailMaterialShaderData;
|
||||
AZ::Render::GpuBufferHandler m_detailMaterialDataBuffer;
|
||||
|
||||
uint8_t m_passthroughMaterialId = 0;
|
||||
|
||||
AZ::Aabb m_dirtyDetailRegion{ AZ::Aabb::CreateNull() };
|
||||
ClipmapBounds m_detailMaterialIdBounds;
|
||||
|
||||
@@ -212,6 +232,6 @@ namespace Terrain
|
||||
bool m_isInitialized{ false };
|
||||
bool m_detailMaterialBufferNeedsUpdate{ false };
|
||||
bool m_detailImageNeedsUpdate{ false };
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -177,6 +177,193 @@ bool TerrainSystem::InWorldBounds(float x, float y) const
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate positions to be queried based on the sampler type.
|
||||
void TerrainSystem::GenerateQueryPositions(const AZStd::span<AZ::Vector3>& inPositions,
|
||||
AZStd::vector<AZ::Vector3>& outPositions,
|
||||
Sampler sampler) const
|
||||
{
|
||||
const float minHeight = m_currentSettings.m_worldBounds.GetMin().GetZ();
|
||||
for (auto& position : inPositions)
|
||||
{
|
||||
switch(sampler)
|
||||
{
|
||||
case AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR:
|
||||
{
|
||||
AZ::Vector2 normalizedDelta;
|
||||
AZ::Vector2 pos0;
|
||||
ClampPosition(position.GetX(), position.GetY(), pos0, normalizedDelta);
|
||||
const AZ::Vector2 pos1(pos0.GetX() + m_currentSettings.m_heightQueryResolution,
|
||||
pos0.GetY() + m_currentSettings.m_heightQueryResolution);
|
||||
outPositions.emplace_back(AZ::Vector3(pos0.GetX(), pos0.GetY(), minHeight));
|
||||
outPositions.emplace_back(AZ::Vector3(pos1.GetX(), pos0.GetY(), minHeight));
|
||||
outPositions.emplace_back(AZ::Vector3(pos0.GetX(), pos1.GetY(), minHeight));
|
||||
outPositions.emplace_back(AZ::Vector3(pos1.GetX(), pos1.GetY(), minHeight));
|
||||
}
|
||||
break;
|
||||
case AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP:
|
||||
{
|
||||
AZ::Vector2 normalizedDelta;
|
||||
AZ::Vector2 clampedPosition;
|
||||
ClampPosition(position.GetX(), position.GetY(), clampedPosition, normalizedDelta);
|
||||
outPositions.emplace_back(AZ::Vector3(clampedPosition.GetX(), clampedPosition.GetY(), minHeight));
|
||||
}
|
||||
break;
|
||||
case AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT:
|
||||
[[fallthrough]];
|
||||
default:
|
||||
outPositions.emplace_back(AZ::Vector3(position.GetX(), position.GetY(), minHeight));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::vector<AZ::Vector3> TerrainSystem::GenerateInputPositionsFromRegion(
|
||||
const AZ::Aabb& inRegion,
|
||||
const AZ::Vector2& stepSize) const
|
||||
{
|
||||
AZStd::vector<AZ::Vector3> inPositions;
|
||||
const auto [numSamplesX, numSamplesY] = GetNumSamplesFromRegion(inRegion, stepSize);
|
||||
inPositions.reserve(numSamplesX * numSamplesY);
|
||||
|
||||
for (size_t y = 0; y < numSamplesY; y++)
|
||||
{
|
||||
float fy = aznumeric_cast<float>(inRegion.GetMin().GetY() + (y * stepSize.GetY()));
|
||||
for (size_t x = 0; x < numSamplesX; x++)
|
||||
{
|
||||
float fx = aznumeric_cast<float>(inRegion.GetMin().GetX() + (x * stepSize.GetX()));
|
||||
inPositions.emplace_back(AZ::Vector3(fx, fy, 0.0f));
|
||||
}
|
||||
}
|
||||
|
||||
return inPositions;
|
||||
}
|
||||
|
||||
void TerrainSystem::MakeBulkQueries(
|
||||
const AZStd::span<AZ::Vector3> inPositions,
|
||||
AZStd::span<AZ::Vector3> outPositions,
|
||||
AZStd::span<bool> outTerrainExists,
|
||||
AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeights,
|
||||
BulkQueriesCallback queryCallback) const
|
||||
{
|
||||
AZ::Aabb bounds;
|
||||
AZ::EntityId prevAreaId = FindBestAreaEntityAtPosition(inPositions[0].GetX(), inPositions[0].GetY(), bounds);
|
||||
|
||||
// We use a sliding window here and update the window end for each
|
||||
// position that falls in the same area as the previous positions. This consumes lesser memory
|
||||
// than sorting the points into separate lists and handling putting them back together.
|
||||
// This may be sub optimal if the points are randomly distributed in the list as opposed
|
||||
// to points in the same area id being close to each other.
|
||||
size_t windowStart = 0;
|
||||
size_t windowEnd = 0;
|
||||
const size_t numPositions = inPositions.size();
|
||||
for(int i = 1; i < numPositions; i++)
|
||||
{
|
||||
AZ::EntityId areaId = FindBestAreaEntityAtPosition(inPositions[i].GetX(), inPositions[i].GetY(), bounds);
|
||||
bool queryHeights = false;
|
||||
if (areaId == prevAreaId)
|
||||
{
|
||||
// Update window end to current position.
|
||||
// If it's the last position, submit the query.
|
||||
windowEnd = i;
|
||||
if (windowEnd == numPositions - 1)
|
||||
{
|
||||
queryHeights = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
queryHeights = true;
|
||||
}
|
||||
|
||||
if (queryHeights)
|
||||
{
|
||||
// If the area id is a default entity id, it usually means the
|
||||
// position is outside world bounds.
|
||||
if (prevAreaId != AZ::EntityId())
|
||||
{
|
||||
size_t spanLength = (windowEnd - windowStart) + 1;
|
||||
queryCallback(AZStd::span<AZ::Vector3>(inPositions.begin() + windowStart, spanLength),
|
||||
AZStd::span<AZ::Vector3>(outPositions.begin() + windowStart, spanLength),
|
||||
AZStd::span<bool>(outTerrainExists.begin() + windowStart, spanLength),
|
||||
AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList>(outSurfaceWeights.begin() + windowStart, spanLength),
|
||||
prevAreaId);
|
||||
}
|
||||
|
||||
// Reset the window to start at the current position. Set the new area
|
||||
// id on which to run the next query.
|
||||
windowStart = windowEnd = i;
|
||||
prevAreaId = areaId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainSystem::GetHeightsSynchronous(const AZStd::span<AZ::Vector3>& inPositions, Sampler sampler,
|
||||
AZStd::span<float> heights, AZStd::span<bool> terrainExists) const
|
||||
{
|
||||
AZStd::shared_lock<AZStd::shared_mutex> lock(m_areaMutex);
|
||||
|
||||
AZStd::vector<AZ::Vector3> outPositions;
|
||||
AZStd::vector<bool> outTerrainExists;
|
||||
|
||||
// outPositions holds the iterators to results of the bulk queries.
|
||||
// In the case of the bilinear sampler, we'll be making 4 queries per
|
||||
// input position.
|
||||
size_t indexStepSize = (sampler == AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR) ? 4 : 1;
|
||||
outPositions.reserve(inPositions.size() * indexStepSize);
|
||||
outTerrainExists.resize(inPositions.size() * indexStepSize);
|
||||
|
||||
GenerateQueryPositions(inPositions, outPositions, sampler);
|
||||
|
||||
auto callback = []([[maybe_unused]] const AZStd::span<AZ::Vector3> inPositions,
|
||||
AZStd::span<AZ::Vector3> outPositions,
|
||||
AZStd::span<bool> outTerrainExists,
|
||||
[[maybe_unused]] AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeights,
|
||||
AZ::EntityId areaId)
|
||||
{
|
||||
AZ_Assert((inPositions.size() == outPositions.size() && inPositions.size() == outTerrainExists.size()),
|
||||
"The sizes of the terrain exists list and in/out positions list should match.");
|
||||
Terrain::TerrainAreaHeightRequestBus::Event(areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetHeights,
|
||||
outPositions, outTerrainExists);
|
||||
};
|
||||
|
||||
// This will be unused for heights. It's fine if it's empty.
|
||||
AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeights;
|
||||
MakeBulkQueries(outPositions, outPositions, outTerrainExists, outSurfaceWeights, callback);
|
||||
|
||||
// Compute/store the final result
|
||||
for (size_t i = 0, iteratorIndex = 0; i < inPositions.size(); i++, iteratorIndex += indexStepSize)
|
||||
{
|
||||
switch(sampler)
|
||||
{
|
||||
case AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR:
|
||||
{
|
||||
// We now need to compute the final height after all the bulk queries are done.
|
||||
AZ::Vector2 normalizedDelta;
|
||||
AZ::Vector2 clampedPosition;
|
||||
ClampPosition(inPositions[i].GetX(), inPositions[i].GetY(), clampedPosition, normalizedDelta);
|
||||
const float heightX0Y0 = outPositions[iteratorIndex].GetZ();
|
||||
const float heightX1Y0 = outPositions[iteratorIndex + 1].GetZ();
|
||||
const float heightX0Y1 = outPositions[iteratorIndex + 2].GetZ();
|
||||
const float heightX1Y1 = outPositions[iteratorIndex + 3].GetZ();
|
||||
const float heightXY0 = AZ::Lerp(heightX0Y0, heightX1Y0, normalizedDelta.GetX());
|
||||
const float heightXY1 = AZ::Lerp(heightX0Y1, heightX1Y1, normalizedDelta.GetX());
|
||||
heights[i] = AZ::Lerp(heightXY0, heightXY1, normalizedDelta.GetY());
|
||||
terrainExists[i] = outTerrainExists[iteratorIndex];
|
||||
}
|
||||
break;
|
||||
case AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP:
|
||||
[[fallthrough]];
|
||||
case AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT:
|
||||
[[fallthrough]];
|
||||
default:
|
||||
// For clamp and exact, we just need to store the results of the bulk query.
|
||||
heights[i] = outPositions[iteratorIndex].GetZ();
|
||||
terrainExists[i] = outTerrainExists[iteratorIndex];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float TerrainSystem::GetHeightSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const
|
||||
{
|
||||
bool terrainExists = false;
|
||||
@@ -315,6 +502,39 @@ bool TerrainSystem::GetIsHoleFromFloats(float x, float y, Sampler sampler) const
|
||||
return !terrainExists;
|
||||
}
|
||||
|
||||
void TerrainSystem::GetNormalsSynchronous(const AZStd::span<AZ::Vector3>& inPositions, Sampler sampler,
|
||||
AZStd::span<AZ::Vector3> normals, AZStd::span<bool> terrainExists) const
|
||||
{
|
||||
AZStd::vector<AZ::Vector3> directionVectors;
|
||||
directionVectors.reserve(inPositions.size() * 4);
|
||||
const AZ::Vector2 range(m_currentSettings.m_heightQueryResolution / 2.0f, m_currentSettings.m_heightQueryResolution / 2.0f);
|
||||
size_t indexStepSize = 4;
|
||||
for (auto& position : inPositions)
|
||||
{
|
||||
directionVectors.emplace_back(position.GetX(), position.GetY() - range.GetY(), 0.0f);
|
||||
directionVectors.emplace_back(position.GetX() - range.GetX(), position.GetY(), 0.0f);
|
||||
directionVectors.emplace_back(position.GetX() + range.GetX(), position.GetY(), 0.0f);
|
||||
directionVectors.emplace_back(position.GetX(), position.GetY() + range.GetY(), 0.0f);
|
||||
}
|
||||
|
||||
AZStd::vector<float> heights(directionVectors.size());
|
||||
AZStd::vector<bool> exists(directionVectors.size());
|
||||
GetHeightsSynchronous(directionVectors, sampler, heights, exists);
|
||||
|
||||
for (size_t i = 0, iteratorIndex = 0; i < inPositions.size(); i++, iteratorIndex += indexStepSize)
|
||||
{
|
||||
directionVectors[iteratorIndex].SetZ(heights[iteratorIndex]);
|
||||
directionVectors[iteratorIndex + 1].SetZ(heights[iteratorIndex + 1]);
|
||||
directionVectors[iteratorIndex + 2].SetZ(heights[iteratorIndex + 2]);
|
||||
directionVectors[iteratorIndex + 3].SetZ(heights[iteratorIndex + 3]);
|
||||
|
||||
normals[i] = (directionVectors[iteratorIndex + 2] - directionVectors[iteratorIndex + 1]).
|
||||
Cross(directionVectors[iteratorIndex + 3] - directionVectors[iteratorIndex]).GetNormalized();
|
||||
|
||||
terrainExists[i] = exists[iteratorIndex];
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Vector3 TerrainSystem::GetNormalSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const
|
||||
{
|
||||
AZStd::shared_lock<AZStd::shared_mutex> lock(m_areaMutex);
|
||||
@@ -471,6 +691,35 @@ AZ::EntityId TerrainSystem::FindBestAreaEntityAtPosition(float x, float y, AZ::A
|
||||
return AZ::EntityId();
|
||||
}
|
||||
|
||||
void TerrainSystem::GetOrderedSurfaceWeightsFromList(
|
||||
const AZStd::span<AZ::Vector3>& inPositions,
|
||||
[[maybe_unused]] Sampler sampler,
|
||||
AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeightsList,
|
||||
AZStd::span<bool> terrainExists) const
|
||||
{
|
||||
if (terrainExists.size() == outSurfaceWeightsList.size())
|
||||
{
|
||||
AZStd::vector<float> heights(inPositions.size());
|
||||
GetHeightsSynchronous(inPositions, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, heights, terrainExists);
|
||||
}
|
||||
|
||||
auto callback = [](const AZStd::span<AZ::Vector3> inPositions,
|
||||
[[maybe_unused]] AZStd::span<AZ::Vector3> outPositions,
|
||||
[[maybe_unused]] AZStd::span<bool> outTerrainExists,
|
||||
AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeights,
|
||||
AZ::EntityId areaId)
|
||||
{
|
||||
AZ_Assert(inPositions.size() == outSurfaceWeights.size(),
|
||||
"The sizes of the surface weights list and in/out positions list should match.");
|
||||
Terrain::TerrainAreaSurfaceRequestBus::Event(areaId, &Terrain::TerrainAreaSurfaceRequestBus::Events::GetSurfaceWeightsFromList,
|
||||
inPositions, outSurfaceWeights);
|
||||
};
|
||||
|
||||
// This will be unused for surface weights. It's fine if it's empty.
|
||||
AZStd::vector<AZ::Vector3> outPositions;
|
||||
MakeBulkQueries(inPositions, outPositions, terrainExists, outSurfaceWeightsList, callback);
|
||||
}
|
||||
|
||||
void TerrainSystem::GetOrderedSurfaceWeights(
|
||||
const float x,
|
||||
const float y,
|
||||
@@ -551,13 +800,17 @@ void TerrainSystem::ProcessHeightsFromList(
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::vector<bool> terrainExists(inPositions.size());
|
||||
AZStd::vector<float> heights(inPositions.size());
|
||||
|
||||
GetHeightsSynchronous(inPositions, sampleFilter, heights, terrainExists);
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (const auto& position : inPositions)
|
||||
for (size_t i = 0; i < inPositions.size(); i++)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
surfacePoint.m_position = position;
|
||||
surfacePoint.m_position.SetZ(GetHeight(position, sampleFilter, &terrainExists));
|
||||
perPositionCallback(surfacePoint, terrainExists);
|
||||
surfacePoint.m_position = inPositions[i];
|
||||
surfacePoint.m_position.SetZ(heights[i]);
|
||||
perPositionCallback(surfacePoint, terrainExists[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -571,13 +824,17 @@ void TerrainSystem::ProcessNormalsFromList(
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::vector<bool> terrainExists(inPositions.size());
|
||||
AZStd::vector<AZ::Vector3> normals(inPositions.size());
|
||||
|
||||
GetNormalsSynchronous(inPositions, sampleFilter, normals, terrainExists);
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (const auto& position : inPositions)
|
||||
for (size_t i = 0; i < inPositions.size(); i++)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
surfacePoint.m_position = position;
|
||||
surfacePoint.m_normal = GetNormal(position, sampleFilter, &terrainExists);
|
||||
perPositionCallback(surfacePoint, terrainExists);
|
||||
surfacePoint.m_position = inPositions[i];
|
||||
surfacePoint.m_normal = AZStd::move(normals[i]);
|
||||
perPositionCallback(surfacePoint, terrainExists[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -591,13 +848,17 @@ void TerrainSystem::ProcessSurfaceWeightsFromList(
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeightsList(inPositions.size());
|
||||
AZStd::vector<bool> terrainExists(inPositions.size());
|
||||
|
||||
GetOrderedSurfaceWeightsFromList(inPositions, sampleFilter, outSurfaceWeightsList, terrainExists);
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (const auto& position : inPositions)
|
||||
for (size_t i = 0; i < inPositions.size(); i++)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
surfacePoint.m_position = position;
|
||||
GetSurfaceWeights(position, surfacePoint.m_surfaceTags, sampleFilter, &terrainExists);
|
||||
perPositionCallback(surfacePoint, terrainExists);
|
||||
surfacePoint.m_position = inPositions[i];
|
||||
surfacePoint.m_surfaceTags = AZStd::move(outSurfaceWeightsList[i]);
|
||||
perPositionCallback(surfacePoint, terrainExists[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,13 +872,26 @@ void TerrainSystem::ProcessSurfacePointsFromList(
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::vector<float> heights(inPositions.size());
|
||||
AZStd::vector<AZ::Vector3> normals(inPositions.size());
|
||||
AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeightsList(inPositions.size());
|
||||
AZStd::vector<bool> terrainExists(inPositions.size());
|
||||
|
||||
GetHeightsSynchronous(inPositions, sampleFilter, heights, terrainExists);
|
||||
GetNormalsSynchronous(inPositions, sampleFilter, normals, terrainExists);
|
||||
|
||||
// We can skip the unnecessary call to GetHeights since we already
|
||||
// got the terrain exists flags in the earlier call to GetHeights
|
||||
AZStd::vector<bool> terrainExistsEmpty;
|
||||
GetOrderedSurfaceWeightsFromList(inPositions, sampleFilter, outSurfaceWeightsList, terrainExistsEmpty);
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (const auto& position : inPositions)
|
||||
for (size_t i = 0; i < inPositions.size(); i++)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
surfacePoint.m_position = position;
|
||||
GetSurfacePoint(position, surfacePoint, sampleFilter, &terrainExists);
|
||||
perPositionCallback(surfacePoint, terrainExists);
|
||||
surfacePoint.m_position.Set(inPositions[i].GetX(), inPositions[i].GetY(), heights[i]);
|
||||
surfacePoint.m_normal = AZStd::move(normals[i]);
|
||||
surfacePoint.m_surfaceTags = AZStd::move(outSurfaceWeightsList[i]);
|
||||
perPositionCallback(surfacePoint, terrainExists[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -723,20 +997,23 @@ void TerrainSystem::ProcessHeightsFromRegion(
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t numSamplesX = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetX() / stepSize.GetX()));
|
||||
const size_t numSamplesY = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetY() / stepSize.GetY()));
|
||||
const auto [numSamplesX, numSamplesY] = GetNumSamplesFromRegion(inRegion, stepSize);
|
||||
|
||||
AZStd::vector<AZ::Vector3> inPositions = GenerateInputPositionsFromRegion(inRegion, stepSize);
|
||||
|
||||
AZStd::vector<bool> terrainExists(inPositions.size());
|
||||
AZStd::vector<float> heights(inPositions.size());
|
||||
|
||||
GetHeightsSynchronous(inPositions, sampleFilter, heights, terrainExists);
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (size_t y = 0; y < numSamplesY; y++)
|
||||
for (size_t y = 0, i = 0; y < numSamplesY; y++)
|
||||
{
|
||||
float fy = aznumeric_cast<float>(inRegion.GetMin().GetY() + (y * stepSize.GetY()));
|
||||
for (size_t x = 0; x < numSamplesX; x++)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
float fx = aznumeric_cast<float>(inRegion.GetMin().GetX() + (x * stepSize.GetX()));
|
||||
surfacePoint.m_position.Set(fx, fy, 0.0f);
|
||||
surfacePoint.m_position.SetZ(GetHeight(surfacePoint.m_position, sampleFilter, &terrainExists));
|
||||
perPositionCallback(x, y, surfacePoint, terrainExists);
|
||||
surfacePoint.m_position.Set(inPositions[i].GetX(), inPositions[i].GetY(), heights[i]);
|
||||
perPositionCallback(x, y, surfacePoint, terrainExists[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -753,20 +1030,24 @@ void TerrainSystem::ProcessNormalsFromRegion(
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t numSamplesX = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetX() / stepSize.GetX()));
|
||||
const size_t numSamplesY = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetY() / stepSize.GetY()));
|
||||
const auto [numSamplesX, numSamplesY] = GetNumSamplesFromRegion(inRegion, stepSize);
|
||||
|
||||
AZStd::vector<AZ::Vector3> inPositions = GenerateInputPositionsFromRegion(inRegion, stepSize);
|
||||
|
||||
AZStd::vector<bool> terrainExists(inPositions.size());
|
||||
AZStd::vector<AZ::Vector3> normals(inPositions.size());
|
||||
|
||||
GetNormalsSynchronous(inPositions, sampleFilter, normals, terrainExists);
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (size_t y = 0; y < numSamplesY; y++)
|
||||
for (size_t y = 0, i = 0; y < numSamplesY; y++)
|
||||
{
|
||||
float fy = aznumeric_cast<float>(inRegion.GetMin().GetY() + (y * stepSize.GetY()));
|
||||
for (size_t x = 0; x < numSamplesX; x++)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
float fx = aznumeric_cast<float>(inRegion.GetMin().GetX() + (x * stepSize.GetX()));
|
||||
surfacePoint.m_position.Set(fx, fy, 0.0f);
|
||||
surfacePoint.m_normal = GetNormal(surfacePoint.m_position, sampleFilter, &terrainExists);
|
||||
perPositionCallback(x, y, surfacePoint, terrainExists);
|
||||
surfacePoint.m_position.Set(inPositions[i].GetX(), inPositions[i].GetY(), 0.0f);
|
||||
surfacePoint.m_normal = AZStd::move(normals[i]);
|
||||
perPositionCallback(x, y, surfacePoint, terrainExists[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -783,20 +1064,24 @@ void TerrainSystem::ProcessSurfaceWeightsFromRegion(
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t numSamplesX = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetX() / stepSize.GetX()));
|
||||
const size_t numSamplesY = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetY() / stepSize.GetY()));
|
||||
const auto [numSamplesX, numSamplesY] = GetNumSamplesFromRegion(inRegion, stepSize);
|
||||
|
||||
AZStd::vector<AZ::Vector3> inPositions = GenerateInputPositionsFromRegion(inRegion, stepSize);
|
||||
|
||||
AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeightsList(inPositions.size());
|
||||
AZStd::vector<bool> terrainExists(inPositions.size());
|
||||
|
||||
GetOrderedSurfaceWeightsFromList(inPositions, sampleFilter, outSurfaceWeightsList, terrainExists);
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (size_t y = 0; y < numSamplesY; y++)
|
||||
for (size_t y = 0, i = 0; y < numSamplesY; y++)
|
||||
{
|
||||
float fy = aznumeric_cast<float>(inRegion.GetMin().GetY() + (y * stepSize.GetY()));
|
||||
for (size_t x = 0; x < numSamplesX; x++)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
float fx = aznumeric_cast<float>(inRegion.GetMin().GetX() + (x * stepSize.GetX()));
|
||||
surfacePoint.m_position.Set(fx, fy, 0.0f);
|
||||
GetSurfaceWeights(surfacePoint.m_position, surfacePoint.m_surfaceTags, sampleFilter, &terrainExists);
|
||||
perPositionCallback(x, y, surfacePoint, terrainExists);
|
||||
surfacePoint.m_position.Set(inPositions[i].GetX(), inPositions[i].GetY(), 0.0f);
|
||||
surfacePoint.m_surfaceTags = AZStd::move(outSurfaceWeightsList[i]);
|
||||
perPositionCallback(x, y, surfacePoint, terrainExists[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -813,20 +1098,33 @@ void TerrainSystem::ProcessSurfacePointsFromRegion(
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t numSamplesX = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetX() / stepSize.GetX()));
|
||||
const size_t numSamplesY = aznumeric_cast<size_t>(ceil(inRegion.GetExtents().GetY() / stepSize.GetY()));
|
||||
const auto [numSamplesX, numSamplesY] = GetNumSamplesFromRegion(inRegion, stepSize);
|
||||
|
||||
AZStd::vector<AZ::Vector3> inPositions = GenerateInputPositionsFromRegion(inRegion, stepSize);
|
||||
|
||||
AZStd::vector<float> heights(inPositions.size());
|
||||
AZStd::vector<AZ::Vector3> normals(inPositions.size());
|
||||
AZStd::vector<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeightsList(inPositions.size());
|
||||
AZStd::vector<bool> terrainExists(inPositions.size());
|
||||
|
||||
GetHeightsSynchronous(inPositions, sampleFilter, heights, terrainExists);
|
||||
GetNormalsSynchronous(inPositions, sampleFilter, normals, terrainExists);
|
||||
|
||||
// We can skip the unnecessary call to GetHeights since we already
|
||||
// got the terrain exists flags in the earlier call to GetHeights
|
||||
AZStd::vector<bool> terrainExistsEmpty;
|
||||
GetOrderedSurfaceWeightsFromList(inPositions, sampleFilter, outSurfaceWeightsList, terrainExistsEmpty);
|
||||
|
||||
AzFramework::SurfaceData::SurfacePoint surfacePoint;
|
||||
for (size_t y = 0; y < numSamplesY; y++)
|
||||
for (size_t y = 0, i = 0; y < numSamplesY; y++)
|
||||
{
|
||||
float fy = aznumeric_cast<float>(inRegion.GetMin().GetY() + (y * stepSize.GetY()));
|
||||
for (size_t x = 0; x < numSamplesX; x++)
|
||||
{
|
||||
bool terrainExists = false;
|
||||
float fx = aznumeric_cast<float>(inRegion.GetMin().GetX() + (x * stepSize.GetX()));
|
||||
surfacePoint.m_position.Set(fx, fy, 0.0f);
|
||||
GetSurfacePoint(surfacePoint.m_position, surfacePoint, sampleFilter, &terrainExists);
|
||||
perPositionCallback(x, y, surfacePoint, terrainExists);
|
||||
surfacePoint.m_position.Set(inPositions[i].GetX(), inPositions[i].GetY(), heights[i]);
|
||||
surfacePoint.m_normal = AZStd::move(normals[i]);
|
||||
surfacePoint.m_surfaceTags = AZStd::move(outSurfaceWeightsList[i]);
|
||||
perPositionCallback(x, y, surfacePoint, terrainExists[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,6 +207,38 @@ namespace Terrain
|
||||
float GetTerrainAreaHeight(float x, float y, bool& terrainExists) const;
|
||||
AZ::Vector3 GetNormalSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const;
|
||||
|
||||
typedef AZStd::function<void(
|
||||
const AZStd::span<AZ::Vector3> inPositions,
|
||||
AZStd::span<AZ::Vector3> outPositions,
|
||||
AZStd::span<bool> outTerrainExists,
|
||||
AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeights,
|
||||
AZ::EntityId areaId)> BulkQueriesCallback;
|
||||
|
||||
void GetHeightsSynchronous(
|
||||
const AZStd::span<AZ::Vector3>& inPositions,
|
||||
Sampler sampler, AZStd::span<float> heights,
|
||||
AZStd::span<bool> terrainExists) const;
|
||||
void GetNormalsSynchronous(
|
||||
const AZStd::span<AZ::Vector3>& inPositions,
|
||||
Sampler sampler, AZStd::span<AZ::Vector3> normals,
|
||||
AZStd::span<bool> terrainExists) const;
|
||||
void GetOrderedSurfaceWeightsFromList(
|
||||
const AZStd::span<AZ::Vector3>& inPositions, Sampler sampler,
|
||||
AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeightsList,
|
||||
AZStd::span<bool> terrainExists) const;
|
||||
void MakeBulkQueries(
|
||||
const AZStd::span<AZ::Vector3> inPositions,
|
||||
AZStd::span<AZ::Vector3> outPositions,
|
||||
AZStd::span<bool> outTerrainExists,
|
||||
AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWieghts,
|
||||
BulkQueriesCallback queryCallback) const;
|
||||
void GenerateQueryPositions(const AZStd::span<AZ::Vector3>& inPositions,
|
||||
AZStd::vector<AZ::Vector3>& outPositions,
|
||||
Sampler sampler) const;
|
||||
AZStd::vector<AZ::Vector3> GenerateInputPositionsFromRegion(
|
||||
const AZ::Aabb& inRegion,
|
||||
const AZ::Vector2& stepSize) const;
|
||||
|
||||
// AZ::TickBus::Handler overrides ...
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
|
||||
|
||||
@@ -158,6 +158,15 @@ namespace UnitTest
|
||||
// Let the test function modify these values based on the needs of the specific test.
|
||||
mockHeights(outPosition, terrainExists);
|
||||
});
|
||||
ON_CALL(*m_terrainAreaHeightRequests, GetHeights)
|
||||
.WillByDefault(
|
||||
[mockHeights](AZStd::span<AZ::Vector3> inOutPositionList, AZStd::span<bool> terrainExistsList)
|
||||
{
|
||||
for (int i = 0; i < inOutPositionList.size(); i++)
|
||||
{
|
||||
mockHeights(inOutPositionList[i], terrainExistsList[i]);
|
||||
}
|
||||
});
|
||||
|
||||
ActivateEntity(entity.get());
|
||||
return entity;
|
||||
@@ -184,9 +193,9 @@ namespace UnitTest
|
||||
tagWeight3.m_weight = 0.3f;
|
||||
expectedTags.push_back(tagWeight3);
|
||||
|
||||
m_terrainAreaSurfaceRequests = AZStd::make_unique<NiceMock<UnitTest::MockTerrainAreaSurfaceRequestBus>>(entity->GetId());
|
||||
ON_CALL(*m_terrainAreaSurfaceRequests, GetSurfaceWeights).WillByDefault(
|
||||
[tagWeight1, tagWeight2, tagWeight3](const AZ::Vector3& position, AzFramework::SurfaceData::SurfaceTagWeightList& surfaceWeights)
|
||||
auto mockGetSurfaceWeights = [tagWeight1, tagWeight2, tagWeight3](
|
||||
const AZ::Vector3& position,
|
||||
AzFramework::SurfaceData::SurfaceTagWeightList& surfaceWeights)
|
||||
{
|
||||
surfaceWeights.clear();
|
||||
float absYPos = fabsf(position.GetY());
|
||||
@@ -202,6 +211,19 @@ namespace UnitTest
|
||||
{
|
||||
surfaceWeights.push_back(tagWeight3);
|
||||
}
|
||||
};
|
||||
|
||||
m_terrainAreaSurfaceRequests = AZStd::make_unique<NiceMock<UnitTest::MockTerrainAreaSurfaceRequestBus>>(entity->GetId());
|
||||
ON_CALL(*m_terrainAreaSurfaceRequests, GetSurfaceWeights).WillByDefault(mockGetSurfaceWeights);
|
||||
ON_CALL(*m_terrainAreaSurfaceRequests, GetSurfaceWeightsFromList).WillByDefault(
|
||||
[mockGetSurfaceWeights](
|
||||
AZStd::span<const AZ::Vector3> inPositionList,
|
||||
AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeightsList)
|
||||
{
|
||||
for (size_t i = 0; i < inPositionList.size(); i++)
|
||||
{
|
||||
mockGetSurfaceWeights(inPositionList[i], outSurfaceWeightsList[i]);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ set(FILES
|
||||
Source/EditorComponents/EditorTerrainSystemComponent.h
|
||||
Source/EditorTerrainModule.cpp
|
||||
Source/EditorTerrainModule.h
|
||||
Source/EditorSurfaceTagListProvider.h
|
||||
Source/EditorSurfaceTagListProvider.cpp
|
||||
Source/TerrainModule.cpp
|
||||
Source/TerrainModule.h
|
||||
Source/TerrainRenderer/EditorComponents/EditorTerrainSurfaceMaterialsListComponent.cpp
|
||||
|
||||
@@ -175,13 +175,20 @@ class AbstractResourceLocator(object):
|
||||
return os.path.join(self.build_directory(), 'AssetProcessor')
|
||||
|
||||
def asset_processor_batch(self):
|
||||
""""
|
||||
"""
|
||||
Return path for the AssetProcessorBatch compatible with this build platform and configuration
|
||||
ex. engine_root/dev/mac/bin/profile/AssetProcessorBatch
|
||||
:return: path to AssetProcessorBatch
|
||||
"""
|
||||
return os.path.join(self.build_directory(), 'AssetProcessorBatch')
|
||||
|
||||
def ap_job_logs(self):
|
||||
"""
|
||||
Return path to the Asset Processor JobLogs directory.
|
||||
:return: path to <project>/user/log/JobLogs
|
||||
"""
|
||||
return os.path.join(self.project_log(), 'JobLogs')
|
||||
|
||||
def editor(self):
|
||||
"""
|
||||
Return path to the editor executable compatible with the current build.
|
||||
|
||||
@@ -851,11 +851,11 @@ class EditorTestSuite():
|
||||
workspace.artifact_manager.save_artifact(os.path.join(editor_utils.retrieve_log_path(run_id, workspace), log_name),
|
||||
f'({run_id}){log_name}')
|
||||
if return_code == 0:
|
||||
# No need to scrap the output, as all the tests have passed
|
||||
# No need to scrape the output, as all the tests have passed
|
||||
for test_spec in test_spec_list:
|
||||
results[test_spec.__name__] = Result.Pass.create(test_spec, output, editor_log_content)
|
||||
else:
|
||||
# Scrap the output to attempt to find out which tests failed.
|
||||
# Scrape the output to attempt to find out which tests failed.
|
||||
# This function should always populate the result list, if it didn't find it, it will have "Unknown" type of result
|
||||
results = self._get_results_using_output(test_spec_list, output, editor_log_content)
|
||||
assert len(results) == len(test_spec_list), "bug in _get_results_using_output(), the number of results don't match the tests ran"
|
||||
@@ -940,6 +940,9 @@ class EditorTestSuite():
|
||||
editor_test_data.results.update(results)
|
||||
test_name, test_result = next(iter(results.items()))
|
||||
self._report_result(test_name, test_result)
|
||||
# If test did not pass, save assets with errors and warnings
|
||||
if not isinstance(test_result, Result.Pass):
|
||||
editor_utils.save_failed_asset_joblogs(workspace)
|
||||
|
||||
def _run_batched_tests(self, request: Request, workspace: AbstractWorkspace, editor: Editor, editor_test_data: TestData,
|
||||
test_spec_list: list[EditorSharedTest], extra_cmdline_args: list[str] = []) -> None:
|
||||
@@ -961,6 +964,11 @@ class EditorTestSuite():
|
||||
extra_cmdline_args)
|
||||
assert results is not None
|
||||
editor_test_data.results.update(results)
|
||||
# If at least one test did not pass, save assets with errors and warnings
|
||||
for result in results:
|
||||
if not isinstance(result, Result.Pass):
|
||||
editor_utils.save_failed_asset_joblogs(workspace)
|
||||
return
|
||||
|
||||
def _run_parallel_tests(self, request: Request, workspace: AbstractWorkspace, editor: Editor, editor_test_data: TestData,
|
||||
test_spec_list: list[EditorSharedTest], extra_cmdline_args: list[str] = []) -> None:
|
||||
@@ -1007,8 +1015,14 @@ class EditorTestSuite():
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
save_asset_logs = False
|
||||
for result in results_per_thread:
|
||||
editor_test_data.results.update(result)
|
||||
if not isinstance(result, Result.Pass):
|
||||
save_asset_logs = True
|
||||
# If at least one test did not pass, save assets with errors and warnings
|
||||
if save_asset_logs:
|
||||
editor_utils.save_failed_asset_joblogs(workspace)
|
||||
|
||||
def _run_parallel_batched_tests(self, request: Request, workspace: AbstractWorkspace, editor: Editor, editor_test_data: TestData,
|
||||
test_spec_list: list[EditorSharedTest], extra_cmdline_args: list[str] = []) -> None:
|
||||
@@ -1056,8 +1070,14 @@ class EditorTestSuite():
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
save_asset_logs = False
|
||||
for result in results_per_thread:
|
||||
editor_test_data.results.update(result)
|
||||
if not isinstance(result, Result.Pass):
|
||||
save_asset_logs = True
|
||||
# If at least one test did not pass, save assets with errors and warnings
|
||||
if save_asset_logs:
|
||||
editor_utils.save_failed_asset_joblogs(workspace)
|
||||
|
||||
def _get_number_parallel_editors(self, request: Request) -> int:
|
||||
"""
|
||||
|
||||
@@ -10,6 +10,7 @@ from __future__ import annotations
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
import re
|
||||
|
||||
import ly_test_tools.environment.process_utils as process_utils
|
||||
import ly_test_tools.environment.waiter as waiter
|
||||
@@ -151,3 +152,41 @@ def retrieve_last_run_test_index_from_output(test_spec_list: list[EditorTestBase
|
||||
else:
|
||||
index += 1
|
||||
return index
|
||||
|
||||
def save_failed_asset_joblogs(workspace: AbstractWorkspace) -> None:
|
||||
"""
|
||||
Checks all asset logs in the JobLogs directory to see if the asset has any warnings or errors. If so, the asset is
|
||||
saved via ArtifactManager.
|
||||
|
||||
:param workspace: The AbstractWorkspace to access the JobLogs path
|
||||
:return: None
|
||||
"""
|
||||
for walk_tuple in os.walk(workspace.paths.ap_job_logs()):
|
||||
for log_file in walk_tuple[2]:
|
||||
full_log_path = os.path.join(walk_tuple[0], log_file)
|
||||
# Only save asset logs that contain errors or warnings
|
||||
if _check_log_errors_warnings(full_log_path):
|
||||
try:
|
||||
workspace.artifact_manager.save_artifact(full_log_path)
|
||||
except Exception as e: # Purposefully broad
|
||||
logger.warning(f"Error when saving log at path:{full_log_path}\n{e}")
|
||||
|
||||
def _check_log_errors_warnings(log_path: str) -> bool:
|
||||
"""
|
||||
Checks to see if the asset log contains any errors or warnings. Also returns True is no regex is found because
|
||||
something probably went wrong.
|
||||
Example log lines: ~~1643759303647~~1~~00000000000009E0~~AssetBuilder~~S: 0 errors, 1 warnings
|
||||
|
||||
:param log_path: The full path to the asset log file to read
|
||||
:return: True if the regex finds an error or warning, else False
|
||||
"""
|
||||
log_regex = "(\\d+) errors, (\\d+) warnings"
|
||||
with open(log_path, 'r') as opened_asset_log:
|
||||
for log_line in opened_asset_log:
|
||||
regex_match = re.search(log_regex, log_line)
|
||||
if regex_match is not None:
|
||||
break
|
||||
# If we match any non zero numbers in: n error, n warnings
|
||||
if regex_match is None or (int)(regex_match.group(1)) != 0 or (int)(regex_match.group(2)) != 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -170,3 +170,59 @@ class TestEditorTestUtils(unittest.TestCase):
|
||||
mock_test_list.append(mock_test)
|
||||
|
||||
assert 0 == editor_test_utils.retrieve_last_run_test_index_from_output(mock_test_list, mock_editor_output)
|
||||
|
||||
@mock.patch('ly_test_tools.o3de.editor_test_utils._check_log_errors_warnings')
|
||||
@mock.patch('os.walk')
|
||||
def test_SaveFailedAssetJoblogs_ManyValidLogs_SavesCorrectly(self, mock_walk, mock_check_log):
|
||||
mock_workspace = mock.MagicMock()
|
||||
mock_walk.return_value = [['MockDirectory', None, ['mock_log.log']],
|
||||
['MockDirectory2', None, ['mock_log2.log']]]
|
||||
mock_check_log.return_value = True
|
||||
|
||||
editor_test_utils.save_failed_asset_joblogs(mock_workspace)
|
||||
|
||||
assert mock_workspace.artifact_manager.save_artifact.call_count == 2
|
||||
|
||||
@mock.patch('ly_test_tools.o3de.editor_test_utils._check_log_errors_warnings')
|
||||
@mock.patch('os.walk')
|
||||
def test_SaveFailedAssetJoblogs_ManyInvalidLogs_NoSaves(self, mock_walk, mock_check_log):
|
||||
mock_workspace = mock.MagicMock()
|
||||
mock_walk.return_value = [['MockDirectory', None, ['mock_log.log']],
|
||||
['MockDirectory2', None, ['mock_log2.log']]]
|
||||
mock_check_log.return_value = False
|
||||
|
||||
editor_test_utils.save_failed_asset_joblogs(mock_workspace)
|
||||
|
||||
assert mock_workspace.artifact_manager.save_artifact.call_count == 0
|
||||
|
||||
def test_CheckLogErrorWarnings_ValidLine_ReturnsTrue(self):
|
||||
mock_log = '~~1643759303647~~1~~00000000000009E0~~AssetBuilder~~S: 0 errors, 1 warnings'
|
||||
mock_log_path = mock.MagicMock()
|
||||
|
||||
with mock.patch('builtins.open', mock.mock_open(read_data=mock_log)) as mock_file:
|
||||
expected = editor_test_utils._check_log_errors_warnings(mock_log_path)
|
||||
assert expected
|
||||
|
||||
def test_CheckLogErrorWarnings_MultipleValidLine_ReturnsTrue(self):
|
||||
mock_log = 'foo\nfoo\n~~1643759303647~~1~~00000000000009E0~~AssetBuilder~~S: 1 errors, 1 warnings'
|
||||
mock_log_path = mock.MagicMock()
|
||||
|
||||
with mock.patch('builtins.open', mock.mock_open(read_data=mock_log)) as mock_file:
|
||||
expected = editor_test_utils._check_log_errors_warnings(mock_log_path)
|
||||
assert expected
|
||||
|
||||
def test_CheckLogErrorWarnings_InvalidLine_ReturnsFalse(self):
|
||||
mock_log = 'foo\n~~1643759303647~~1~~00000000000009E0~~AssetBuilder~~S: 0 errors, 0 warnings'
|
||||
mock_log_path = mock.MagicMock()
|
||||
|
||||
with mock.patch('builtins.open', mock.mock_open(read_data=mock_log)) as mock_file:
|
||||
expected = editor_test_utils._check_log_errors_warnings(mock_log_path)
|
||||
assert not expected
|
||||
|
||||
def test_CheckLogErrorWarnings_InvalidRegex_ReturnsTrue(self):
|
||||
mock_log = 'Invalid last line'
|
||||
mock_log_path = mock.MagicMock()
|
||||
|
||||
with mock.patch('builtins.open', mock.mock_open(read_data=mock_log)) as mock_file:
|
||||
expected = editor_test_utils._check_log_errors_warnings(mock_log_path)
|
||||
assert expected
|
||||
|
||||
@@ -871,6 +871,7 @@ class TestRunningTests(unittest.TestCase):
|
||||
@mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._report_result')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._exec_editor_test')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test_utils.save_failed_asset_joblogs', mock.MagicMock())
|
||||
def test_RunSingleTest_ValidTest_ReportsResults(self, mock_setup_test, mock_exec_editor_test, mock_report_result):
|
||||
mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
|
||||
mock_test_data = mock.MagicMock()
|
||||
@@ -903,6 +904,7 @@ class TestRunningTests(unittest.TestCase):
|
||||
@mock.patch('threading.Thread')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_number_parallel_editors')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test_utils.save_failed_asset_joblogs', mock.MagicMock())
|
||||
def test_RunParallelTests_TwoTestsAndEditors_TwoThreads(self, mock_setup_test, mock_get_num_editors, mock_thread):
|
||||
mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
|
||||
mock_get_num_editors.return_value = 2
|
||||
@@ -919,6 +921,7 @@ class TestRunningTests(unittest.TestCase):
|
||||
@mock.patch('threading.Thread')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_number_parallel_editors')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test_utils.save_failed_asset_joblogs', mock.MagicMock())
|
||||
def test_RunParallelTests_TenTestsAndTwoEditors_TenThreads(self, mock_setup_test, mock_get_num_editors, mock_thread):
|
||||
mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
|
||||
mock_get_num_editors.return_value = 2
|
||||
@@ -937,6 +940,7 @@ class TestRunningTests(unittest.TestCase):
|
||||
@mock.patch('threading.Thread')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_number_parallel_editors')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test_utils.save_failed_asset_joblogs', mock.MagicMock())
|
||||
def test_RunParallelTests_TenTestsAndThreeEditors_TenThreads(self, mock_setup_test, mock_get_num_editors,
|
||||
mock_thread):
|
||||
mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
|
||||
@@ -956,6 +960,7 @@ class TestRunningTests(unittest.TestCase):
|
||||
@mock.patch('threading.Thread')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_number_parallel_editors')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test_utils.save_failed_asset_joblogs', mock.MagicMock())
|
||||
def test_RunParallelBatchedTests_TwoTestsAndEditors_TwoThreads(self, mock_setup_test, mock_get_num_editors,
|
||||
mock_thread):
|
||||
mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
|
||||
@@ -973,6 +978,7 @@ class TestRunningTests(unittest.TestCase):
|
||||
@mock.patch('threading.Thread')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_number_parallel_editors')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test_utils.save_failed_asset_joblogs', mock.MagicMock())
|
||||
def test_RunParallelBatchedTests_TenTestsAndTwoEditors_2Threads(self, mock_setup_test, mock_get_num_editors,
|
||||
mock_thread):
|
||||
mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
|
||||
@@ -992,6 +998,7 @@ class TestRunningTests(unittest.TestCase):
|
||||
@mock.patch('threading.Thread')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._get_number_parallel_editors')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test.EditorTestSuite._setup_editor_test')
|
||||
@mock.patch('ly_test_tools.o3de.editor_test_utils.save_failed_asset_joblogs', mock.MagicMock())
|
||||
def test_RunParallelBatchedTests_TenTestsAndThreeEditors_ThreeThreads(self, mock_setup_test, mock_get_num_editors,
|
||||
mock_thread):
|
||||
mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
|
||||
|
||||
+1
-1
@@ -42,5 +42,5 @@ ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev5-mac
|
||||
ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-mac TARGETS ISPCTexComp PACKAGE_HASH 8a4e93277b8face6ea2fd57c6d017bdb55643ed3d6387110bc5f6b3b884dd169)
|
||||
ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-mac TARGETS lz4 PACKAGE_HASH 891ff630bf34f7ab1d8eaee2ea0a8f1fca89dbdc63fca41ee592703dd488a73b)
|
||||
ly_associate_package(PACKAGE_NAME azslc-1.7.35-rev1-mac TARGETS azslc PACKAGE_HASH 03cb1ea8c47d4c80c893e2e88767272d5d377838f5ba94b777a45902dd85052e)
|
||||
ly_associate_package(PACKAGE_NAME SQLite-3.37.2-rev1-mac TARGETS SQLite PACKAGE_HASH f9101023f99cf32fc5867284ceb28c0761c23d2c5a4b1748349c69f976a2fbea)
|
||||
ly_associate_package(PACKAGE_NAME SQLite-3.37.2-rev2-mac TARGETS SQLite PACKAGE_HASH b7d9abdb68045003e030e1a9a805db1aefa5e8fde6dccfbb4fab3a06249a41fc)
|
||||
ly_associate_package(PACKAGE_NAME AwsIotDeviceSdkCpp-1.15.2-rev2-mac TARGETS AwsIotDeviceSdkCpp PACKAGE_HASH 4854edb7b88fa6437b4e69e87d0ee111a25313ac2a2db5bb2f8b674ba0974f95)
|
||||
|
||||
+60
-45
@@ -6,13 +6,13 @@ attrs==20.1.0 \
|
||||
--hash=sha256:0ef97238856430dcf9228e07f316aefc17e8939fc8507e18c6501b761ef1a42a \
|
||||
--hash=sha256:2867b7b9f8326499ab5b0e2d12801fa5c98842d2cbd22b35112ae04bf85b4dff
|
||||
# via -r .\requirements.txt
|
||||
boto3==1.12.21 \
|
||||
--hash=sha256:50105a25e301e20b361b2b8fafee196a425a4758e51f400a0f381d42e4bd909e \
|
||||
--hash=sha256:5fe70e656f92e4e649dc4cf05786f57d180e5d491bcb22c80411512ec2b27c15 \
|
||||
boto3==1.20.44 \
|
||||
--hash=sha256:26f18ca7411615f33d8d1bf60cc8efe5b331a57b3013d5f8f3587cd5350c27cb \
|
||||
--hash=sha256:4470f64e4af609ff678055338c96a6f7cbe601d1fb06a4ea7dc8d9223c2e527a \
|
||||
# via -r .\requirements.txt
|
||||
botocore==1.15.21 \
|
||||
--hash=sha256:4aaf6c94bcaace260138d32eae144be1b5d2ddce9ef0f395da32c68e106ff20f \
|
||||
--hash=sha256:86f7f1c489887f9e3c2ede598e2a30f8bd259c11e8ebe25e897e40231b3f4bc8 \
|
||||
botocore==1.23.44 \
|
||||
--hash=sha256:11483a493de4a76ef218d8cd3980c63550d006a0d082c10d53c0954184ca542a \
|
||||
--hash=sha256:8e5317f84fc1118bff58fa6fa79a9b62083e75a2a9c62feb3ea73694c550b99d \
|
||||
# via -r .\requirements.txt, boto3, s3transfer
|
||||
certifi==2019.11.28 \
|
||||
--hash=sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3 \
|
||||
@@ -22,6 +22,11 @@ chardet==3.0.4 \
|
||||
--hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae \
|
||||
--hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691 \
|
||||
# via -r .\requirements.txt, requests
|
||||
charset-normalizer==2.0.10 \
|
||||
--hash=sha256:876d180e9d7432c5d1dfd4c5d26b72f099d503e8fcc0feb7532c9289be60fcbd \
|
||||
--hash=sha256:cb957888737fc0bbcd78e3df769addb41fd1ff8cf950dc9e7ad7793f1bf44455 \
|
||||
# via
|
||||
# -r requirements.txt, requests
|
||||
colorama==0.4.3 \
|
||||
--hash=sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff \
|
||||
--hash=sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1 \
|
||||
@@ -129,30 +134,40 @@ packaging==20.4 \
|
||||
--hash=sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8 \
|
||||
--hash=sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181 \
|
||||
# pytest
|
||||
pillow==7.0.0 \
|
||||
--hash=sha256:0a628977ac2e01ca96aaae247ec2bd38e729631ddf2221b4b715446fd45505be \
|
||||
--hash=sha256:4d9ed9a64095e031435af120d3c910148067087541131e82b3e8db302f4c8946 \
|
||||
--hash=sha256:54ebae163e8412aff0b9df1e88adab65788f5f5b58e625dc5c7f51eaf14a6837 \
|
||||
--hash=sha256:5bfef0b1cdde9f33881c913af14e43db69815c7e8df429ceda4c70a5e529210f \
|
||||
--hash=sha256:5f3546ceb08089cedb9e8ff7e3f6a7042bb5b37c2a95d392fb027c3e53a2da00 \
|
||||
--hash=sha256:5f7ae9126d16194f114435ebb79cc536b5682002a4fa57fa7bb2cbcde65f2f4d \
|
||||
--hash=sha256:62a889aeb0a79e50ecf5af272e9e3c164148f4bd9636cc6bcfa182a52c8b0533 \
|
||||
--hash=sha256:7406f5a9b2fd966e79e6abdaf700585a4522e98d6559ce37fc52e5c955fade0a \
|
||||
--hash=sha256:8453f914f4e5a3d828281a6628cf517832abfa13ff50679a4848926dac7c0358 \
|
||||
--hash=sha256:87269cc6ce1e3dee11f23fa515e4249ae678dbbe2704598a51cee76c52e19cda \
|
||||
--hash=sha256:875358310ed7abd5320f21dd97351d62de4929b0426cdb1eaa904b64ac36b435 \
|
||||
--hash=sha256:8ac6ce7ff3892e5deaab7abaec763538ffd011f74dc1801d93d3c5fc541feee2 \
|
||||
--hash=sha256:91b710e3353aea6fc758cdb7136d9bbdcb26b53cefe43e2cba953ac3ee1d3313 \
|
||||
--hash=sha256:9d2ba4ed13af381233e2d810ff3bab84ef9f18430a9b336ab69eaf3cd24299ff \
|
||||
--hash=sha256:a62ec5e13e227399be73303ff301f2865bf68657d15ea50b038d25fc41097317 \
|
||||
--hash=sha256:ab76e5580b0ed647a8d8d2d2daee170e8e9f8aad225ede314f684e297e3643c2 \
|
||||
--hash=sha256:bf4003aa538af3f4205c5fac56eacaa67a6dd81e454ffd9e9f055fff9f1bc614 \
|
||||
--hash=sha256:bf598d2e37cf8edb1a2f26ed3fb255191f5232badea4003c16301cb94ac5bdd0 \
|
||||
--hash=sha256:c18f70dc27cc5d236f10e7834236aff60aadc71346a5bc1f4f83a4b3abee6386 \
|
||||
--hash=sha256:c5ed816632204a2fc9486d784d8e0d0ae754347aba99c811458d69fcdfd2a2f9 \
|
||||
--hash=sha256:dc058b7833184970d1248135b8b0ab702e6daa833be14035179f2acb78ff5636 \
|
||||
--hash=sha256:ff3797f2f16bf9d17d53257612da84dd0758db33935777149b3334c01ff68865 \
|
||||
# via requirements.txt, imageio
|
||||
pillow==9.0.0 \
|
||||
--hash=sha256:03b27b197deb4ee400ed57d8d4e572d2d8d80f825b6634daf6e2c18c3c6ccfa6 \
|
||||
--hash=sha256:0b281fcadbb688607ea6ece7649c5d59d4bbd574e90db6cd030e9e85bde9fecc \
|
||||
--hash=sha256:0ebd8b9137630a7bbbff8c4b31e774ff05bbb90f7911d93ea2c9371e41039b52 \
|
||||
--hash=sha256:113723312215b25c22df1fdf0e2da7a3b9c357a7d24a93ebbe80bfda4f37a8d4 \
|
||||
--hash=sha256:2d16b6196fb7a54aff6b5e3ecd00f7c0bab1b56eee39214b2b223a9d938c50af \
|
||||
--hash=sha256:2fd8053e1f8ff1844419842fd474fc359676b2e2a2b66b11cc59f4fa0a301315 \
|
||||
--hash=sha256:31b265496e603985fad54d52d11970383e317d11e18e856971bdbb86af7242a4 \
|
||||
--hash=sha256:3586e12d874ce2f1bc875a3ffba98732ebb12e18fb6d97be482bd62b56803281 \
|
||||
--hash=sha256:47f5cf60bcb9fbc46011f75c9b45a8b5ad077ca352a78185bd3e7f1d294b98bb \
|
||||
--hash=sha256:490e52e99224858f154975db61c060686df8a6b3f0212a678e5d2e2ce24675c9 \
|
||||
--hash=sha256:500d397ddf4bbf2ca42e198399ac13e7841956c72645513e8ddf243b31ad2128 \
|
||||
--hash=sha256:52abae4c96b5da630a8b4247de5428f593465291e5b239f3f843a911a3cf0105 \
|
||||
--hash=sha256:6579f9ba84a3d4f1807c4aab4be06f373017fc65fff43498885ac50a9b47a553 \
|
||||
--hash=sha256:68e06f8b2248f6dc8b899c3e7ecf02c9f413aab622f4d6190df53a78b93d97a5 \
|
||||
--hash=sha256:6c5439bfb35a89cac50e81c751317faea647b9a3ec11c039900cd6915831064d \
|
||||
--hash=sha256:72c3110228944019e5f27232296c5923398496b28be42535e3b2dc7297b6e8b6 \
|
||||
--hash=sha256:72f649d93d4cc4d8cf79c91ebc25137c358718ad75f99e99e043325ea7d56100 \
|
||||
--hash=sha256:7aaf07085c756f6cb1c692ee0d5a86c531703b6e8c9cae581b31b562c16b98ce \
|
||||
--hash=sha256:80fe92813d208ce8aa7d76da878bdc84b90809f79ccbad2a288e9bcbeac1d9bd \
|
||||
--hash=sha256:95545137fc56ce8c10de646074d242001a112a92de169986abd8c88c27566a05 \
|
||||
--hash=sha256:97b6d21771da41497b81652d44191489296555b761684f82b7b544c49989110f \
|
||||
--hash=sha256:98cb63ca63cb61f594511c06218ab4394bf80388b3d66cd61d0b1f63ee0ea69f \
|
||||
--hash=sha256:9f3b4522148586d35e78313db4db0df4b759ddd7649ef70002b6c3767d0fdeb7 \
|
||||
--hash=sha256:a09a9d4ec2b7887f7a088bbaacfd5c07160e746e3d47ec5e8050ae3b2a229e9f \
|
||||
--hash=sha256:b5050d681bcf5c9f2570b93bee5d3ec8ae4cf23158812f91ed57f7126df91762 \
|
||||
--hash=sha256:bb47a548cea95b86494a26c89d153fd31122ed65255db5dcbc421a2d28eb3379 \
|
||||
--hash=sha256:bc462d24500ba707e9cbdef436c16e5c8cbf29908278af053008d9f689f56dee \
|
||||
--hash=sha256:c2067b3bb0781f14059b112c9da5a91c80a600a97915b4f48b37f197895dd925 \
|
||||
--hash=sha256:d154ed971a4cc04b93a6d5b47f37948d1f621f25de3e8fa0c26b2d44f24e3e8f \
|
||||
--hash=sha256:d5dcea1387331c905405b09cdbfb34611050cc52c865d71f2362f354faee1e9f \
|
||||
--hash=sha256:ee6e2963e92762923956fe5d3479b1fdc3b76c83f290aad131a2f98c3df0593e \
|
||||
--hash=sha256:fd0e5062f11cb3e730450a7d9f323f4051b532781026395c4323b8ad055523c4 \
|
||||
# via -r requirements.txt
|
||||
pluggy==0.13.1 \
|
||||
--hash=sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0 \
|
||||
--hash=sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d \
|
||||
@@ -191,10 +206,10 @@ psutil==5.8.0 \
|
||||
--hash=sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3 \
|
||||
--hash=sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563
|
||||
# via requirements.txt
|
||||
py==1.9.0 \
|
||||
--hash=sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2 \
|
||||
--hash=sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342 \
|
||||
# via -r .\requirements.txt, pytest
|
||||
py==1.11.0 \
|
||||
--hash=sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719 \
|
||||
--hash=sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378 \
|
||||
# via -r requirements.txt
|
||||
pyparsing==2.4.7 \
|
||||
--hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \
|
||||
--hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b \
|
||||
@@ -246,13 +261,13 @@ PyYAML==5.4.1 \
|
||||
--hash=sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df \
|
||||
--hash=sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc \
|
||||
# via requirements.txt
|
||||
requests==2.23.0 \
|
||||
--hash=sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee \
|
||||
--hash=sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6 \
|
||||
# via -r .\requirements.txt
|
||||
s3transfer==0.3.3 \
|
||||
--hash=sha256:2482b4259524933a022d59da830f51bd746db62f047d6eb213f2f8855dcb8a13 \
|
||||
--hash=sha256:921a37e2aefc64145e7b73d50c71bb4f26f46e4c9f414dc648c6245ff92cf7db \
|
||||
requests==2.27.1 \
|
||||
--hash=sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61 \
|
||||
--hash=sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d \
|
||||
# via -r requirements.txt
|
||||
s3transfer==0.5.0 \
|
||||
--hash=sha256:50ed823e1dc5868ad40c8dc92072f757aa0e653a192845c94a3b676f4a62da4c \
|
||||
--hash=sha256:9c1dc369814391a6bda20ebbf4b70a0f34630592c9aa520856bf384916af2803 \
|
||||
# boto3
|
||||
scipy==1.4.1 \
|
||||
--hash=sha256:00af72998a46c25bdb5824d2b729e7dabec0c765f9deb0b504f928591f5ff9d4 \
|
||||
@@ -290,10 +305,10 @@ smmap==3.0.5 \
|
||||
--hash=sha256:7bfcf367828031dc893530a29cb35eb8c8f2d7c8f2d0989354d75d24c8573714 \
|
||||
--hash=sha256:84c2751ef3072d4f6b2785ec7ee40244c6f45eb934d9e543e2c51f1bd3d54c50 \
|
||||
# via smmap2
|
||||
urllib3==1.25.8 \
|
||||
--hash=sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc \
|
||||
--hash=sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc \
|
||||
# via -r .\requirements.txt, botocore, requests
|
||||
urllib3==1.26.8 \
|
||||
--hash=sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed \
|
||||
--hash=sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c \
|
||||
# via -r requirements.txt
|
||||
wcwidth==0.2.5 \
|
||||
--hash=sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784 \
|
||||
--hash=sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83 \
|
||||
|
||||
Reference in New Issue
Block a user