Merge branch 'development' of https://github.com/o3de/o3de into cgalvan/DraftStreamingImageAssetPixelAPI
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AtomCore/std/containers/array_view.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -16,31 +17,38 @@ namespace AZ
|
||||
{
|
||||
class MaterialAsset;
|
||||
|
||||
//! Utility for building material property names consisting of a group name and a property sub-name.
|
||||
//! Represented as "[groupName].[propertyName]".
|
||||
//! The group name is optional, in which case the ID will just be "[propertyName]".
|
||||
//! Utility for building material property IDs.
|
||||
//! These IDs are represented like "groupA.groupB.[...].propertyName".
|
||||
//! The groups are optional, in which case the full property ID will just be like "propertyName".
|
||||
class MaterialPropertyId
|
||||
{
|
||||
public:
|
||||
static bool IsValidName(AZStd::string_view name);
|
||||
static bool IsValidName(const AZ::Name& name);
|
||||
|
||||
//! Creates a MaterialPropertyId from a full name string like "[groupName].[propertyName]" or just "[propertyName]"
|
||||
//! Creates a MaterialPropertyId from a full name string like "groupA.groupB.[...].propertyName" or just "propertyName".
|
||||
//! Also checks the name for validity.
|
||||
static MaterialPropertyId Parse(AZStd::string_view fullPropertyId);
|
||||
|
||||
MaterialPropertyId() = default;
|
||||
explicit MaterialPropertyId(AZStd::string_view propertyName);
|
||||
MaterialPropertyId(AZStd::string_view groupName, AZStd::string_view propertyName);
|
||||
MaterialPropertyId(const Name& groupName, const Name& propertyName);
|
||||
explicit MaterialPropertyId(const AZStd::array_view<AZStd::string> names);
|
||||
|
||||
AZ_DEFAULT_COPY_MOVE(MaterialPropertyId);
|
||||
|
||||
const Name& GetGroupName() const;
|
||||
const Name& GetPropertyName() const;
|
||||
const Name& GetFullName() const;
|
||||
operator const Name&() const;
|
||||
|
||||
//! Returns a pointer to the full name ("[groupName].[propertyName]").
|
||||
//! Same as Name::GetCStr()
|
||||
//! This is included for convenience so it can be used for error messages in the same way an AZ::Name is used.
|
||||
const char* GetCStr() const;
|
||||
|
||||
//! Returns a string_view of the full name ("[groupName].[propertyName]").
|
||||
//! Same as Name::GetStringView()
|
||||
//! This is included for convenience so it can be used for string comparison in the same way an AZ::Name is used.
|
||||
AZStd::string_view GetStringView() const;
|
||||
|
||||
//! Returns a hash of the full name. This is needed for compatibility with NameIdReflectionMap.
|
||||
Name::Hash GetHash() const;
|
||||
@@ -52,8 +60,6 @@ namespace AZ
|
||||
|
||||
private:
|
||||
Name m_fullName;
|
||||
Name m_groupName;
|
||||
Name m_propertyName;
|
||||
};
|
||||
} // namespace RPI
|
||||
|
||||
|
||||
@@ -27,63 +27,84 @@ namespace AZ
|
||||
|
||||
bool MaterialPropertyId::IsValid() const
|
||||
{
|
||||
const bool groupNameIsValid = m_groupName.IsEmpty() || IsValidName(m_groupName);
|
||||
const bool propertyNameIsValid = IsValidName(m_propertyName);
|
||||
return groupNameIsValid && propertyNameIsValid;
|
||||
return !m_fullName.IsEmpty();
|
||||
}
|
||||
|
||||
MaterialPropertyId MaterialPropertyId::Parse(AZStd::string_view fullPropertyId)
|
||||
{
|
||||
AZStd::vector<AZStd::string> tokens;
|
||||
AzFramework::StringFunc::Tokenize(fullPropertyId.data(), tokens, '.', true, true);
|
||||
AzFramework::StringFunc::Tokenize(fullPropertyId, tokens, '.', true, true);
|
||||
|
||||
if (tokens.size() == 1)
|
||||
if (tokens.empty())
|
||||
{
|
||||
return MaterialPropertyId{"", tokens[0]};
|
||||
AZ_Error("MaterialPropertyId", false, "Property ID is empty.", fullPropertyId.data());
|
||||
return MaterialPropertyId{};
|
||||
}
|
||||
else if (tokens.size() == 2)
|
||||
|
||||
for (const auto& token : tokens)
|
||||
{
|
||||
return MaterialPropertyId{tokens[0], tokens[1]};
|
||||
if (!IsValidName(token))
|
||||
{
|
||||
AZ_Error("MaterialPropertyId", false, "Property ID '%.*s' is not a valid identifier.", AZ_STRING_ARG(fullPropertyId));
|
||||
return MaterialPropertyId{};
|
||||
}
|
||||
}
|
||||
|
||||
MaterialPropertyId id;
|
||||
id.m_fullName = fullPropertyId;
|
||||
return id;
|
||||
}
|
||||
|
||||
MaterialPropertyId::MaterialPropertyId(AZStd::string_view propertyName)
|
||||
{
|
||||
if (!IsValidName(propertyName))
|
||||
{
|
||||
AZ_Error("MaterialPropertyId", false, "Property name '%.*s' is not a valid identifier.", AZ_STRING_ARG(propertyName));
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("MaterialPropertyId", false, "Property ID '%s' is not a valid identifier.", fullPropertyId.data());
|
||||
return MaterialPropertyId{};
|
||||
m_fullName = propertyName;
|
||||
}
|
||||
}
|
||||
|
||||
MaterialPropertyId::MaterialPropertyId(AZStd::string_view groupName, AZStd::string_view propertyName)
|
||||
: MaterialPropertyId(Name{groupName}, Name{propertyName})
|
||||
{
|
||||
}
|
||||
|
||||
MaterialPropertyId::MaterialPropertyId(const Name& groupName, const Name& propertyName)
|
||||
{
|
||||
AZ_Error("MaterialPropertyId", groupName.IsEmpty() || IsValidName(groupName), "Group name '%s' is not a valid identifier.", groupName.GetCStr());
|
||||
AZ_Error("MaterialPropertyId", IsValidName(propertyName), "Property name '%s' is not a valid identifier.", propertyName.GetCStr());
|
||||
m_groupName = groupName;
|
||||
m_propertyName = propertyName;
|
||||
if (groupName.IsEmpty())
|
||||
if (!IsValidName(groupName))
|
||||
{
|
||||
m_fullName = m_propertyName.GetStringView();
|
||||
AZ_Error("MaterialPropertyId", false, "Group name '%.*s' is not a valid identifier.", AZ_STRING_ARG(groupName));
|
||||
}
|
||||
else if (!IsValidName(propertyName))
|
||||
{
|
||||
AZ_Error("MaterialPropertyId", false, "Property name '%.*s' is not a valid identifier.", AZ_STRING_ARG(propertyName));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fullName = AZStd::string::format("%s.%s", m_groupName.GetCStr(), m_propertyName.GetCStr());
|
||||
m_fullName = AZStd::string::format("%.*s.%.*s", AZ_STRING_ARG(groupName), AZ_STRING_ARG(propertyName));
|
||||
}
|
||||
}
|
||||
|
||||
const Name& MaterialPropertyId::GetGroupName() const
|
||||
|
||||
MaterialPropertyId::MaterialPropertyId(const Name& groupName, const Name& propertyName)
|
||||
: MaterialPropertyId(groupName.GetStringView(), propertyName.GetStringView())
|
||||
{
|
||||
return m_groupName;
|
||||
}
|
||||
|
||||
MaterialPropertyId::MaterialPropertyId(const AZStd::array_view<AZStd::string> names)
|
||||
{
|
||||
for (const auto& name : names)
|
||||
{
|
||||
if (!IsValidName(name))
|
||||
{
|
||||
AZ_Error("MaterialPropertyId", false, "'%s' is not a valid identifier.", name.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string fullName; // m_fullName is a Name, not a string, so we have to join into a local variable temporarily.
|
||||
AzFramework::StringFunc::Join(fullName, names.begin(), names.end(), ".");
|
||||
m_fullName = fullName;
|
||||
}
|
||||
|
||||
const Name& MaterialPropertyId::GetPropertyName() const
|
||||
{
|
||||
return m_propertyName;
|
||||
}
|
||||
|
||||
const Name& MaterialPropertyId::GetFullName() const
|
||||
MaterialPropertyId::operator const Name&() const
|
||||
{
|
||||
return m_fullName;
|
||||
}
|
||||
@@ -92,6 +113,11 @@ namespace AZ
|
||||
{
|
||||
return m_fullName.GetCStr();
|
||||
}
|
||||
|
||||
AZStd::string_view MaterialPropertyId::GetStringView() const
|
||||
{
|
||||
return m_fullName.GetStringView();
|
||||
}
|
||||
|
||||
Name::Hash MaterialPropertyId::GetHash() const
|
||||
{
|
||||
|
||||
@@ -340,16 +340,16 @@ namespace AZ
|
||||
if (result == MaterialUtils::GetImageAssetResult::Missing)
|
||||
{
|
||||
materialAssetCreator.ReportWarning(
|
||||
"Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(),
|
||||
"Material property '%s': Could not find the image '%s'", propertyId.GetCStr(),
|
||||
property.second.m_value.GetValue<AZStd::string>().data());
|
||||
}
|
||||
|
||||
imageAsset.SetAutoLoadBehavior(Data::AssetLoadBehavior::PreLoad);
|
||||
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset);
|
||||
materialAssetCreator.SetPropertyValue(propertyId, imageAsset);
|
||||
}
|
||||
else
|
||||
{
|
||||
materialAssetCreator.SetPropertyValue(propertyId.GetFullName(), property.second.m_value);
|
||||
materialAssetCreator.SetPropertyValue(propertyId, property.second.m_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace AZ
|
||||
{
|
||||
if (action.m_operation == "rename")
|
||||
{
|
||||
if (action.m_renameFrom == propertyId.GetFullName().GetStringView())
|
||||
if (action.m_renameFrom == propertyId.GetStringView())
|
||||
{
|
||||
propertyId = MaterialPropertyId::Parse(action.m_renameTo);
|
||||
renamed = true;
|
||||
@@ -177,14 +177,19 @@ namespace AZ
|
||||
|
||||
// Do the search again with the new names
|
||||
|
||||
groupIter = m_propertyLayout.m_properties.find(propertyId.GetGroupName().GetStringView());
|
||||
if (groupIter != m_propertyLayout.m_properties.end())
|
||||
AZStd::vector<AZStd::string> tokens;
|
||||
AZ::StringFunc::Tokenize(propertyId.GetStringView(), tokens, ".", true, true);
|
||||
if (tokens.size() == 2)
|
||||
{
|
||||
for (const PropertyDefinition& property : groupIter->second)
|
||||
groupIter = m_propertyLayout.m_properties.find(tokens[0]);
|
||||
if (groupIter != m_propertyLayout.m_properties.end())
|
||||
{
|
||||
if (property.m_name == propertyId.GetPropertyName().GetStringView())
|
||||
for (const PropertyDefinition& property : groupIter->second)
|
||||
{
|
||||
return &property;
|
||||
if (property.m_name == tokens[1])
|
||||
{
|
||||
return &property;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,7 +405,7 @@ namespace AZ
|
||||
continue;
|
||||
}
|
||||
|
||||
materialTypeAssetCreator.BeginMaterialProperty(propertyId.GetFullName(), property.m_dataType);
|
||||
materialTypeAssetCreator.BeginMaterialProperty(propertyId, property.m_dataType);
|
||||
|
||||
if (property.m_dataType == MaterialPropertyDataType::Enum)
|
||||
{
|
||||
@@ -454,18 +459,18 @@ namespace AZ
|
||||
if (result == MaterialUtils::GetImageAssetResult::Missing)
|
||||
{
|
||||
materialTypeAssetCreator.ReportError(
|
||||
"Material property '%s': Could not find the image '%s'", propertyId.GetFullName().GetCStr(),
|
||||
"Material property '%s': Could not find the image '%s'", propertyId.GetCStr(),
|
||||
property.m_value.GetValue<AZStd::string>().data());
|
||||
}
|
||||
else
|
||||
{
|
||||
materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), imageAsset);
|
||||
materialTypeAssetCreator.SetPropertyValue(propertyId, imageAsset);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MaterialPropertyDataType::Enum:
|
||||
{
|
||||
MaterialPropertyIndex propertyIndex = materialTypeAssetCreator.GetMaterialPropertiesLayout()->FindPropertyIndex(propertyId.GetFullName());
|
||||
MaterialPropertyIndex propertyIndex = materialTypeAssetCreator.GetMaterialPropertiesLayout()->FindPropertyIndex(propertyId);
|
||||
const MaterialPropertyDescriptor* propertyDescriptor = materialTypeAssetCreator.GetMaterialPropertiesLayout()->GetPropertyDescriptor(propertyIndex);
|
||||
|
||||
AZ::Name enumName = AZ::Name(property.m_value.GetValue<AZStd::string>());
|
||||
@@ -476,12 +481,12 @@ namespace AZ
|
||||
}
|
||||
else
|
||||
{
|
||||
materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), enumValue);
|
||||
materialTypeAssetCreator.SetPropertyValue(propertyId, enumValue);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
materialTypeAssetCreator.SetPropertyValue(propertyId.GetFullName(), property.m_value);
|
||||
materialTypeAssetCreator.SetPropertyValue(propertyId, property.m_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +322,7 @@ namespace AZ
|
||||
|
||||
void Scene::RemoveRenderPipeline(const RenderPipelineId& pipelineId)
|
||||
{
|
||||
bool removed = false;
|
||||
[[maybe_unused]] bool removed = false;
|
||||
for (auto it = m_pipelines.begin(); it != m_pipelines.end(); ++it)
|
||||
{
|
||||
if (pipelineId == (*it)->GetId())
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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 <AzTest/AzTest.h>
|
||||
#include <Common/RPITestFixture.h>
|
||||
#include <Common/ErrorMessageFinder.h>
|
||||
#include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
using namespace AZ;
|
||||
using namespace RPI;
|
||||
|
||||
class MaterialPropertyIdTests
|
||||
: public RPITestFixture
|
||||
{
|
||||
};
|
||||
|
||||
TEST_F(MaterialPropertyIdTests, TestConstructWithPropertyName)
|
||||
{
|
||||
MaterialPropertyId id{"color"};
|
||||
EXPECT_TRUE(id.IsValid());
|
||||
EXPECT_STREQ(id.GetCStr(), "color");
|
||||
AZ::Name idCastedToName = id;
|
||||
EXPECT_EQ(idCastedToName, AZ::Name{"color"});
|
||||
}
|
||||
|
||||
TEST_F(MaterialPropertyIdTests, TestConstructWithPropertyName_BadName)
|
||||
{
|
||||
ErrorMessageFinder errorMessageFinder;
|
||||
errorMessageFinder.AddExpectedErrorMessage("not a valid identifier");
|
||||
|
||||
MaterialPropertyId id{"color?"};
|
||||
EXPECT_FALSE(id.IsValid());
|
||||
|
||||
errorMessageFinder.CheckExpectedErrorsFound();
|
||||
}
|
||||
|
||||
TEST_F(MaterialPropertyIdTests, TestConstructWithTwoNames)
|
||||
{
|
||||
MaterialPropertyId id{"baseColor", "factor"};
|
||||
EXPECT_TRUE(id.IsValid());
|
||||
EXPECT_STREQ(id.GetCStr(), "baseColor.factor");
|
||||
AZ::Name idCastedToName = id;
|
||||
EXPECT_EQ(idCastedToName, AZ::Name{"baseColor.factor"});
|
||||
}
|
||||
|
||||
TEST_F(MaterialPropertyIdTests, TestConstructWithTwoNames_BadGroupName)
|
||||
{
|
||||
ErrorMessageFinder errorMessageFinder;
|
||||
errorMessageFinder.AddExpectedErrorMessage("not a valid identifier");
|
||||
|
||||
MaterialPropertyId id{"layer1.baseColor", "factor"};
|
||||
EXPECT_FALSE(id.IsValid());
|
||||
|
||||
errorMessageFinder.CheckExpectedErrorsFound();
|
||||
}
|
||||
|
||||
TEST_F(MaterialPropertyIdTests, TestConstructWithTwoNames_BadPropertyName)
|
||||
{
|
||||
ErrorMessageFinder errorMessageFinder;
|
||||
errorMessageFinder.AddExpectedErrorMessage("not a valid identifier");
|
||||
|
||||
MaterialPropertyId id{"baseColor", ".factor"};
|
||||
EXPECT_FALSE(id.IsValid());
|
||||
|
||||
errorMessageFinder.CheckExpectedErrorsFound();
|
||||
}
|
||||
|
||||
TEST_F(MaterialPropertyIdTests, TestConstructWithMultipleNames)
|
||||
{
|
||||
AZStd::vector<AZStd::string> names{"layer1", "clearCoat", "normal", "factor"};
|
||||
MaterialPropertyId id{names};
|
||||
EXPECT_TRUE(id.IsValid());
|
||||
EXPECT_STREQ(id.GetCStr(), "layer1.clearCoat.normal.factor");
|
||||
AZ::Name idCastedToName = id;
|
||||
EXPECT_EQ(idCastedToName, AZ::Name{"layer1.clearCoat.normal.factor"});
|
||||
}
|
||||
|
||||
TEST_F(MaterialPropertyIdTests, TestConstructWithMultipleNames_BadName)
|
||||
{
|
||||
ErrorMessageFinder errorMessageFinder;
|
||||
errorMessageFinder.AddExpectedErrorMessage("not a valid identifier");
|
||||
|
||||
AZStd::vector<AZStd::string> names{"layer1", "clear-coat", "normal", "factor"};
|
||||
MaterialPropertyId id{names};
|
||||
EXPECT_FALSE(id.IsValid());
|
||||
|
||||
errorMessageFinder.CheckExpectedErrorsFound();
|
||||
}
|
||||
|
||||
TEST_F(MaterialPropertyIdTests, TestParse)
|
||||
{
|
||||
MaterialPropertyId id = MaterialPropertyId::Parse("layer1.clearCoat.normal.factor");
|
||||
EXPECT_TRUE(id.IsValid());
|
||||
EXPECT_STREQ(id.GetCStr(), "layer1.clearCoat.normal.factor");
|
||||
AZ::Name idCastedToName = id;
|
||||
EXPECT_EQ(idCastedToName, AZ::Name{"layer1.clearCoat.normal.factor"});
|
||||
}
|
||||
|
||||
TEST_F(MaterialPropertyIdTests, TestParse_BadName)
|
||||
{
|
||||
ErrorMessageFinder errorMessageFinder;
|
||||
errorMessageFinder.AddExpectedErrorMessage("not a valid identifier");
|
||||
|
||||
MaterialPropertyId id = MaterialPropertyId::Parse("layer1.clearCoat.normal,factor");
|
||||
EXPECT_FALSE(id.IsValid());
|
||||
|
||||
errorMessageFinder.CheckExpectedErrorsFound();
|
||||
}
|
||||
|
||||
TEST_F(MaterialPropertyIdTests, TestNameValidity)
|
||||
{
|
||||
EXPECT_TRUE(MaterialPropertyId::IsValidName("a"));
|
||||
EXPECT_TRUE(MaterialPropertyId::IsValidName("z"));
|
||||
EXPECT_TRUE(MaterialPropertyId::IsValidName("A"));
|
||||
EXPECT_TRUE(MaterialPropertyId::IsValidName("Z"));
|
||||
EXPECT_TRUE(MaterialPropertyId::IsValidName("_"));
|
||||
EXPECT_TRUE(MaterialPropertyId::IsValidName("m_layer10bazBAZ"));
|
||||
EXPECT_FALSE(MaterialPropertyId::IsValidName(""));
|
||||
EXPECT_FALSE(MaterialPropertyId::IsValidName("1layer"));
|
||||
EXPECT_FALSE(MaterialPropertyId::IsValidName("base-color"));
|
||||
EXPECT_FALSE(MaterialPropertyId::IsValidName("base.color"));
|
||||
EXPECT_FALSE(MaterialPropertyId::IsValidName("base/color"));
|
||||
}
|
||||
}
|
||||
@@ -905,7 +905,7 @@ namespace UnitTest
|
||||
JsonTestResult loadResult = LoadTestDataFromJson(material, inputJson);
|
||||
auto materialAssetResult = material.CreateMaterialAsset(Uuid::CreateRandom(), "test.material", AZ::RPI::MaterialAssetProcessingMode::PreBake);
|
||||
EXPECT_TRUE(materialAssetResult);
|
||||
MaterialPropertyIndex propertyIndex = materialAssetResult.GetValue()->GetMaterialPropertiesLayout()->FindPropertyIndex(MaterialPropertyId{groupName, propertyName}.GetFullName());
|
||||
MaterialPropertyIndex propertyIndex = materialAssetResult.GetValue()->GetMaterialPropertiesLayout()->FindPropertyIndex(MaterialPropertyId{groupName, propertyName});
|
||||
CheckSimilar(expectedFinalValue, materialAssetResult.GetValue()->GetPropertyValues()[propertyIndex.GetIndex()].GetValue<PropertyTypeT>());
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ set(FILES
|
||||
Tests/Material/MaterialSourceDataTests.cpp
|
||||
Tests/Material/MaterialFunctorTests.cpp
|
||||
Tests/Material/MaterialFunctorSourceDataSerializerTests.cpp
|
||||
Tests/Material/MaterialPropertyIdTests.cpp
|
||||
Tests/Material/MaterialPropertyValueSourceDataTests.cpp
|
||||
Tests/Material/MaterialTests.cpp
|
||||
Tests/Model/ModelTests.cpp
|
||||
|
||||
Reference in New Issue
Block a user