Merge branch 'main' into script_canvas
This commit is contained in:
@@ -51,7 +51,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
TEST_SUITE sandbox
|
||||
TEST_SERIAL
|
||||
PATH ${CMAKE_CURRENT_LIST_DIR}/physics/TestSuite_Sandbox.py
|
||||
TIMEOUT 3600
|
||||
TIMEOUT 1500
|
||||
RUNTIME_DEPENDENCIES
|
||||
Legacy::Editor
|
||||
AZ::AssetProcessor
|
||||
|
||||
@@ -42,6 +42,7 @@ class TestAssetPicker(object):
|
||||
|
||||
@pytest.mark.test_case_id("C13751579", "C1508814")
|
||||
@pytest.mark.SUITE_periodic
|
||||
@pytest.mark.xfail # ATOM-15493
|
||||
def test_AssetPicker_UI_UX(self, request, editor, level, launcher_platform):
|
||||
expected_lines = [
|
||||
"TestEntity Entity successfully created",
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace AZ
|
||||
"Unable to retrieve the correct container information for AZStd::array instance.");
|
||||
}
|
||||
|
||||
Flags flags = Flags::None;
|
||||
ContinuationFlags flags = ContinuationFlags::None;
|
||||
Uuid elementTypeId = Uuid::CreateNull();
|
||||
auto typeEnumCallback = [&elementTypeId, &flags](const Uuid&, const SerializeContext::ClassElement* genericClassElement)
|
||||
{
|
||||
@@ -82,7 +82,7 @@ namespace AZ
|
||||
elementTypeId = genericClassElement->m_typeId;
|
||||
if (genericClassElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
|
||||
{
|
||||
flags = Flags::ResolvePointer;
|
||||
flags = ContinuationFlags::ResolvePointer;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -161,7 +161,7 @@ namespace AZ
|
||||
"Not enough entries in JSON array to load an AZStd::array from.");
|
||||
}
|
||||
|
||||
Flags flags = Flags::None;
|
||||
ContinuationFlags flags = ContinuationFlags::None;
|
||||
Uuid elementTypeId = Uuid::CreateNull();
|
||||
auto typeEnumCallback = [&elementTypeId, &flags](const Uuid&, const SerializeContext::ClassElement* genericClassElement)
|
||||
{
|
||||
@@ -169,7 +169,7 @@ namespace AZ
|
||||
elementTypeId = genericClassElement->m_typeId;
|
||||
if (genericClassElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
|
||||
{
|
||||
flags = Flags::ResolvePointer;
|
||||
flags = ContinuationFlags::ResolvePointer;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -208,22 +208,28 @@ namespace AZ
|
||||
// BaseJsonSerializer
|
||||
//
|
||||
|
||||
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoading(void* object, const Uuid& typeId, const rapidjson::Value& value,
|
||||
JsonDeserializerContext& context, Flags flags)
|
||||
BaseJsonSerializer::OperationFlags BaseJsonSerializer::GetOperationsFlags() const
|
||||
{
|
||||
return flags & Flags::ResolvePointer ?
|
||||
JsonDeserializer::LoadToPointer(object, typeId, value, context) :
|
||||
JsonDeserializer::Load(object, typeId, value, context);
|
||||
return OperationFlags::None;
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoring(rapidjson::Value& output, const void* object,
|
||||
const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context, Flags flags)
|
||||
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoading(
|
||||
void* object, const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context, ContinuationFlags flags)
|
||||
{
|
||||
return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer
|
||||
? JsonDeserializer::LoadToPointer(object, typeId, value, context)
|
||||
: JsonDeserializer::Load(object, typeId, value, context);
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoring(
|
||||
rapidjson::Value& output, const void* object, const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context,
|
||||
ContinuationFlags flags)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
if (flags & Flags::ReplaceDefault && !context.ShouldKeepDefaults())
|
||||
if ((flags & ContinuationFlags::ReplaceDefault) == ContinuationFlags::ReplaceDefault && !context.ShouldKeepDefaults())
|
||||
{
|
||||
if (flags & Flags::ResolvePointer)
|
||||
if ((flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer)
|
||||
{
|
||||
return JsonSerializer::StoreFromPointer(output, object, nullptr, typeId, context);
|
||||
}
|
||||
@@ -248,7 +254,7 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
return flags & Flags::ResolvePointer ?
|
||||
return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer ?
|
||||
JsonSerializer::StoreFromPointer(output, object, defaultObject, typeId, context) :
|
||||
JsonSerializer::Store(output, object, defaultObject, typeId, context);
|
||||
}
|
||||
@@ -265,8 +271,9 @@ namespace AZ
|
||||
return JsonSerializer::StoreTypeName(output, typeId, context);
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoadingFromJsonObjectField(void* object, const Uuid& typeId, const rapidjson::Value& value,
|
||||
rapidjson::Value::StringRefType memberName, JsonDeserializerContext& context, Flags flags)
|
||||
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoadingFromJsonObjectField(
|
||||
void* object, const Uuid& typeId, const rapidjson::Value& value, rapidjson::Value::StringRefType memberName,
|
||||
JsonDeserializerContext& context, ContinuationFlags flags)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
@@ -291,7 +298,7 @@ namespace AZ
|
||||
|
||||
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoringToJsonObjectField(rapidjson::Value& output,
|
||||
rapidjson::Value::StringRefType newMemberName, const void* object, const void* defaultObject,
|
||||
const Uuid& typeId, JsonSerializerContext& context, Flags flags)
|
||||
const Uuid& typeId, JsonSerializerContext& context, ContinuationFlags flags)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
|
||||
@@ -161,13 +161,19 @@ namespace AZ
|
||||
public:
|
||||
AZ_RTTI(BaseJsonSerializer, "{7291FFDC-D339-40B5-BB26-EA067A327B21}");
|
||||
|
||||
enum Flags
|
||||
enum class ContinuationFlags
|
||||
{
|
||||
None = 0, //! No extra flags.
|
||||
None = 0, //! No extra flags.
|
||||
ResolvePointer = 1 << 0, //! The pointer passed in contains a pointer. The (de)serializer will attempt to resolve to an instance.
|
||||
ReplaceDefault = 1 << 1 //! The default value provided for storing will be replaced with a newly created one.
|
||||
};
|
||||
|
||||
enum class OperationFlags
|
||||
{
|
||||
None = 0, //! No flags that control how the custom json serializer is used.
|
||||
ManualDefault = 1 << 0 //! Even if an (explicit) default is found the custom json serializer will still be called.
|
||||
};
|
||||
|
||||
virtual ~BaseJsonSerializer() = default;
|
||||
|
||||
//! Transforms the data from the rapidjson Value to outputValue, if the conversion is possible and supported.
|
||||
@@ -180,6 +186,9 @@ namespace AZ
|
||||
virtual JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
|
||||
const Uuid& valueTypeId, JsonSerializerContext& context) = 0;
|
||||
|
||||
//! Returns the operation flags which tells the Json Serialization how this custom json serializer can be used.
|
||||
virtual OperationFlags GetOperationsFlags() const;
|
||||
|
||||
protected:
|
||||
//! Continues loading of a (sub)value. Use this function to load member variables for instance. This is more optimal than
|
||||
//! directly calling the json serialization.
|
||||
@@ -187,8 +196,9 @@ namespace AZ
|
||||
//! @param typeId Type id of the object passed in.
|
||||
//! @param value The value in the JSON document where the deserializer will start reading data from.
|
||||
//! @param context The context used during deserialization. Use the value passed in from Load.
|
||||
JsonSerializationResult::ResultCode ContinueLoading(void* object, const Uuid& typeId, const rapidjson::Value& value,
|
||||
JsonDeserializerContext& context, Flags flags = Flags::None);
|
||||
JsonSerializationResult::ResultCode ContinueLoading(
|
||||
void* object, const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context,
|
||||
ContinuationFlags flags = ContinuationFlags::None);
|
||||
|
||||
//! Continues storing of a (sub)value. Use this function to store member variables for instance. This is more optimal than
|
||||
//! directly calling the json serialization.
|
||||
@@ -200,8 +210,9 @@ namespace AZ
|
||||
//! the settings.
|
||||
//! @param typeId The type id of the object and default object.
|
||||
//! @param context The context used during serialization. Use the value passed in from Store.
|
||||
JsonSerializationResult::ResultCode ContinueStoring(rapidjson::Value& output, const void* object, const void* defaultObject,
|
||||
const Uuid& typeId, JsonSerializerContext& context, Flags flags = Flags::None);
|
||||
JsonSerializationResult::ResultCode ContinueStoring(
|
||||
rapidjson::Value& output, const void* object, const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context,
|
||||
ContinuationFlags flags = ContinuationFlags::None);
|
||||
|
||||
//! Retrieves the type id from a json object or json string.
|
||||
//! @param typeId The retrieved type id.
|
||||
@@ -222,12 +233,14 @@ namespace AZ
|
||||
const Uuid& typeId, JsonSerializerContext& context);
|
||||
|
||||
//! Helper function similar to ContinueLoading, but loads the data as a member of 'value' rather than 'value' itself, if it exists.
|
||||
JsonSerializationResult::ResultCode ContinueLoadingFromJsonObjectField(void* object, const Uuid& typeId, const rapidjson::Value& value,
|
||||
rapidjson::Value::StringRefType memberName, JsonDeserializerContext& context, Flags flags = Flags::None);
|
||||
JsonSerializationResult::ResultCode ContinueLoadingFromJsonObjectField(
|
||||
void* object, const Uuid& typeId, const rapidjson::Value& value, rapidjson::Value::StringRefType memberName,
|
||||
JsonDeserializerContext& context, ContinuationFlags flags = ContinuationFlags::None);
|
||||
|
||||
//! Helper function similar to ContinueStoring, but stores the data as a member of 'output' rather than overwriting 'output'.
|
||||
JsonSerializationResult::ResultCode ContinueStoringToJsonObjectField(rapidjson::Value& output, rapidjson::Value::StringRefType newMemberName,
|
||||
const void* object, const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context, Flags flags = Flags::None);
|
||||
JsonSerializationResult::ResultCode ContinueStoringToJsonObjectField(
|
||||
rapidjson::Value& output, rapidjson::Value::StringRefType newMemberName, const void* object, const void* defaultObject,
|
||||
const Uuid& typeId, JsonSerializerContext& context, ContinuationFlags flags = ContinuationFlags::None);
|
||||
|
||||
//! Checks if a value is an explicit default. This useful for containers where not storing anything as a default would mean
|
||||
//! a slot wouldn't be used so something has to be added to represent the fully default target.
|
||||
@@ -238,6 +251,7 @@ namespace AZ
|
||||
rapidjson::Value GetExplicitDefault();
|
||||
};
|
||||
|
||||
AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::BaseJsonSerializer::Flags)
|
||||
AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::BaseJsonSerializer::ContinuationFlags)
|
||||
AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::BaseJsonSerializer::OperationFlags)
|
||||
|
||||
} // namespace AZ
|
||||
|
||||
@@ -75,9 +75,10 @@ namespace AZ
|
||||
auto elementCallback = [this, &array, &retVal, &index, &context]
|
||||
(void* elementPtr, const Uuid& elementId, const SerializeContext::ClassData*, const SerializeContext::ClassElement* classElement)
|
||||
{
|
||||
Flags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ?
|
||||
Flags::ResolvePointer : Flags::None;
|
||||
flags |= Flags::ReplaceDefault;
|
||||
ContinuationFlags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
|
||||
? ContinuationFlags::ResolvePointer
|
||||
: ContinuationFlags::None;
|
||||
flags |= ContinuationFlags::ReplaceDefault;
|
||||
|
||||
ScopedContextPath subPath(context, index);
|
||||
index++;
|
||||
@@ -161,8 +162,9 @@ namespace AZ
|
||||
container->EnumTypes(typeEnumCallback);
|
||||
AZ_Assert(classElement, "No class element found for the type in the basic container.");
|
||||
|
||||
Flags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ?
|
||||
Flags::ResolvePointer : Flags::None;
|
||||
ContinuationFlags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
|
||||
? ContinuationFlags::ResolvePointer
|
||||
: ContinuationFlags::None;
|
||||
|
||||
const size_t capacity = container->IsFixedCapacity() ? container->Capacity(outputValue) : std::numeric_limits<size_t>::max();
|
||||
|
||||
|
||||
@@ -22,6 +22,19 @@
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
JsonSerializationResult::ResultCode JsonDeserializer::DeserializerDefaultCheck(BaseJsonSerializer* serializer, void* object,
|
||||
const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context)
|
||||
{
|
||||
using namespace AZ::JsonSerializationResult;
|
||||
|
||||
bool isExplicitDefault = IsExplicitDefault(value);
|
||||
bool manuallyDefaults = (serializer->GetOperationsFlags() & BaseJsonSerializer::OperationFlags::ManualDefault) ==
|
||||
BaseJsonSerializer::OperationFlags::ManualDefault;
|
||||
return !isExplicitDefault || (isExplicitDefault && manuallyDefaults)
|
||||
? serializer->Load(object, typeId, value, context)
|
||||
: context.Report(Tasks::ReadField, Outcomes::DefaultsUsed, "Value has an explicit default.");
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode JsonDeserializer::Load(void* object, const Uuid& typeId, const rapidjson::Value& value,
|
||||
JsonDeserializerContext& context)
|
||||
{
|
||||
@@ -33,17 +46,12 @@ namespace AZ
|
||||
"Target object for Json Serialization is pointing to nothing during loading.");
|
||||
}
|
||||
|
||||
if (IsExplicitDefault(value))
|
||||
{
|
||||
return context.Report(Tasks::ReadField, Outcomes::DefaultsUsed, "Value has an explicit default.");
|
||||
}
|
||||
|
||||
BaseJsonSerializer* serializer = context.GetRegistrationContext()->GetSerializerForType(typeId);
|
||||
if (serializer)
|
||||
{
|
||||
return serializer->Load(object, typeId, value, context);
|
||||
return DeserializerDefaultCheck(serializer, object, typeId, value, context);
|
||||
}
|
||||
|
||||
|
||||
const SerializeContext::ClassData* classData = context.GetSerializeContext()->FindClassData(typeId);
|
||||
if (!classData)
|
||||
{
|
||||
@@ -56,9 +64,14 @@ namespace AZ
|
||||
serializer = context.GetRegistrationContext()->GetSerializerForType(classData->m_azRtti->GetGenericTypeId());
|
||||
if (serializer)
|
||||
{
|
||||
return serializer->Load(object, typeId, value, context);
|
||||
return DeserializerDefaultCheck(serializer, object, typeId, value, context);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsExplicitDefault(value))
|
||||
{
|
||||
return context.Report(Tasks::ReadField, Outcomes::DefaultsUsed, "Value has an explicit default.");
|
||||
}
|
||||
|
||||
if (classData->m_azRtti && (classData->m_azRtti->GetTypeTraits() & AZ::TypeTraits::is_enum) == AZ::TypeTraits::is_enum)
|
||||
{
|
||||
|
||||
@@ -113,5 +113,13 @@ namespace AZ
|
||||
|
||||
//! Checks if a value is an explicit default. This means the value is an object with no members.
|
||||
static bool IsExplicitDefault(const rapidjson::Value& value);
|
||||
|
||||
private:
|
||||
static JsonSerializationResult::ResultCode DeserializerDefaultCheck(
|
||||
BaseJsonSerializer* serializer,
|
||||
void* object,
|
||||
const Uuid& typeId,
|
||||
const rapidjson::Value& value,
|
||||
JsonDeserializerContext& context);
|
||||
};
|
||||
} // namespace AZ
|
||||
|
||||
@@ -215,10 +215,10 @@ namespace AZ
|
||||
// Load key
|
||||
void* keyAddress = pairContainer->GetElementByIndex(address, pairElement, 0);
|
||||
AZ_Assert(keyAddress, "Element reserved for associative container, but unable to retrieve address of the key.");
|
||||
Flags keyLoadFlags = Flags::None;
|
||||
ContinuationFlags keyLoadFlags = ContinuationFlags::None;
|
||||
if (keyElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
|
||||
{
|
||||
keyLoadFlags = Flags::ResolvePointer;
|
||||
keyLoadFlags = ContinuationFlags::ResolvePointer;
|
||||
*reinterpret_cast<void**>(keyAddress) = nullptr;
|
||||
}
|
||||
JSR::ResultCode keyResult = ContinueLoading(keyAddress, keyElement->m_typeId, key, context, keyLoadFlags);
|
||||
@@ -231,10 +231,10 @@ namespace AZ
|
||||
// Load value
|
||||
void* valueAddress = pairContainer->GetElementByIndex(address, pairElement, 1);
|
||||
AZ_Assert(valueAddress, "Element reserved for associative container, but unable to retrieve address of the value.");
|
||||
Flags valueLoadFlags = Flags::None;
|
||||
ContinuationFlags valueLoadFlags = ContinuationFlags::None;
|
||||
if (valueElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
|
||||
{
|
||||
valueLoadFlags = Flags::ResolvePointer;
|
||||
valueLoadFlags = ContinuationFlags::ResolvePointer;
|
||||
*reinterpret_cast<void**>(valueAddress) = nullptr;
|
||||
}
|
||||
JSR::ResultCode valueResult = ContinueLoading(valueAddress, valueElement->m_typeId, value, context, valueLoadFlags);
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace AZ
|
||||
{
|
||||
// If the target type is the same as the type already stored in the smart pointer than no new
|
||||
// instance is created and the existing instance will be updated with the data in the json document.
|
||||
result = ContinueLoading(instance, elementClassId, inputValue, context, Flags::ResolvePointer);
|
||||
result = ContinueLoading(instance, elementClassId, inputValue, context, ContinuationFlags::ResolvePointer);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,7 @@ namespace AZ
|
||||
// the wrong address. In these cases explicitly reset the smart pointer. This will erase the existing
|
||||
// data but that's fine as it's not being used.
|
||||
void* element = nullptr;
|
||||
result = ContinueLoading(&element, elementClassId, inputValue, context, Flags::ResolvePointer);
|
||||
result = ContinueLoading(&element, elementClassId, inputValue, context, ContinuationFlags::ResolvePointer);
|
||||
if (result.GetProcessing() != JSR::Processing::Halted && result.GetProcessing() != JSR::Processing::Altered)
|
||||
{
|
||||
void* elementPtr = container->ReserveElement(instance, nullptr);
|
||||
@@ -155,8 +155,14 @@ namespace AZ
|
||||
container->EnumElements(const_cast<void*>(defaultValue), defaultInputCallback);
|
||||
}
|
||||
|
||||
JSR::ResultCode result = ContinueStoring(outputValue, inputValue, defaultValue, inputPtrType, context, Flags::ResolvePointer);
|
||||
JSR::ResultCode result =
|
||||
ContinueStoring(outputValue, inputValue, defaultValue, inputPtrType, context, ContinuationFlags::ResolvePointer);
|
||||
return context.Report(result, result.GetProcessing() != JSR::Processing::Halted ?
|
||||
"Successfully processed smart pointer." : "A problem occurred while processing a smart pointer.");
|
||||
}
|
||||
|
||||
BaseJsonSerializer::OperationFlags JsonSmartPointerSerializer::GetOperationsFlags() const
|
||||
{
|
||||
return OperationFlags::ManualDefault;
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
@@ -28,5 +28,7 @@ namespace AZ
|
||||
JsonDeserializerContext& context) override;
|
||||
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
|
||||
const Uuid& valueTypeId, JsonSerializerContext& context) override;
|
||||
|
||||
OperationFlags GetOperationsFlags() const override;
|
||||
};
|
||||
} // namespace AZ
|
||||
|
||||
@@ -99,8 +99,9 @@ namespace AZ
|
||||
|
||||
ScopedContextPath subPath(context, i);
|
||||
|
||||
Flags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ?
|
||||
Flags::ResolvePointer : Flags::None;
|
||||
ContinuationFlags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
|
||||
? ContinuationFlags::ResolvePointer
|
||||
: ContinuationFlags::None;
|
||||
|
||||
JSR::ResultCode result = ContinueStoring(elementValues[i], elementAddress, defaultElementAddress,
|
||||
classElements[i]->m_typeId, context, flags);
|
||||
@@ -179,8 +180,9 @@ namespace AZ
|
||||
void* elementAddress = container->GetElementByIndex(outputValue, nullptr, i);
|
||||
AZ_Assert(elementAddress, "Address of AZStd::pair or AZStd::tuple element %zu could not be retrieved.", i);
|
||||
|
||||
Flags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ?
|
||||
Flags::ResolvePointer : Flags::None;
|
||||
ContinuationFlags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
|
||||
? ContinuationFlags::ResolvePointer
|
||||
: ContinuationFlags::None;
|
||||
|
||||
while (arrayIndex < inputValue.Size())
|
||||
{
|
||||
|
||||
@@ -104,6 +104,11 @@ namespace JsonSerializationTests
|
||||
AZ::AllocatorInstance<AZ::PoolAllocator>::Destroy();
|
||||
}
|
||||
|
||||
void Reflect(AZStd::unique_ptr<AZ::SerializeContext>& context) override
|
||||
{
|
||||
context->RegisterGenericType<Asset>();
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<AZ::BaseJsonSerializer> CreateSerializer() override
|
||||
{
|
||||
return AZStd::make_shared<AZ::Data::AssetJsonSerializer>();
|
||||
|
||||
@@ -119,7 +119,8 @@ namespace JsonSerializationTests
|
||||
int value = 0;
|
||||
int* ptrValue = &value;
|
||||
|
||||
ResultCode result = ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, Flags::ResolvePointer);
|
||||
ResultCode result =
|
||||
ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, ContinuationFlags::ResolvePointer);
|
||||
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
ASSERT_NE(nullptr, ptrValue);
|
||||
@@ -134,7 +135,8 @@ namespace JsonSerializationTests
|
||||
json.Set(42);
|
||||
int* ptrValue = nullptr;
|
||||
|
||||
ResultCode result = ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, Flags::ResolvePointer);
|
||||
ResultCode result =
|
||||
ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, ContinuationFlags::ResolvePointer);
|
||||
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
ASSERT_NE(nullptr, ptrValue);
|
||||
@@ -150,7 +152,8 @@ namespace JsonSerializationTests
|
||||
rapidjson::Value json(rapidjson::kObjectType);
|
||||
int* ptrValue = nullptr;
|
||||
|
||||
ResultCode result = ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, Flags::ResolvePointer);
|
||||
ResultCode result =
|
||||
ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, ContinuationFlags::ResolvePointer);
|
||||
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
ASSERT_NE(nullptr, ptrValue);
|
||||
@@ -165,7 +168,8 @@ namespace JsonSerializationTests
|
||||
rapidjson::Value json(rapidjson::kNullType);
|
||||
int* ptrValue = reinterpret_cast<int*>(azmalloc(sizeof(int), alignof(int), AZ::SystemAllocator));
|
||||
|
||||
ResultCode result = ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, Flags::ResolvePointer);
|
||||
ResultCode result =
|
||||
ContinueLoading(&ptrValue, azrtti_typeid<int>(), json, *m_jsonDeserializationContext, ContinuationFlags::ResolvePointer);
|
||||
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
ASSERT_EQ(nullptr, ptrValue);
|
||||
@@ -194,8 +198,8 @@ namespace JsonSerializationTests
|
||||
int value = 42;
|
||||
int* ptrValue = &value;
|
||||
|
||||
ResultCode result = ContinueStoring(*m_jsonDocument, &ptrValue, nullptr, azrtti_typeid<int>(), *m_jsonSerializationContext,
|
||||
Flags::ResolvePointer);
|
||||
ResultCode result = ContinueStoring(
|
||||
*m_jsonDocument, &ptrValue, nullptr, azrtti_typeid<int>(), *m_jsonSerializationContext, ContinuationFlags::ResolvePointer);
|
||||
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
Expect_DocStrEq("42");
|
||||
@@ -210,8 +214,9 @@ namespace JsonSerializationTests
|
||||
int value2 = 42;
|
||||
int* defaultPtrValue = &value2;
|
||||
|
||||
ResultCode result =
|
||||
ContinueStoring(*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid<int>(), *m_jsonSerializationContext, Flags::ResolvePointer);
|
||||
ResultCode result = ContinueStoring(
|
||||
*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid<int>(), *m_jsonSerializationContext,
|
||||
ContinuationFlags::ResolvePointer);
|
||||
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
Expect_DocStrEq("{}");
|
||||
@@ -224,7 +229,7 @@ namespace JsonSerializationTests
|
||||
int* ptrValue = nullptr;
|
||||
|
||||
ResultCode result = ContinueStoring(
|
||||
*m_jsonDocument, &ptrValue, nullptr, azrtti_typeid<int>(), *m_jsonSerializationContext, Flags::ResolvePointer);
|
||||
*m_jsonDocument, &ptrValue, nullptr, azrtti_typeid<int>(), *m_jsonSerializationContext, ContinuationFlags::ResolvePointer);
|
||||
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
Expect_DocStrEq("null");
|
||||
@@ -238,8 +243,9 @@ namespace JsonSerializationTests
|
||||
int value2 = 42;
|
||||
int* defaultPtrValue = &value2;
|
||||
|
||||
ResultCode result =
|
||||
ContinueStoring(*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid<int>(), *m_jsonSerializationContext, Flags::ResolvePointer);
|
||||
ResultCode result = ContinueStoring(
|
||||
*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid<int>(), *m_jsonSerializationContext,
|
||||
ContinuationFlags::ResolvePointer);
|
||||
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
Expect_DocStrEq("null");
|
||||
@@ -252,8 +258,9 @@ namespace JsonSerializationTests
|
||||
int* ptrValue = nullptr;
|
||||
int* defaultPtrValue = nullptr;
|
||||
|
||||
ResultCode result =
|
||||
ContinueStoring(*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid<int>(), *m_jsonSerializationContext, Flags::ResolvePointer);
|
||||
ResultCode result = ContinueStoring(
|
||||
*m_jsonDocument, &ptrValue, &defaultPtrValue, azrtti_typeid<int>(), *m_jsonSerializationContext,
|
||||
ContinuationFlags::ResolvePointer);
|
||||
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
Expect_DocStrEq("null");
|
||||
@@ -265,8 +272,8 @@ namespace JsonSerializationTests
|
||||
|
||||
int value = 42;
|
||||
|
||||
ResultCode result = ContinueStoring(*m_jsonDocument, &value, nullptr, azrtti_typeid<int>(), *m_jsonSerializationContext,
|
||||
Flags::ReplaceDefault);
|
||||
ResultCode result = ContinueStoring(
|
||||
*m_jsonDocument, &value, nullptr, azrtti_typeid<int>(), *m_jsonSerializationContext, ContinuationFlags::ReplaceDefault);
|
||||
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
Expect_DocStrEq("42");
|
||||
@@ -280,7 +287,7 @@ namespace JsonSerializationTests
|
||||
int* ptrValue = &value;
|
||||
|
||||
ResultCode result = ContinueStoring(*m_jsonDocument, &ptrValue, nullptr, azrtti_typeid<int>(), *m_jsonSerializationContext,
|
||||
Flags::ResolvePointer | Flags::ReplaceDefault);
|
||||
ContinuationFlags::ResolvePointer | ContinuationFlags::ReplaceDefault);
|
||||
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
Expect_DocStrEq("42");
|
||||
@@ -293,8 +300,8 @@ namespace JsonSerializationTests
|
||||
int value = 42;
|
||||
AZ::Uuid unknownType("{09AE3CEC-EBFC-41EC-A7F6-949721521716}");
|
||||
|
||||
ResultCode result = ContinueStoring(*m_jsonDocument, &value, nullptr, unknownType, *m_jsonSerializationContext,
|
||||
Flags::ReplaceDefault);
|
||||
ResultCode result =
|
||||
ContinueStoring(*m_jsonDocument, &value, nullptr, unknownType, *m_jsonSerializationContext, ContinuationFlags::ReplaceDefault);
|
||||
|
||||
EXPECT_EQ(Processing::Halted, result.GetProcessing());
|
||||
}
|
||||
|
||||
@@ -90,9 +90,14 @@ namespace JsonSerializationTests
|
||||
virtual ~JsonSerializerConformityTestDescriptor() = default;
|
||||
|
||||
virtual AZStd::shared_ptr<AZ::BaseJsonSerializer> CreateSerializer() = 0;
|
||||
|
||||
|
||||
//! Create an instance of the target type with all values set to default.
|
||||
virtual AZStd::shared_ptr<T> CreateDefaultInstance() = 0;
|
||||
//! Create an instance of the target type that constructed with default constructor.
|
||||
//! This will be the same instance that Json Serialization creates for dynamic types. Typically it's the same
|
||||
//! as from CreateDefaultInstance(), except of types, such as pointers, that need to do minimal (de)serialization
|
||||
//! to initialize an object.
|
||||
virtual AZStd::shared_ptr<T> CreateDefaultConstructedInstance() { return CreateDefaultInstance(); }
|
||||
//! Create an instance of the target type with some values set and some kept on defaults.
|
||||
//! If the target type doesn't support partial specialization this can be ignored and
|
||||
//! tests for partial support will be skipped.
|
||||
@@ -316,10 +321,10 @@ namespace JsonSerializationTests
|
||||
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
|
||||
|
||||
auto serializer = this->m_description.CreateSerializer();
|
||||
auto instance = this->m_description.CreateDefaultInstance();
|
||||
auto instance = this->m_description.CreateDefaultConstructedInstance();
|
||||
auto original = this->m_description.CreateDefaultInstance();
|
||||
|
||||
ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*original),
|
||||
ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*instance),
|
||||
*this->m_jsonDocument, *this->m_jsonDeserializationContext);
|
||||
|
||||
if (this->m_features.m_mandatoryFields.empty())
|
||||
@@ -339,6 +344,42 @@ namespace JsonSerializationTests
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeEmptyObjectThroughMainLoad_SucceedsAndObjectMatchesDefaults)
|
||||
{
|
||||
using namespace AZ::JsonSerializationResult;
|
||||
|
||||
if (this->m_features.SupportsJsonType(rapidjson::kObjectType))
|
||||
{
|
||||
this->m_jsonDocument->Parse("{}");
|
||||
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
|
||||
|
||||
auto serializer = this->m_description.CreateSerializer();
|
||||
auto instance = this->m_description.CreateDefaultConstructedInstance();
|
||||
auto original = this->m_description.CreateDefaultInstance();
|
||||
|
||||
AZ::JsonDeserializerSettings settings;
|
||||
settings.m_serializeContext = this->m_jsonDeserializationContext->GetSerializeContext();
|
||||
settings.m_registrationContext = this->m_jsonDeserializationContext->GetRegistrationContext();
|
||||
ResultCode result = AZ::JsonSerialization::Load(
|
||||
instance.get(), azrtti_typeid(*instance), *this->m_jsonDocument, settings);
|
||||
|
||||
if (this->m_features.m_mandatoryFields.empty())
|
||||
{
|
||||
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_EQ(Outcomes::Unsupported, result.GetOutcome());
|
||||
bool validProcessing =
|
||||
result.GetProcessing() == Processing::Altered ||
|
||||
result.GetProcessing() == Processing::PartialAlter;
|
||||
EXPECT_TRUE(validProcessing);
|
||||
}
|
||||
EXPECT_TRUE(this->m_description.AreEqual(*original, *instance));
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeEmptyArray_SucceedsAndObjectMatchesDefaults)
|
||||
{
|
||||
using namespace AZ::JsonSerializationResult;
|
||||
@@ -349,7 +390,7 @@ namespace JsonSerializationTests
|
||||
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
|
||||
|
||||
auto serializer = this->m_description.CreateSerializer();
|
||||
auto instance = this->m_description.CreateDefaultInstance();
|
||||
auto instance = this->m_description.CreateDefaultConstructedInstance();
|
||||
auto original = this->m_description.CreateDefaultInstance();
|
||||
|
||||
this->m_deserializationSettings->m_clearContainers = false;
|
||||
@@ -384,7 +425,7 @@ namespace JsonSerializationTests
|
||||
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
|
||||
|
||||
auto serializer = this->m_description.CreateSerializer();
|
||||
auto instance = this->m_description.CreateDefaultInstance();
|
||||
auto instance = this->m_description.CreateDefaultConstructedInstance();
|
||||
auto original = this->m_description.CreateDefaultInstance();
|
||||
|
||||
this->m_deserializationSettings->m_clearContainers = true;
|
||||
@@ -488,7 +529,7 @@ namespace JsonSerializationTests
|
||||
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
|
||||
|
||||
auto serializer = this->m_description.CreateSerializer();
|
||||
auto instance = this->m_description.CreateDefaultInstance();
|
||||
auto instance = this->m_description.CreateDefaultConstructedInstance();
|
||||
auto compare = this->m_description.CreateFullySetInstance();
|
||||
|
||||
ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*instance),
|
||||
@@ -499,6 +540,28 @@ namespace JsonSerializationTests
|
||||
EXPECT_TRUE(this->m_description.AreEqual(*instance, *compare));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeFullySetInstanceThroughMainLoad_SucceedsAndObjectMatchesFullySetInstance)
|
||||
{
|
||||
using namespace AZ::JsonSerializationResult;
|
||||
|
||||
AZStd::string_view json = this->m_description.GetJsonFor_Load_DeserializeFullySetInstance();
|
||||
this->m_jsonDocument->Parse(json.data());
|
||||
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
|
||||
|
||||
auto serializer = this->m_description.CreateSerializer();
|
||||
auto instance = this->m_description.CreateDefaultConstructedInstance();
|
||||
auto compare = this->m_description.CreateFullySetInstance();
|
||||
|
||||
AZ::JsonDeserializerSettings settings;
|
||||
settings.m_serializeContext = this->m_jsonDeserializationContext->GetSerializeContext();
|
||||
settings.m_registrationContext = this->m_jsonDeserializationContext->GetRegistrationContext();
|
||||
ResultCode result = AZ::JsonSerialization::Load(instance.get(), azrtti_typeid(*instance), *this->m_jsonDocument, settings);
|
||||
|
||||
EXPECT_EQ(Outcomes::Success, result.GetOutcome());
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
EXPECT_TRUE(this->m_description.AreEqual(*instance, *compare));
|
||||
}
|
||||
|
||||
TYPED_TEST_P(JsonSerializerConformityTests, Load_DeserializeWithMissingMandatoryField_LoadFailedAndUnsupportedReported)
|
||||
{
|
||||
using namespace AZ::JsonSerializationResult;
|
||||
@@ -518,7 +581,7 @@ namespace JsonSerializationTests
|
||||
ASSERT_NE(this->m_jsonDocument->MemberEnd(), memberToErase);
|
||||
this->m_jsonDocument->RemoveMember(memberToErase);
|
||||
|
||||
auto instance = this->m_description.CreateDefaultInstance();
|
||||
auto instance = this->m_description.CreateDefaultConstructedInstance();
|
||||
|
||||
ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*instance),
|
||||
*this->m_jsonDocument, *this->m_jsonDeserializationContext);
|
||||
@@ -546,7 +609,7 @@ namespace JsonSerializationTests
|
||||
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
|
||||
|
||||
auto serializer = this->m_description.CreateSerializer();
|
||||
auto instance = this->m_description.CreateDefaultInstance();
|
||||
auto instance = this->m_description.CreateDefaultConstructedInstance();
|
||||
auto compare = this->m_description.CreatePartialDefaultInstance();
|
||||
ASSERT_NE(nullptr, compare);
|
||||
|
||||
@@ -567,7 +630,7 @@ namespace JsonSerializationTests
|
||||
ASSERT_FALSE(this->m_jsonDocument->HasParseError());
|
||||
|
||||
auto serializer = this->m_description.CreateSerializer();
|
||||
auto instance = this->m_description.CreateDefaultInstance();
|
||||
auto instance = this->m_description.CreateDefaultConstructedInstance();
|
||||
|
||||
AZ::ScopedContextReporter reporter(*this->m_jsonDeserializationContext,
|
||||
[](AZStd::string_view message, ResultCode result, AZStd::string_view path) -> ResultCode
|
||||
@@ -604,7 +667,7 @@ namespace JsonSerializationTests
|
||||
}
|
||||
|
||||
auto serializer = this->m_description.CreateSerializer();
|
||||
auto instance = this->m_description.CreateDefaultInstance();
|
||||
auto instance = this->m_description.CreateDefaultConstructedInstance();
|
||||
auto compare = this->m_description.CreateFullySetInstance();
|
||||
|
||||
ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*instance),
|
||||
@@ -635,7 +698,7 @@ namespace JsonSerializationTests
|
||||
}
|
||||
|
||||
auto serializer = this->m_description.CreateSerializer();
|
||||
auto instance = this->m_description.CreateDefaultInstance();
|
||||
auto instance = this->m_description.CreateDefaultConstructedInstance();
|
||||
|
||||
ResultCode result = serializer->Load(instance.get(), azrtti_typeid(*instance),
|
||||
*this->m_jsonDocument, *this->m_jsonDeserializationContext);
|
||||
@@ -693,6 +756,36 @@ namespace JsonSerializationTests
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST_P(JsonSerializerConformityTests, Store_SerializeDefaultInstanceThroughMainStore_EmptyJsonReturned)
|
||||
{
|
||||
using namespace AZ::JsonSerializationResult;
|
||||
|
||||
auto serializer = this->m_description.CreateSerializer();
|
||||
auto instance = this->m_description.CreateDefaultInstance();
|
||||
rapidjson::Value convertedValue = this->CreateExplicitDefault();
|
||||
|
||||
AZ::JsonSerializerSettings settings;
|
||||
settings.m_serializeContext = this->m_jsonDeserializationContext->GetSerializeContext();
|
||||
settings.m_registrationContext = this->m_jsonDeserializationContext->GetRegistrationContext();
|
||||
ResultCode result = AZ::JsonSerialization::Store(
|
||||
convertedValue, this->m_jsonDocument->GetAllocator(), instance.get(), instance.get(), azrtti_typeid(*instance), settings);
|
||||
|
||||
EXPECT_EQ(Processing::Completed, result.GetProcessing());
|
||||
if (convertedValue.IsObject() && !this->m_features.m_mandatoryFields.empty())
|
||||
{
|
||||
ASSERT_EQ(convertedValue.MemberCount(), this->m_features.m_mandatoryFields.size());
|
||||
for (const AZStd::string& mandatoryField : this->m_features.m_mandatoryFields)
|
||||
{
|
||||
EXPECT_NE(convertedValue.MemberEnd(), convertedValue.FindMember(mandatoryField.c_str()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_EQ(Outcomes::DefaultsUsed, result.GetOutcome());
|
||||
this->Expect_ExplicitDefault(convertedValue);
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST_P(JsonSerializerConformityTests, Store_SerializeWithDefaultsKept_FullyWrittenJson)
|
||||
{
|
||||
using namespace AZ::JsonSerializationResult;
|
||||
@@ -924,6 +1017,20 @@ namespace JsonSerializationTests
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST_P(JsonSerializerConformityTests, GetOperationsFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared)
|
||||
{
|
||||
if (this->m_features.SupportsJsonType(rapidjson::kObjectType))
|
||||
{
|
||||
if (!this->m_features.m_mandatoryFields.empty())
|
||||
{
|
||||
auto serializer = this->m_description.CreateSerializer();
|
||||
bool manuallyHandlesDefaults = (serializer->GetOperationsFlags() & AZ::BaseJsonSerializer::OperationFlags::ManualDefault) ==
|
||||
AZ::BaseJsonSerializer::OperationFlags::ManualDefault;
|
||||
EXPECT_TRUE(manuallyHandlesDefaults);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_CASE_P(JsonSerializerConformityTests,
|
||||
Registration_SerializerIsRegisteredWithContext_SerializerFound,
|
||||
|
||||
@@ -934,14 +1041,16 @@ namespace JsonSerializationTests
|
||||
Load_InvalidTypeOfArrayType_ReturnsUnsupported,
|
||||
Load_InvalidTypeOfStringType_ReturnsUnsupported,
|
||||
Load_InvalidTypeOfNumberType_ReturnsUnsupported,
|
||||
|
||||
|
||||
Load_DeserializeUnreflectedType_ReturnsUnsupported,
|
||||
Load_DeserializeEmptyObject_SucceedsAndObjectMatchesDefaults,
|
||||
Load_DeserializeEmptyObjectThroughMainLoad_SucceedsAndObjectMatchesDefaults,
|
||||
Load_DeserializeEmptyArray_SucceedsAndObjectMatchesDefaults,
|
||||
Load_DeserializeEmptyArrayWithClearEnabled_SucceedsAndObjectMatchesDefaults,
|
||||
Load_DeserializeEmptyArrayWithClearedTarget_SucceedsAndObjectMatchesDefaults,
|
||||
Load_InterruptClearingTarget_ContainerIsNotCleared,
|
||||
Load_DeserializeFullySetInstance_SucceedsAndObjectMatchesFullySetInstance,
|
||||
Load_DeserializeFullySetInstanceThroughMainLoad_SucceedsAndObjectMatchesFullySetInstance,
|
||||
Load_DeserializePartialInstance_SucceedsAndObjectMatchesParialInstance,
|
||||
Load_DeserializeWithMissingMandatoryField_LoadFailedAndUnsupportedReported,
|
||||
Load_InsertAdditionalData_SucceedsAndObjectMatchesFullySetInstance,
|
||||
@@ -950,6 +1059,7 @@ namespace JsonSerializationTests
|
||||
|
||||
Store_SerializeUnreflectedType_ReturnsUnsupported,
|
||||
Store_SerializeDefaultInstance_EmptyJsonReturned,
|
||||
Store_SerializeDefaultInstanceThroughMainStore_EmptyJsonReturned,
|
||||
Store_SerializeWithDefaultsKept_FullyWrittenJson,
|
||||
Store_SerializeFullySetInstance_StoredSuccessfullyAndJsonMatches,
|
||||
Store_SerializeWithoutDefault_StoredSuccessfullyAndJsonMatches,
|
||||
@@ -957,10 +1067,12 @@ namespace JsonSerializationTests
|
||||
Store_SerializePartialInstance_StoredSuccessfullyAndJsonMatches,
|
||||
Store_SerializeEmptyArray_StoredSuccessfullyAndJsonMatches,
|
||||
Store_HaltedThroughCallback_StoreFailsAndHaltReported,
|
||||
|
||||
|
||||
StoreLoad_RoundTripWithPartialDefault_IdenticalInstances,
|
||||
StoreLoad_RoundTripWithFullSet_IdenticalInstances,
|
||||
StoreLoad_RoundTripWithDefaultsKept_IdenticalInstances);
|
||||
StoreLoad_RoundTripWithDefaultsKept_IdenticalInstances,
|
||||
|
||||
GetOperationsFlags_ManualDefaultSetIfNeeded_ManualDefaultOperationSetIfMandatoryFieldsAreDeclared);
|
||||
} // namespace JsonSerializationTests
|
||||
|
||||
namespace AZ
|
||||
|
||||
@@ -32,6 +32,11 @@ namespace JsonSerializationTests
|
||||
return AZStd::make_shared<AZ::JsonSmartPointerSerializer>();
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<SmartPointer> CreateDefaultConstructedInstance() override
|
||||
{
|
||||
return AZStd::make_shared<SmartPointer>();
|
||||
}
|
||||
|
||||
void Reflect(AZStd::unique_ptr<AZ::SerializeContext>& context) override
|
||||
{
|
||||
context->RegisterGenericType<SmartPointer>();
|
||||
@@ -228,13 +233,19 @@ namespace JsonSerializationTests
|
||||
public:
|
||||
using SmartPointer = typename SmartPointerSimpleDerivedClassTestDescription<T>::SmartPointer;
|
||||
|
||||
AZStd::shared_ptr<SmartPointer> CreateDefaultInstance() override
|
||||
// This test is specific for derived classes being used as a default value.
|
||||
AZStd::shared_ptr<SmartPointer> CreateDefaultConstructedInstance() override
|
||||
{
|
||||
auto result = AZStd::make_shared<SmartPointer>();
|
||||
*result = SmartPointer(aznew SimpleInheritence());
|
||||
return result;
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<SmartPointer> CreateDefaultInstance() override
|
||||
{
|
||||
return CreateDefaultConstructedInstance();
|
||||
}
|
||||
|
||||
AZStd::string_view GetJsonForPartialDefaultInstance() override
|
||||
{
|
||||
return R"(
|
||||
@@ -386,13 +397,19 @@ namespace JsonSerializationTests
|
||||
public:
|
||||
using SmartPointer = typename SmartPointerComplexDerivedClassTestDescription<T>::SmartPointer;
|
||||
|
||||
AZStd::shared_ptr<SmartPointer> CreateDefaultInstance() override
|
||||
// This test is specific for derived classes being used as a default value.
|
||||
AZStd::shared_ptr<SmartPointer> CreateDefaultConstructedInstance() override
|
||||
{
|
||||
auto result = AZStd::make_shared<SmartPointer>();
|
||||
*result = SmartPointer(aznew MultipleInheritence());
|
||||
return result;
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<SmartPointer> CreateDefaultInstance() override
|
||||
{
|
||||
return CreateDefaultConstructedInstance();
|
||||
}
|
||||
|
||||
AZStd::string_view GetJsonForPartialDefaultInstance() override
|
||||
{
|
||||
return R"(
|
||||
|
||||
@@ -48,6 +48,9 @@ namespace Physics
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext
|
||||
->RegisterGenericType<AZStd::shared_ptr<SphereShapeConfiguration>>();
|
||||
|
||||
serializeContext->Class<SphereShapeConfiguration, ShapeConfiguration>()
|
||||
->Version(1)
|
||||
->Field("Radius", &SphereShapeConfiguration::m_radius)
|
||||
@@ -76,6 +79,9 @@ namespace Physics
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext
|
||||
->RegisterGenericType<AZStd::shared_ptr<BoxShapeConfiguration>>();
|
||||
|
||||
serializeContext->Class<BoxShapeConfiguration, ShapeConfiguration>()
|
||||
->Version(1)
|
||||
->Field("Configuration", &BoxShapeConfiguration::m_dimensions)
|
||||
@@ -104,6 +110,9 @@ namespace Physics
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext
|
||||
->RegisterGenericType<AZStd::shared_ptr<CapsuleShapeConfiguration>>();
|
||||
|
||||
serializeContext->Class<CapsuleShapeConfiguration, ShapeConfiguration>()
|
||||
->Version(1)
|
||||
->Field("Height", &CapsuleShapeConfiguration::m_height)
|
||||
@@ -153,6 +162,9 @@ namespace Physics
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext
|
||||
->RegisterGenericType<AZStd::shared_ptr<PhysicsAssetShapeConfiguration>>();
|
||||
|
||||
serializeContext->Class<PhysicsAssetShapeConfiguration, ShapeConfiguration>()
|
||||
->Version(1)
|
||||
->Field("PhysicsAsset", &PhysicsAssetShapeConfiguration::m_asset)
|
||||
@@ -185,6 +197,9 @@ namespace Physics
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext
|
||||
->RegisterGenericType<AZStd::shared_ptr<NativeShapeConfiguration>>();
|
||||
|
||||
serializeContext->Class<NativeShapeConfiguration, ShapeConfiguration>()
|
||||
->Version(1)
|
||||
->Field("Scale", &NativeShapeConfiguration::m_nativeShapeScale)
|
||||
@@ -208,6 +223,9 @@ namespace Physics
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext
|
||||
->RegisterGenericType<AZStd::shared_ptr<CookedMeshShapeConfiguration>>();
|
||||
|
||||
serializeContext->Class<CookedMeshShapeConfiguration, ShapeConfiguration>()
|
||||
->Version(1)
|
||||
->Field("CookedData", &CookedMeshShapeConfiguration::m_cookedData)
|
||||
|
||||
@@ -117,8 +117,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
EditorEntityModel::EditorEntityModel()
|
||||
{
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(m_isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
|
||||
EntityCompositionNotificationBus::Handler::BusConnect();
|
||||
EditorOnlyEntityComponentNotificationBus::Handler::BusConnect();
|
||||
EditorEntityRuntimeActivationChangeNotificationBus::Handler::BusConnect();
|
||||
@@ -565,7 +563,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
//retrieve or add an entity entry to the table
|
||||
//the entry must exist, even if not connected, so children and other data can be assigned
|
||||
[[maybe_unused]] auto [it, inserted] = m_entityInfoTable.try_emplace(entityId, m_isPrefabEnabled);
|
||||
[[maybe_unused]] auto [it, inserted] = m_entityInfoTable.try_emplace(entityId);
|
||||
auto& entityInfo = it->second;
|
||||
|
||||
//the entity id defaults to invalid and must be set to match the requested id
|
||||
@@ -882,11 +880,6 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
EditorEntityModel::EditorEntityModelEntry::EditorEntityModelEntry(bool isPrefabEnabled)
|
||||
: m_isPrefabEnabled(isPrefabEnabled)
|
||||
{
|
||||
}
|
||||
|
||||
EditorEntityModel::EditorEntityModelEntry::~EditorEntityModelEntry()
|
||||
{
|
||||
Disconnect();
|
||||
@@ -1213,29 +1206,15 @@ namespace AzToolsFramework
|
||||
auto childItr = m_childIndexCache.find(childId);
|
||||
if (childItr != m_childIndexCache.end())
|
||||
{
|
||||
if (m_isPrefabEnabled)
|
||||
{
|
||||
// Take the last entry and move it into the removed spot instead of deleting the entry and having to move all
|
||||
// following entries one step down.
|
||||
AZ::EntityId backEntity = m_children.back();
|
||||
m_children[childItr->second] = backEntity;
|
||||
// Update cached index for the moved id to the new index.
|
||||
m_childIndexCache[backEntity] = childItr->second;
|
||||
// Now remove the deleted id from the children and cache.
|
||||
m_childIndexCache.erase(childId);
|
||||
m_children.erase(m_children.end() - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_children.erase(m_children.begin() + childItr->second);
|
||||
|
||||
// rebuild index cache for faster lookup
|
||||
m_childIndexCache.clear();
|
||||
for (auto childIdToCache : m_children)
|
||||
{
|
||||
m_childIndexCache[childIdToCache] = static_cast<AZ::u64>(m_childIndexCache.size());
|
||||
}
|
||||
}
|
||||
// Take the last entry and move it into the removed spot instead of deleting the entry and having to move all
|
||||
// following entries one step down.
|
||||
AZ::EntityId backEntity = m_children.back();
|
||||
m_children[childItr->second] = backEntity;
|
||||
// Update cached index for the moved id to the new index.
|
||||
m_childIndexCache[backEntity] = childItr->second;
|
||||
// Now remove the deleted id from the children and cache.
|
||||
m_childIndexCache.erase(childId);
|
||||
m_children.erase(m_children.end() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -171,7 +171,6 @@ namespace AzToolsFramework
|
||||
, public PropertyEditorEntityChangeNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
explicit EditorEntityModelEntry(bool isPrefabEnabled);
|
||||
~EditorEntityModelEntry();
|
||||
|
||||
// Separately connect to EditorEntityInfoRequestBus and refresh Entity
|
||||
@@ -336,7 +335,6 @@ namespace AzToolsFramework
|
||||
bool m_visible = true;
|
||||
bool m_locked = false;
|
||||
bool m_connected = false;
|
||||
bool m_isPrefabEnabled = false;
|
||||
AZStd::string m_name;
|
||||
AZStd::string m_sliceAssetName;
|
||||
AZStd::unordered_map<AZ::EntityId, AZ::u64> m_childIndexCache;
|
||||
@@ -375,6 +373,5 @@ namespace AzToolsFramework
|
||||
AZ::EntityId m_postInstantiateBeforeEntity;
|
||||
AZ::EntityId m_postInstantiateSliceParent;
|
||||
bool m_gotInstantiateSliceDetails = false;
|
||||
bool m_isPrefabEnabled = false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -244,6 +244,8 @@ namespace AzToolsFramework
|
||||
/// from ViewportCursorScreenPosition. This method will always return the correct position to generate a mouse
|
||||
/// position delta.
|
||||
virtual AZStd::optional<AzFramework::ScreenPoint> PreviousViewportCursorScreenPosition() = 0;
|
||||
/// Is mouse over viewport.
|
||||
virtual bool IsMouseOver() const = 0;
|
||||
|
||||
protected:
|
||||
~ViewportMouseCursorRequests() = default;
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ namespace Benchmark
|
||||
}
|
||||
BENCHMARK_REGISTER_F(BM_PrefabUpdateInstances, UpdateInstances_SingeEntityInstances)
|
||||
->RangeMultiplier(10)
|
||||
->Range(100, 1000)
|
||||
->Range(100, 10000)
|
||||
->Unit(benchmark::kMillisecond)
|
||||
->Complexity();
|
||||
|
||||
|
||||
@@ -1327,7 +1327,7 @@ QToolButton* MainWindow::CreateDebugModeButton()
|
||||
|
||||
QWidget* MainWindow::CreateSpacerRightWidget()
|
||||
{
|
||||
QWidget* spacer = new QWidget();
|
||||
QWidget* spacer = new QWidget(this);
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
spacer->setVisible(true);
|
||||
return spacer;
|
||||
|
||||
@@ -1473,14 +1473,15 @@ void OutlinerListModel::OnEntityInfoUpdatedRemoveChildBegin(AZ::EntityId parentI
|
||||
emit EnableSelectionUpdates(false);
|
||||
auto parentIndex = GetIndexFromEntity(parentId);
|
||||
auto childIndex = GetIndexFromEntity(childId);
|
||||
beginRemoveRows(parentIndex, childIndex.row(), childIndex.row());
|
||||
beginResetModel();
|
||||
}
|
||||
|
||||
void OutlinerListModel::OnEntityInfoUpdatedRemoveChildEnd(AZ::EntityId parentId, AZ::EntityId childId)
|
||||
{
|
||||
(void)childId;
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
endRemoveRows();
|
||||
|
||||
endResetModel();
|
||||
|
||||
//must refresh partial lock/visibility of parents
|
||||
m_isFilterDirty = true;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "GemInfo.h"
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
GemInfo::GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded)
|
||||
: m_name(name)
|
||||
, m_creator(creator)
|
||||
, m_summary(summary)
|
||||
, m_platforms(platforms)
|
||||
, m_isAdded(isAdded)
|
||||
{
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzCore/Math/Uuid.h>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QVector>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class GemInfo
|
||||
{
|
||||
public:
|
||||
enum Platform
|
||||
{
|
||||
Android = 0x0,
|
||||
iOS = 0x1,
|
||||
Linux = 0x2,
|
||||
macOS = 0x3,
|
||||
Windows = 0x4
|
||||
};
|
||||
Q_DECLARE_FLAGS(Platforms, Platform)
|
||||
|
||||
GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded);
|
||||
|
||||
QString m_name;
|
||||
QString m_displayName;
|
||||
AZ::Uuid m_uuid;
|
||||
QString m_creator;
|
||||
bool m_isAdded = false; //! Is the gem currently added and enabled in the project?
|
||||
QString m_summary;
|
||||
Platforms m_platforms;
|
||||
QStringList m_features;
|
||||
QString m_version;
|
||||
QString m_lastUpdatedDate;
|
||||
QString m_documentationUrl;
|
||||
QVector<AZ::Uuid> m_dependingGemUuids;
|
||||
QVector<AZ::Uuid> m_conflictingGemUuids;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(O3DE::ProjectManager::GemInfo::Platforms)
|
||||
@@ -37,4 +37,6 @@ set(FILES
|
||||
Source/EngineSettings.h
|
||||
Source/EngineSettings.cpp
|
||||
Source/EngineSettings.ui
|
||||
Source/GemCatalog/GemInfo.h
|
||||
Source/GemCatalog/GemInfo.cpp
|
||||
)
|
||||
|
||||
@@ -25,13 +25,24 @@ namespace AWSCore
|
||||
: AWSCoreInternalRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
static constexpr const char AWSCORE_CONFIGURATION_FILENAME[] = "awscoreconfiguration.setreg";
|
||||
static constexpr const char AWSCoreConfigurationName[] = "AWSCoreConfiguration";
|
||||
static constexpr const char AWSCoreConfigurationFileName[] = "awscoreconfiguration.setreg";
|
||||
|
||||
static constexpr const char AWSCORE_RESOURCE_MAPPING_CONFIG_FOLDERNAME[] = "Config";
|
||||
static constexpr const char AWSCORE_RESOURCE_MAPPING_CONFIG_FILENAME_KEY[] = "/AWSCore/ResourceMappingConfigFileName";
|
||||
static constexpr const char AWSCoreResourceMappingConfigFolderName[] = "Config";
|
||||
static constexpr const char AWSCoreResourceMappingConfigFileNameKey[] = "/AWSCore/ResourceMappingConfigFileName";
|
||||
|
||||
static constexpr const char AWSCoreDefaultProfileName[] = "default";
|
||||
static constexpr const char AWSCoreProfileNameKey[] = "/AWSCore/ProfileName";
|
||||
|
||||
static constexpr const char ProjectSourceFolderNotFoundErrorMessage[] =
|
||||
"Failed to get project source folder path.";
|
||||
static constexpr const char ProfileNameNotFoundErrorMessage[] =
|
||||
"Failed to get profile name, return default value instead.";
|
||||
static constexpr const char ResourceMappingFileNameNotFoundErrorMessage[] =
|
||||
"Failed to get resource mapping config file name, return empty value instead.";
|
||||
static constexpr const char SettingsRegistryLoadFailureErrorMessage[] =
|
||||
"Failed to load AWSCore settings registry file.";
|
||||
|
||||
static constexpr const char AWSCORE_DEFAULT_PROFILE_NAME[] = "default";
|
||||
static constexpr const char AWSCORE_PROFILENAME_KEY[] = "/AWSCore/ProfileName";
|
||||
|
||||
AWSCoreConfiguration();
|
||||
~AWSCoreConfiguration() = default;
|
||||
@@ -55,6 +66,9 @@ namespace AWSCore
|
||||
// Parse values from project .setreg file
|
||||
void ParseSettingsRegistryValues();
|
||||
|
||||
// Reset settings registry data
|
||||
void ResetSettingsRegistryData();
|
||||
|
||||
AZStd::string m_sourceProjectFolder;
|
||||
AZ::SettingsRegistryImpl m_settingsRegistry;
|
||||
AZStd::string m_profileName;
|
||||
|
||||
@@ -14,20 +14,20 @@
|
||||
|
||||
namespace AWSCore
|
||||
{
|
||||
static constexpr const char AWS_CHINA_REGION_PREFIX[] = "cn-";
|
||||
static constexpr const char AWSChinaRegionPrefix[] = "cn-";
|
||||
|
||||
static constexpr const char AWS_FEATURE_GEM_RESTAPI_ID_KEYNAME_SUFFIX[] = ".RESTApiId";
|
||||
static constexpr const char AWS_FEATURE_GEM_RESTAPI_STAGE_KEYNAME_SUFFIX[] = ".RESTApiStage";
|
||||
static constexpr const char AWSFeatureGemRESTApiIdKeyNameSuffix[] = ".RESTApiId";
|
||||
static constexpr const char AWSFeatureGemRESTApiStageKeyNameSuffix[] = ".RESTApiStage";
|
||||
|
||||
static constexpr const char RESOURCE_MAPPING_ACCOUNTID_KEYNAME[] = "AccountId";
|
||||
static constexpr const char RESOURCE_MAPPING_RESOURCES_KEYNAME[] = "AWSResourceMappings";
|
||||
static constexpr const char RESOURCE_MAPPING_NAMEID_KEYNAME[] = "Name/ID";
|
||||
static constexpr const char RESOURCE_MAPPING_REGION_KEYNAME[] = "Region";
|
||||
static constexpr const char RESOURCE_MAPPING_TYPE_KEYNAME[] = "Type";
|
||||
static constexpr const char RESOURCE_MAPPING_VERSION_KEYNAME[] = "Version";
|
||||
static constexpr const char ResourceMappingAccountIdKeyName[] = "AccountId";
|
||||
static constexpr const char ResourceMappingResourcesKeyName[] = "AWSResourceMappings";
|
||||
static constexpr const char ResourceMappingNameIdKeyName[] = "Name/ID";
|
||||
static constexpr const char ResourceMappingRegionKeyName[] = "Region";
|
||||
static constexpr const char ResourceMappingTypeKeyName[] = "Type";
|
||||
static constexpr const char ResourceMappingVersionKeyName[] = "Version";
|
||||
|
||||
// TODO: move this into an independent file under AWSCore gem, if resource mapping tool can reuse it
|
||||
static constexpr const char RESOURCE_MAPPING_JSON_SCHEMA[] =
|
||||
static constexpr const char ResourceMappingJsonSchema[] =
|
||||
R"({
|
||||
"$schema": "http://json-schema.org/draft-04/schema",
|
||||
"type": "object",
|
||||
|
||||
@@ -45,6 +45,35 @@ namespace AWSCore
|
||||
};
|
||||
|
||||
public:
|
||||
static constexpr const char AWSResourceMappingManagerName[] = "AWSResourceMappingManager";
|
||||
static constexpr const char ManagerUnexpectedStatusErrorMessage[] =
|
||||
"AWSResourceMappingManager is in unexpected status.";
|
||||
static constexpr const char ResourceMappingFileInvalidPathErrorMessage[] =
|
||||
"Failed to get resource mapping config file path.";
|
||||
static constexpr const char ResourceMappingKeyNotFoundErrorMessage[] =
|
||||
"Failed to find resource mapping key: %s";
|
||||
static constexpr const char ResourceMappingFileNotLoadedErrorMessage[] =
|
||||
"Resource mapping config file is not loaded, please confirm %s is setup correctly.";
|
||||
static constexpr const char ResourceMappingFileLoadFailureErrorMessage[] =
|
||||
"Resource mapping config file failed to load, please confirm file is present and in correct format.";
|
||||
static constexpr const char ResourceMappingRESTApiIdAndStageInconsistentErrorMessage[] =
|
||||
"Resource mapping %s and %s have inconsistent region value, return empty service url.";
|
||||
static constexpr const char ResourceMappingRESTApiInvalidServiceUrlErrorMessage[] =
|
||||
"Unable to format REST Api url with RESTApiId=%s, RESTApiRegion=%s, RESTApiStage=%s, return empty service url.";
|
||||
static constexpr const char ResourceMappingFileInvalidJsonFormatErrorMessage[] =
|
||||
"Failed to read resource mapping config file: %s";
|
||||
static constexpr const char ResourceMappingFileInvalidSchemaErrorMessage[] =
|
||||
"Failed to load resource mapping config file json schema.";
|
||||
static constexpr const char ResourceMappingFileInvalidContentErrorMessage[] =
|
||||
"Failed to parse resource mapping config file: %s";
|
||||
|
||||
enum class Status : AZ::u8
|
||||
{
|
||||
NotLoaded = 0,
|
||||
Ready = 1,
|
||||
Error = 2
|
||||
};
|
||||
|
||||
AWSResourceMappingManager();
|
||||
~AWSResourceMappingManager() = default;
|
||||
|
||||
@@ -63,7 +92,12 @@ namespace AWSCore
|
||||
const AZStd::string& restApiIdKeyName, const AZStd::string& restApiStageKeyName) const override;
|
||||
void ReloadConfigFile(bool reloadConfigFileName = false) override;
|
||||
|
||||
Status GetStatus() const;
|
||||
|
||||
private:
|
||||
// Get resource attribute error message based on the status
|
||||
AZStd::string GetResourceAttributeErrorMessageByStatus(const AZStd::string& resourceKeyName) const;
|
||||
|
||||
// Get resource attribute from resource mappings
|
||||
AZStd::string GetResourceAttribute(
|
||||
AZStd::function<AZStd::string(const AWSResourceMappingAttributes&)> getAttributeFunction,
|
||||
@@ -83,6 +117,7 @@ namespace AWSCore
|
||||
// Validate JSON document against schema
|
||||
bool ValidateJsonDocumentAgainstSchema(const rapidjson::Document& jsonDocument);
|
||||
|
||||
Status m_status;
|
||||
// Resource mapping related data
|
||||
AZStd::string m_defaultAccountId;
|
||||
AZStd::string m_defaultRegion;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace AWSCore
|
||||
{
|
||||
AWSCoreConfiguration::AWSCoreConfiguration()
|
||||
: m_sourceProjectFolder("")
|
||||
, m_profileName(AWSCORE_DEFAULT_PROFILE_NAME)
|
||||
, m_profileName(AWSCoreDefaultProfileName)
|
||||
, m_resourceMappingConfigFileName("")
|
||||
{
|
||||
}
|
||||
@@ -44,16 +44,16 @@ namespace AWSCore
|
||||
{
|
||||
if (m_sourceProjectFolder.empty())
|
||||
{
|
||||
AZ_Warning("AWSCoreConfiguration", false, "Failed to get source project folder path.");
|
||||
AZ_Warning(AWSCoreConfigurationName, false, ProjectSourceFolderNotFoundErrorMessage);
|
||||
return "";
|
||||
}
|
||||
if (m_resourceMappingConfigFileName.empty())
|
||||
{
|
||||
AZ_Warning("AWSCoreConfiguration", false, "Failed to get resource mapping config file name.");
|
||||
AZ_Warning(AWSCoreConfigurationName, false, ResourceMappingFileNameNotFoundErrorMessage);
|
||||
return "";
|
||||
}
|
||||
AZStd::string configFilePath = AZStd::string::format("%s/%s/%s",
|
||||
m_sourceProjectFolder.c_str(), AWSCORE_RESOURCE_MAPPING_CONFIG_FOLDERNAME, m_resourceMappingConfigFileName.c_str());
|
||||
m_sourceProjectFolder.c_str(), AWSCoreResourceMappingConfigFolderName, m_resourceMappingConfigFileName.c_str());
|
||||
AzFramework::StringFunc::Path::Normalize(configFilePath);
|
||||
return configFilePath;
|
||||
}
|
||||
@@ -68,17 +68,17 @@ namespace AWSCore
|
||||
{
|
||||
if (m_sourceProjectFolder.empty())
|
||||
{
|
||||
AZ_Warning("AWSCoreConfiguration", false, "Failed to get source project folder path.");
|
||||
AZ_Warning(AWSCoreConfigurationName, false, ProjectSourceFolderNotFoundErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::string settingsRegistryPath = AZStd::string::format("%s/%s/%s",
|
||||
m_sourceProjectFolder.c_str(), AZ::SettingsRegistryInterface::RegistryFolder, AWSCoreConfiguration::AWSCORE_CONFIGURATION_FILENAME);
|
||||
m_sourceProjectFolder.c_str(), AZ::SettingsRegistryInterface::RegistryFolder, AWSCoreConfiguration::AWSCoreConfigurationFileName);
|
||||
AzFramework::StringFunc::Path::Normalize(settingsRegistryPath);
|
||||
|
||||
if (!m_settingsRegistry.MergeSettingsFile(settingsRegistryPath, AZ::SettingsRegistryInterface::Format::JsonMergePatch, ""))
|
||||
{
|
||||
AZ_Warning("AWSCoreConfiguration", false, "Failed to merge AWS core settings registry.");
|
||||
AZ_Warning(AWSCoreConfigurationName, false, SettingsRegistryLoadFailureErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace AWSCore
|
||||
auto sourceProjectFolder = AZ::IO::FileIOBase::GetInstance()->GetAlias("@devassets@");
|
||||
if (!sourceProjectFolder)
|
||||
{
|
||||
AZ_Error("AWSCoreConfiguration", false, "Failed to initialize source project folder path.");
|
||||
AZ_Error(AWSCoreConfigurationName, false, ProjectSourceFolderNotFoundErrorMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -102,24 +102,38 @@ namespace AWSCore
|
||||
{
|
||||
m_resourceMappingConfigFileName.clear();
|
||||
auto resourceMappingConfigFileNamePath = AZStd::string::format("%s%s",
|
||||
AZ::SettingsRegistryMergeUtils::OrganizationRootKey, AWSCORE_RESOURCE_MAPPING_CONFIG_FILENAME_KEY);
|
||||
AZ::SettingsRegistryMergeUtils::OrganizationRootKey, AWSCoreResourceMappingConfigFileNameKey);
|
||||
if (!m_settingsRegistry.Get(m_resourceMappingConfigFileName, resourceMappingConfigFileNamePath))
|
||||
{
|
||||
AZ_Warning("AWSCoreConfiguration", false, "Failed to get resource mapping config file name from settings registry.");
|
||||
AZ_Warning(AWSCoreConfigurationName, false, ResourceMappingFileNameNotFoundErrorMessage);
|
||||
}
|
||||
|
||||
m_profileName.clear();
|
||||
auto profileNamePath = AZStd::string::format(
|
||||
"%s%s", AZ::SettingsRegistryMergeUtils::OrganizationRootKey, AWSCORE_PROFILENAME_KEY);
|
||||
"%s%s", AZ::SettingsRegistryMergeUtils::OrganizationRootKey, AWSCoreProfileNameKey);
|
||||
if (!m_settingsRegistry.Get(m_profileName, profileNamePath))
|
||||
{
|
||||
AZ_Warning("AWSCoreConfiguration", false, "Failed to get profile name from settings registry, using default value instead.");
|
||||
m_profileName = AWSCORE_DEFAULT_PROFILE_NAME;
|
||||
AZ_Warning(AWSCoreConfigurationName, false, ProfileNameNotFoundErrorMessage);
|
||||
m_profileName = AWSCoreDefaultProfileName;
|
||||
}
|
||||
}
|
||||
|
||||
void AWSCoreConfiguration::ResetSettingsRegistryData()
|
||||
{
|
||||
auto profileNamePath = AZStd::string::format("%s%s",
|
||||
AZ::SettingsRegistryMergeUtils::OrganizationRootKey, AWSCoreProfileNameKey);
|
||||
m_settingsRegistry.Remove(profileNamePath);
|
||||
m_profileName.clear();
|
||||
|
||||
auto resourceMappingConfigFileNamePath = AZStd::string::format("%s%s",
|
||||
AZ::SettingsRegistryMergeUtils::OrganizationRootKey, AWSCoreResourceMappingConfigFileNameKey);
|
||||
m_settingsRegistry.Remove(resourceMappingConfigFileNamePath);
|
||||
m_resourceMappingConfigFileName.clear();
|
||||
}
|
||||
|
||||
void AWSCoreConfiguration::ReloadConfiguration()
|
||||
{
|
||||
ResetSettingsRegistryData();
|
||||
InitSettingsRegistry();
|
||||
}
|
||||
} // namespace AWSCore
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace AWSCore
|
||||
{
|
||||
AZ_Warning("AWSDefaultCredentialHandler", false, "Failed to get profile name, use default profile name instead");
|
||||
SetProfileCredentialsProvider(Aws::MakeShared<Aws::Auth::ProfileConfigFileAWSCredentialsProvider>(
|
||||
AWSDEFAULTCREDENTIALHANDLER_ALLOC_TAG, AWSCoreConfiguration::AWSCORE_DEFAULT_PROFILE_NAME));
|
||||
AWSDEFAULTCREDENTIALHANDLER_ALLOC_TAG, AWSCoreConfiguration::AWSCoreDefaultProfileName));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -12,12 +12,14 @@
|
||||
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/JSON/schema.h>
|
||||
#include <AzCore/JSON/prettywriter.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzCore/Settings/SettingsRegistryImpl.h>
|
||||
#include <AzFramework/FileFunc/FileFunc.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
#include <AWSCoreInternalBus.h>
|
||||
#include <Configuration/AWSCoreConfiguration.h>
|
||||
#include <ResourceMapping/AWSResourceMappingConstants.h>
|
||||
#include <ResourceMapping/AWSResourceMappingManager.h>
|
||||
#include <ResourceMapping/AWSResourceMappingUtils.h>
|
||||
@@ -25,7 +27,8 @@
|
||||
namespace AWSCore
|
||||
{
|
||||
AWSResourceMappingManager::AWSResourceMappingManager()
|
||||
: m_defaultAccountId("")
|
||||
: m_status(Status::NotLoaded)
|
||||
, m_defaultAccountId("")
|
||||
, m_defaultRegion("")
|
||||
, m_resourceMappings()
|
||||
{
|
||||
@@ -43,11 +46,27 @@ namespace AWSCore
|
||||
ResetResourceMappingsData();
|
||||
}
|
||||
|
||||
AZStd::string AWSResourceMappingManager::GetResourceAttributeErrorMessageByStatus(const AZStd::string& resourceKeyName) const
|
||||
{
|
||||
switch (m_status)
|
||||
{
|
||||
case Status::NotLoaded:
|
||||
return AZStd::string::format(ResourceMappingFileNotLoadedErrorMessage, AWSCoreConfiguration::AWSCoreConfigurationFileName);
|
||||
case Status::Ready:
|
||||
return AZStd::string::format(ResourceMappingKeyNotFoundErrorMessage, resourceKeyName.c_str());
|
||||
case Status::Error:
|
||||
return ResourceMappingFileLoadFailureErrorMessage;
|
||||
default:
|
||||
return ManagerUnexpectedStatusErrorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string AWSResourceMappingManager::GetDefaultAccountId() const
|
||||
{
|
||||
if (m_defaultAccountId.empty())
|
||||
{
|
||||
AZ_Warning("AWSResourceMappingManager", false, "Account Id should not be empty, please make sure config file is valid.");
|
||||
AZ_Warning(AWSResourceMappingManagerName, false,
|
||||
GetResourceAttributeErrorMessageByStatus(ResourceMappingAccountIdKeyName).c_str());
|
||||
}
|
||||
return m_defaultAccountId;
|
||||
}
|
||||
@@ -56,7 +75,8 @@ namespace AWSCore
|
||||
{
|
||||
if (m_defaultRegion.empty())
|
||||
{
|
||||
AZ_Warning("AWSResourceMappingManager", false, "Region should not be empty, please make sure config file is valid.");
|
||||
AZ_Warning(AWSResourceMappingManagerName, false,
|
||||
GetResourceAttributeErrorMessageByStatus(ResourceMappingRegionKeyName).c_str());
|
||||
}
|
||||
return m_defaultRegion;
|
||||
}
|
||||
@@ -100,8 +120,8 @@ namespace AWSCore
|
||||
AZStd::string AWSResourceMappingManager::GetServiceUrlByServiceName(const AZStd::string& serviceName) const
|
||||
{
|
||||
return GetServiceUrlByRESTApiIdAndStage(
|
||||
AZStd::string::format("%s%s", serviceName.c_str(), AWS_FEATURE_GEM_RESTAPI_ID_KEYNAME_SUFFIX),
|
||||
AZStd::string::format("%s%s", serviceName.c_str(), AWS_FEATURE_GEM_RESTAPI_STAGE_KEYNAME_SUFFIX));
|
||||
AZStd::string::format("%s%s", serviceName.c_str(), AWSFeatureGemRESTApiIdKeyNameSuffix),
|
||||
AZStd::string::format("%s%s", serviceName.c_str(), AWSFeatureGemRESTApiStageKeyNameSuffix));
|
||||
}
|
||||
|
||||
AZStd::string AWSResourceMappingManager::GetServiceUrlByRESTApiIdAndStage(
|
||||
@@ -113,16 +133,13 @@ namespace AWSCore
|
||||
AZStd::string serviceRegion = GetResourceRegion(restApiIdKeyName);
|
||||
if (serviceRegion != GetResourceRegion(restApiStageKeyName))
|
||||
{
|
||||
AZ_Warning(
|
||||
"AWSResourceMappingManager", false, "%s and %s have inconsistent region value, return empty service url.",
|
||||
AZ_Warning(AWSResourceMappingManagerName, false, ResourceMappingRESTApiIdAndStageInconsistentErrorMessage,
|
||||
restApiIdKeyName.c_str(), restApiStageKeyName.c_str());
|
||||
return "";
|
||||
}
|
||||
|
||||
AZStd::string serviceRESTApiUrl = AWSResourceMappingUtils::FormatRESTApiUrl(serviceRESTApiId, serviceRegion, serviceRESTApiStage);
|
||||
AZ_Warning(
|
||||
"AWSResourceMappingManager", !serviceRESTApiUrl.empty(),
|
||||
"Unable to format REST Api url with RESTApiId=%s, RESTApiRegion=%s, RESTApiStage=%s, return empty service url.",
|
||||
AZ_Warning(AWSResourceMappingManagerName, !serviceRESTApiUrl.empty(), ResourceMappingRESTApiInvalidServiceUrlErrorMessage,
|
||||
serviceRESTApiId.c_str(), serviceRegion.c_str(), serviceRESTApiStage.c_str());
|
||||
return serviceRESTApiUrl;
|
||||
}
|
||||
@@ -136,16 +153,21 @@ namespace AWSCore
|
||||
return getAttributeFunction(iter->second);
|
||||
}
|
||||
|
||||
AZ_Warning("AWSResourceMappingManager", false, "Failed to find resource mapping key: %s.", resourceKeyName.c_str());
|
||||
AZ_Warning(AWSResourceMappingManagerName, false, GetResourceAttributeErrorMessageByStatus(resourceKeyName).c_str());
|
||||
return "";
|
||||
}
|
||||
|
||||
AWSResourceMappingManager::Status AWSResourceMappingManager::GetStatus() const
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
void AWSResourceMappingManager::ParseJsonDocument(const rapidjson::Document& jsonDocument)
|
||||
{
|
||||
m_defaultAccountId = jsonDocument.FindMember(RESOURCE_MAPPING_ACCOUNTID_KEYNAME)->value.GetString();
|
||||
m_defaultRegion = jsonDocument.FindMember(RESOURCE_MAPPING_REGION_KEYNAME)->value.GetString();
|
||||
m_defaultAccountId = jsonDocument.FindMember(ResourceMappingAccountIdKeyName)->value.GetString();
|
||||
m_defaultRegion = jsonDocument.FindMember(ResourceMappingRegionKeyName)->value.GetString();
|
||||
|
||||
auto resourceMappings = jsonDocument.FindMember(RESOURCE_MAPPING_RESOURCES_KEYNAME)->value.GetObject();
|
||||
auto resourceMappings = jsonDocument.FindMember(ResourceMappingResourcesKeyName)->value.GetObject();
|
||||
for (auto mappingIter = resourceMappings.MemberBegin(); mappingIter != resourceMappings.MemberEnd(); mappingIter++)
|
||||
{
|
||||
auto mappingValue = mappingIter->value.GetObject();
|
||||
@@ -162,16 +184,16 @@ namespace AWSCore
|
||||
const JsonObject& jsonObject)
|
||||
{
|
||||
AWSResourceMappingAttributes attributes;
|
||||
if (jsonObject.HasMember(RESOURCE_MAPPING_ACCOUNTID_KEYNAME))
|
||||
if (jsonObject.HasMember(ResourceMappingAccountIdKeyName))
|
||||
{
|
||||
attributes.resourceAccountId = jsonObject.FindMember(RESOURCE_MAPPING_ACCOUNTID_KEYNAME)->value.GetString();
|
||||
attributes.resourceAccountId = jsonObject.FindMember(ResourceMappingAccountIdKeyName)->value.GetString();
|
||||
}
|
||||
attributes.resourceNameId = jsonObject.FindMember(RESOURCE_MAPPING_NAMEID_KEYNAME)->value.GetString();
|
||||
if (jsonObject.HasMember(RESOURCE_MAPPING_REGION_KEYNAME))
|
||||
attributes.resourceNameId = jsonObject.FindMember(ResourceMappingNameIdKeyName)->value.GetString();
|
||||
if (jsonObject.HasMember(ResourceMappingRegionKeyName))
|
||||
{
|
||||
attributes.resourceRegion = jsonObject.FindMember(RESOURCE_MAPPING_REGION_KEYNAME)->value.GetString();
|
||||
attributes.resourceRegion = jsonObject.FindMember(ResourceMappingRegionKeyName)->value.GetString();
|
||||
}
|
||||
attributes.resourceType = jsonObject.FindMember(RESOURCE_MAPPING_TYPE_KEYNAME)->value.GetString();
|
||||
attributes.resourceType = jsonObject.FindMember(ResourceMappingTypeKeyName)->value.GetString();
|
||||
return attributes;
|
||||
}
|
||||
|
||||
@@ -188,7 +210,7 @@ namespace AWSCore
|
||||
AWSCoreInternalRequestBus::BroadcastResult(configJsonPath, &AWSCoreInternalRequests::GetResourceMappingConfigFilePath);
|
||||
if (configJsonPath.empty())
|
||||
{
|
||||
AZ_Warning("AWSResourceMappingManager", false, "Failed to get resource mapping config file path.");
|
||||
AZ_Warning(AWSResourceMappingManagerName, false, ResourceMappingFileInvalidPathErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -201,20 +223,26 @@ namespace AWSCore
|
||||
if (!ValidateJsonDocumentAgainstSchema(jsonDocument))
|
||||
{
|
||||
// Failed to satisfy the validation against json schema
|
||||
m_status = Status::Error;
|
||||
return;
|
||||
}
|
||||
ParseJsonDocument(jsonDocument);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning(
|
||||
"AWSResourceMappingManager", false, "Failed to get read resource mapping config file: %s\n Error: %s",
|
||||
configJsonPath.c_str(), readJsonOutcome.GetError().c_str());
|
||||
m_status = Status::Error;
|
||||
AZ_Warning(AWSResourceMappingManagerName, false,
|
||||
ResourceMappingFileInvalidJsonFormatErrorMessage, readJsonOutcome.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Resource mapping config file gets loaded successfully
|
||||
m_status = Status::Ready;
|
||||
}
|
||||
|
||||
void AWSResourceMappingManager::ResetResourceMappingsData()
|
||||
{
|
||||
m_status = Status::NotLoaded;
|
||||
m_defaultAccountId = "";
|
||||
m_defaultRegion = "";
|
||||
m_resourceMappings.clear();
|
||||
@@ -223,9 +251,9 @@ namespace AWSCore
|
||||
bool AWSResourceMappingManager::ValidateJsonDocumentAgainstSchema(const rapidjson::Document& jsonDocument)
|
||||
{
|
||||
rapidjson::Document jsonSchemaDocument;
|
||||
if (jsonSchemaDocument.Parse(RESOURCE_MAPPING_JSON_SCHEMA).HasParseError())
|
||||
if (jsonSchemaDocument.Parse(ResourceMappingJsonSchema).HasParseError())
|
||||
{
|
||||
AZ_Error("AWSResourceMappingManager", false, "Invalid resource mapping json schema.");
|
||||
AZ_Error(AWSResourceMappingManagerName, false, ResourceMappingFileInvalidSchemaErrorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -235,12 +263,10 @@ namespace AWSCore
|
||||
if (!jsonDocument.Accept(validator))
|
||||
{
|
||||
rapidjson::StringBuffer error;
|
||||
validator.GetInvalidSchemaPointer().StringifyUriFragment(error);
|
||||
AZ_Warning("AWSResourceMappingManager", false, "Failed to load config file, invalid schema: %s.", error.GetString());
|
||||
AZ_Warning("AWSResourceMappingManager", false, "Failed to load config file, invalid keyword: %s.", validator.GetInvalidSchemaKeyword());
|
||||
error.Clear();
|
||||
validator.GetInvalidDocumentPointer().StringifyUriFragment(error);
|
||||
AZ_Warning("AWSResourceMappingManager", false, "Failed to load config file, invalid document: %s.", error.GetString());
|
||||
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(error);
|
||||
validator.GetError().Accept(writer);
|
||||
AZ_Warning(AWSResourceMappingManagerName, false, ResourceMappingFileInvalidContentErrorMessage, error.GetString());
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -18,8 +18,8 @@ namespace AWSCore
|
||||
namespace AWSResourceMappingUtils
|
||||
{
|
||||
// https://docs.aws.amazon.com/general/latest/gr/apigateway.html
|
||||
static constexpr char RESTAPI_URL_FORMAT[] = "https://%s.execute-api.%s.amazonaws.com/%s";
|
||||
static constexpr char RESTAPI_CHINA_URL_FORMAT[] = "https://%s.execute-api.%s.amazonaws.com.cn/%s";
|
||||
static constexpr char RESTApiUrlFormat[] = "https://%s.execute-api.%s.amazonaws.com/%s";
|
||||
static constexpr char RESTApiChinaUrlFormat[] = "https://%s.execute-api.%s.amazonaws.com.cn/%s";
|
||||
|
||||
AZStd::string FormatRESTApiUrl(
|
||||
const AZStd::string& restApiId, const AZStd::string& restApiRegion, const AZStd::string& restApiStage)
|
||||
@@ -27,14 +27,14 @@ namespace AWSCore
|
||||
// https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-call-api.html
|
||||
if (!restApiId.empty() && !restApiRegion.empty() && !restApiStage.empty())
|
||||
{
|
||||
if (restApiRegion.rfind(AWS_CHINA_REGION_PREFIX, 0) == 0)
|
||||
if (restApiRegion.rfind(AWSChinaRegionPrefix, 0) == 0)
|
||||
{
|
||||
return AZStd::string::format(RESTAPI_CHINA_URL_FORMAT,
|
||||
return AZStd::string::format(RESTApiChinaUrlFormat,
|
||||
restApiId.c_str(), restApiRegion.c_str(), restApiStage.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
return AZStd::string::format(RESTAPI_URL_FORMAT,
|
||||
return AZStd::string::format(RESTApiUrlFormat,
|
||||
restApiId.c_str(), restApiRegion.c_str(), restApiStage.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
void CreateTestSetRegFile(const AZStd::string& setregContent)
|
||||
{
|
||||
m_normalizedSetRegFilePath = AZStd::string::format("%s/%s",
|
||||
m_normalizedSetRegFolderPath.c_str(), AWSCore::AWSCoreConfiguration::AWSCORE_CONFIGURATION_FILENAME);
|
||||
m_normalizedSetRegFolderPath.c_str(), AWSCore::AWSCoreConfiguration::AWSCoreConfigurationFileName);
|
||||
AzFramework::StringFunc::Path::Normalize(m_normalizedSetRegFilePath);
|
||||
CreateTestFile(m_normalizedSetRegFilePath, setregContent);
|
||||
}
|
||||
@@ -177,7 +177,7 @@ TEST_F(AWSCoreConfigurationTest, ReloadConfiguration_LoadValidSettingsRegistryAf
|
||||
auto actualConfigFilePath = m_awsCoreConfiguration->GetResourceMappingConfigFilePath();
|
||||
auto actualProfileName = m_awsCoreConfiguration->GetProfileName();
|
||||
EXPECT_TRUE(actualConfigFilePath.empty());
|
||||
EXPECT_TRUE(actualProfileName == AWSCoreConfiguration::AWSCORE_DEFAULT_PROFILE_NAME);
|
||||
EXPECT_TRUE(actualProfileName == AWSCoreConfiguration::AWSCoreDefaultProfileName);
|
||||
|
||||
CreateTestSetRegFile(TEST_VALID_RESOURCE_MAPPING_SETREG);
|
||||
m_awsCoreConfiguration->ReloadConfiguration();
|
||||
@@ -185,5 +185,24 @@ TEST_F(AWSCoreConfigurationTest, ReloadConfiguration_LoadValidSettingsRegistryAf
|
||||
actualConfigFilePath = m_awsCoreConfiguration->GetResourceMappingConfigFilePath();
|
||||
actualProfileName = m_awsCoreConfiguration->GetProfileName();
|
||||
EXPECT_FALSE(actualConfigFilePath.empty());
|
||||
EXPECT_TRUE(actualProfileName != AWSCoreConfiguration::AWSCORE_DEFAULT_PROFILE_NAME);
|
||||
EXPECT_TRUE(actualProfileName != AWSCoreConfiguration::AWSCoreDefaultProfileName);
|
||||
}
|
||||
|
||||
TEST_F(AWSCoreConfigurationTest, ReloadConfiguration_LoadInvalidSettingsRegistryAfterValidOne_ReturnEmptyConfigFilePath)
|
||||
{
|
||||
CreateTestSetRegFile(TEST_VALID_RESOURCE_MAPPING_SETREG);
|
||||
m_awsCoreConfiguration->InitConfig();
|
||||
|
||||
auto actualConfigFilePath = m_awsCoreConfiguration->GetResourceMappingConfigFilePath();
|
||||
auto actualProfileName = m_awsCoreConfiguration->GetProfileName();
|
||||
EXPECT_FALSE(actualConfigFilePath.empty());
|
||||
EXPECT_TRUE(actualProfileName != AWSCoreConfiguration::AWSCoreDefaultProfileName);
|
||||
|
||||
CreateTestSetRegFile(TEST_INVALID_RESOURCE_MAPPING_SETREG);
|
||||
m_awsCoreConfiguration->ReloadConfiguration();
|
||||
|
||||
actualConfigFilePath = m_awsCoreConfiguration->GetResourceMappingConfigFilePath();
|
||||
actualProfileName = m_awsCoreConfiguration->GetProfileName();
|
||||
EXPECT_TRUE(actualConfigFilePath.empty());
|
||||
EXPECT_TRUE(actualProfileName == AWSCoreConfiguration::AWSCoreDefaultProfileName);
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ public:
|
||||
"AWSResourceMappingManager", AZ::Uuid::CreateRandom().ToString<AZStd::string>(false, false).c_str());
|
||||
AzFramework::StringFunc::Path::Normalize(m_normalizedSourceProjectFolder);
|
||||
m_normalizedConfigFolderPath = AZStd::string::format("%s/%s/",
|
||||
m_normalizedSourceProjectFolder.c_str(), AWSCore::AWSCoreConfiguration::AWSCORE_RESOURCE_MAPPING_CONFIG_FOLDERNAME);
|
||||
m_normalizedSourceProjectFolder.c_str(), AWSCore::AWSCoreConfiguration::AWSCoreResourceMappingConfigFolderName);
|
||||
AzFramework::StringFunc::Path::Normalize(m_normalizedConfigFolderPath);
|
||||
AWSCoreInternalRequestBus::Handler::BusConnect();
|
||||
}
|
||||
@@ -178,6 +178,7 @@ TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseInvalidConfigFile_Con
|
||||
EXPECT_EQ(m_reloadConfigurationCounter, 1);
|
||||
EXPECT_TRUE(actualAccountId.empty());
|
||||
EXPECT_TRUE(actualRegion.empty());
|
||||
EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::Error);
|
||||
}
|
||||
|
||||
TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseValidConfigFile_ConfigDataIsNotEmpty)
|
||||
@@ -192,6 +193,7 @@ TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseValidConfigFile_Confi
|
||||
EXPECT_EQ(m_reloadConfigurationCounter, 1);
|
||||
EXPECT_FALSE(actualAccountId.empty());
|
||||
EXPECT_FALSE(actualRegion.empty());
|
||||
EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::Ready);
|
||||
}
|
||||
|
||||
TEST_F(AWSResourceMappingManagerTest, ActivateManager_ParseValidConfigFile_ConfigDataIsNotEmptyWithMultithreadCalls)
|
||||
@@ -230,11 +232,13 @@ TEST_F(AWSResourceMappingManagerTest, DeactivateManager_AfterActivatingWithValid
|
||||
AWSResourceMappingRequestBus::BroadcastResult(actualRegion, &AWSResourceMappingRequests::GetDefaultRegion);
|
||||
EXPECT_FALSE(actualAccountId.empty());
|
||||
EXPECT_FALSE(actualRegion.empty());
|
||||
EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::Ready);
|
||||
|
||||
m_resourceMappingManager->DeactivateManager();
|
||||
|
||||
EXPECT_TRUE(m_resourceMappingManager->GetDefaultAccountId().empty());
|
||||
EXPECT_TRUE(m_resourceMappingManager->GetDefaultRegion().empty());
|
||||
EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::NotLoaded);
|
||||
}
|
||||
|
||||
TEST_F(AWSResourceMappingManagerTest, GetDefaultAccountId_AfterParsingValidConfigFile_GetExpectedDefaultAccountId)
|
||||
@@ -416,6 +420,7 @@ TEST_F(AWSResourceMappingManagerTest, ReloadConfigFile_ParseValidConfigFileAfter
|
||||
EXPECT_EQ(m_reloadConfigurationCounter, 1);
|
||||
EXPECT_TRUE(actualAccountId.empty());
|
||||
EXPECT_TRUE(actualRegion.empty());
|
||||
EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::Error);
|
||||
|
||||
CreateTestConfigFile(TEST_VALID_RESOURCE_MAPPING_CONFIG_FILE);
|
||||
m_resourceMappingManager->ReloadConfigFile();
|
||||
@@ -425,6 +430,7 @@ TEST_F(AWSResourceMappingManagerTest, ReloadConfigFile_ParseValidConfigFileAfter
|
||||
EXPECT_EQ(m_reloadConfigurationCounter, 1);
|
||||
EXPECT_FALSE(actualAccountId.empty());
|
||||
EXPECT_FALSE(actualRegion.empty());
|
||||
EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::Ready);
|
||||
}
|
||||
|
||||
TEST_F(AWSResourceMappingManagerTest, ReloadConfigFile_ReloadConfigFileNameAndParseValidConfigFile_ConfigDataGetParsed)
|
||||
@@ -435,6 +441,7 @@ TEST_F(AWSResourceMappingManagerTest, ReloadConfigFile_ReloadConfigFileNameAndPa
|
||||
EXPECT_EQ(m_reloadConfigurationCounter, 1);
|
||||
EXPECT_FALSE(m_resourceMappingManager->GetDefaultAccountId().empty());
|
||||
EXPECT_FALSE(m_resourceMappingManager->GetDefaultRegion().empty());
|
||||
EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::Ready);
|
||||
}
|
||||
|
||||
TEST_F(AWSResourceMappingManagerTest, ReloadConfigFile_MissingSetRegFile_ConfigDataIsNotParsed)
|
||||
@@ -444,4 +451,5 @@ TEST_F(AWSResourceMappingManagerTest, ReloadConfigFile_MissingSetRegFile_ConfigD
|
||||
EXPECT_EQ(m_reloadConfigurationCounter, 1);
|
||||
EXPECT_TRUE(m_resourceMappingManager->GetDefaultAccountId().empty());
|
||||
EXPECT_TRUE(m_resourceMappingManager->GetDefaultRegion().empty());
|
||||
EXPECT_TRUE(m_resourceMappingManager->GetStatus() == AWSResourceMappingManager::Status::NotLoaded);
|
||||
}
|
||||
|
||||
@@ -851,7 +851,9 @@ namespace ImageProcessingAtom
|
||||
|
||||
if (preset == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "preset should always exist");
|
||||
AZStd::string uuidStr;
|
||||
textureSettings.m_preset.ToString(uuidStr);
|
||||
AZ_Assert(false, "%s cannot find image preset with ID %s.", imageFilePath.c_str(), uuidStr.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -946,19 +946,6 @@
|
||||
"type": "Bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "factor",
|
||||
"displayName": "Factor",
|
||||
"description": "Strength factor for scaling the depth values",
|
||||
"type": "Float",
|
||||
"defaultValue": 0.0,
|
||||
"min": 0.0,
|
||||
"softMax": 0.1,
|
||||
"connection": {
|
||||
"type": "ShaderInput",
|
||||
"id": "m_depthFactor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "textureMap",
|
||||
"displayName": "Texture Map",
|
||||
@@ -981,6 +968,32 @@
|
||||
"id": "m_parallaxUvIndex"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "factor",
|
||||
"displayName": "Heightmap Scale",
|
||||
"description": "The total height of the heightmap in local model units.",
|
||||
"type": "Float",
|
||||
"defaultValue": 0.0,
|
||||
"min": 0.0,
|
||||
"softMax": 0.1,
|
||||
"connection": {
|
||||
"type": "ShaderInput",
|
||||
"id": "m_depthFactor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "offset",
|
||||
"displayName": "Offset",
|
||||
"description": "Adjusts the overall displacement amount in local model units.",
|
||||
"type": "Float",
|
||||
"defaultValue": 0.0,
|
||||
"softMin": -0.1,
|
||||
"softMax": 0.1,
|
||||
"connection": {
|
||||
"type": "ShaderInput",
|
||||
"id": "m_depthOffset"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "invert",
|
||||
"displayName": "Invert",
|
||||
@@ -1026,6 +1039,17 @@
|
||||
"type": "ShaderOption",
|
||||
"id": "o_parallax_enablePixelDepthOffset"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "showClipping",
|
||||
"displayName": "Show Clipping",
|
||||
"description": "Highlight areas where the heightmap is clipped by the mesh surface.",
|
||||
"type": "Bool",
|
||||
"defaultValue": false,
|
||||
"connection": {
|
||||
"type": "ShaderOption",
|
||||
"id": "o_parallax_highlightClipping"
|
||||
}
|
||||
}
|
||||
],
|
||||
"subsurfaceScattering": [
|
||||
@@ -1714,22 +1738,6 @@
|
||||
"shaderOption": "o_emissive_useTexture"
|
||||
}
|
||||
},
|
||||
{
|
||||
// See the comment above for details.
|
||||
"type": "UseTexture",
|
||||
"args": {
|
||||
"textureProperty": "parallax.textureMap",
|
||||
"dependentProperties": ["parallax.textureMapUv"],
|
||||
"useTextureProperty": "parallax.enable",
|
||||
"shaderTags": [
|
||||
"ForwardPass",
|
||||
"ForwardPass_EDS",
|
||||
"Shadowmap_WithPS",
|
||||
"DepthPass_WithPS"
|
||||
],
|
||||
"shaderOption": "o_parallax_feature_enabled"
|
||||
}
|
||||
},
|
||||
{
|
||||
// See the comment above for details.
|
||||
"type": "UseTexture",
|
||||
@@ -1813,34 +1821,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
// Controls visibility for properties in the editor.
|
||||
// @param actions - a list of actions that are executed in order. visibility will be set when triggerProperty hits the triggerValue.
|
||||
// @param affectedProperties - the properties that are affected by actions.
|
||||
"type": "UpdatePropertyVisibility",
|
||||
"args": {
|
||||
"actions": [
|
||||
{
|
||||
"triggerProperty": "parallax.enable",
|
||||
"triggerValue": true,
|
||||
"visibility": "Enabled"
|
||||
},
|
||||
{
|
||||
"triggerProperty": "parallax.enable",
|
||||
"triggerValue": false,
|
||||
"visibility": "Hidden"
|
||||
}
|
||||
],
|
||||
"affectedProperties": [
|
||||
"parallax.factor",
|
||||
"parallax.textureMap",
|
||||
"parallax.invert",
|
||||
"parallax.algorithm",
|
||||
"parallax.quality",
|
||||
"parallax.pdo"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UpdatePropertyVisibility",
|
||||
"args": {
|
||||
@@ -2076,6 +2056,12 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
"file": "StandardPBR_ParallaxState.lua"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <Atom/Features/SrgSemantics.azsli>
|
||||
#include <viewsrg.srgi>
|
||||
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
|
||||
#include <Atom/Features/PBR/AlphaUtils.azsli>
|
||||
#include <Atom/Features/PBR/LightingOptions.azsli>
|
||||
|
||||
#include "MaterialInputs/BaseColorInput.azsli"
|
||||
#include "MaterialInputs/RoughnessInput.azsli"
|
||||
@@ -104,7 +106,22 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
|
||||
}
|
||||
|
||||
// Callback function for ParallaxMapping.azsli
|
||||
float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
{
|
||||
return SampleDepthOrHeightMap(MaterialSrg::m_depthInverted, MaterialSrg::m_depthMap, MaterialSrg::m_sampler, uv, uv_ddx, uv_ddy);
|
||||
}
|
||||
|
||||
COMMON_OPTIONS_PARALLAX()
|
||||
|
||||
bool ShouldHandleParallax()
|
||||
{
|
||||
// Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scattering is enabled.
|
||||
return !o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useDepthMap;
|
||||
}
|
||||
|
||||
bool ShouldHandleParallaxInDepthShaders()
|
||||
{
|
||||
// The depth pass shaders need to calculate parallax when the result could affect the depth buffer, or when
|
||||
// parallax could affect texel clipping.
|
||||
return ShouldHandleParallax() && (o_parallax_enablePixelDepthOffset || o_opacity_mode == OpacityMode::Cutout);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Atom/Features/PBR/AlphaUtils.azsli>
|
||||
#include "./EnhancedPBR_Common.azsli"
|
||||
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
|
||||
#include <Atom/Features/ParallaxMapping.azsli>
|
||||
@@ -55,7 +54,7 @@ VSDepthOutput MainVS(VSInput IN)
|
||||
OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy;
|
||||
OUT.m_uv[1] = IN.m_uv1;
|
||||
|
||||
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
|
||||
if(ShouldHandleParallaxInDepthShaders())
|
||||
{
|
||||
OUT.m_worldPosition = worldPosition.xyz;
|
||||
|
||||
@@ -74,45 +73,28 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace)
|
||||
{
|
||||
PSDepthOutput OUT;
|
||||
|
||||
// Clip Alpha
|
||||
float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex];
|
||||
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
|
||||
float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
|
||||
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
|
||||
|
||||
OUT.m_depth = IN.m_position.z;
|
||||
|
||||
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
|
||||
|
||||
if(ShouldHandleParallaxInDepthShaders())
|
||||
{
|
||||
// We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space.
|
||||
float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) };
|
||||
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) };
|
||||
PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1);
|
||||
|
||||
float3 tangent = tangents[MaterialSrg::m_parallaxUvIndex];
|
||||
float3 bitangent = bitangents[MaterialSrg::m_parallaxUvIndex];
|
||||
|
||||
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
|
||||
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
|
||||
|
||||
float3 tangentOffset = GetParallaxOffset( MaterialSrg::m_depthFactor,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex],
|
||||
ViewSrg::m_worldPosition.xyz - IN.m_worldPosition,
|
||||
tangent,
|
||||
bitangent,
|
||||
IN.m_normal,
|
||||
uvMatrix);
|
||||
|
||||
PixelDepthOffset pdo = CalcPixelDepthOffset(MaterialSrg::m_depthFactor,
|
||||
tangentOffset,
|
||||
IN.m_worldPosition,
|
||||
tangent,
|
||||
bitangent,
|
||||
IN.m_normal,
|
||||
uvMatrixInverse,
|
||||
ObjectSrg::GetWorldMatrix(),
|
||||
ViewSrg::m_viewProjectionMatrix);
|
||||
OUT.m_depth = pdo.m_depth;
|
||||
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset,
|
||||
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth);
|
||||
}
|
||||
|
||||
// Clip Alpha
|
||||
float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex];
|
||||
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
|
||||
float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
|
||||
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
|
||||
|
||||
return OUT;
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ COMMON_OPTIONS_NORMAL()
|
||||
COMMON_OPTIONS_CLEAR_COAT()
|
||||
COMMON_OPTIONS_OCCLUSION()
|
||||
COMMON_OPTIONS_EMISSIVE()
|
||||
COMMON_OPTIONS_PARALLAX()
|
||||
COMMON_OPTIONS_DETAIL_MAPS()
|
||||
// Note COMMON_OPTIONS_PARALLAX is in StandardPBR_Common.azsli because it's needed by all StandardPBR shaders.
|
||||
|
||||
// Alpha
|
||||
#include "MaterialInputs/AlphaInput.azsli"
|
||||
@@ -102,8 +102,11 @@ VSOutput EnhancedPbr_ForwardPassVS(VSInput IN)
|
||||
// but we would need to address how it works with the parallax code below that indexes into the m_detailUV array.
|
||||
OUT.m_detailUv[0] = mul(MaterialSrg::m_detailUvMatrix, float3(IN.m_uv0, 1.0)).xy;
|
||||
OUT.m_detailUv[1] = mul(MaterialSrg::m_detailUvMatrix, float3(IN.m_uv1, 1.0)).xy;
|
||||
|
||||
// Shadow coords will be calculated in the pixel shader in this case
|
||||
bool skipShadowCoords = ShouldHandleParallax() && o_parallax_enablePixelDepthOffset;
|
||||
|
||||
VertexHelper(IN, OUT, worldPosition, o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset);
|
||||
VertexHelper(IN, OUT, worldPosition, skipShadowCoords);
|
||||
|
||||
return OUT;
|
||||
}
|
||||
@@ -132,9 +135,11 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
|
||||
// ------- Depth & Parallax -------
|
||||
|
||||
depth = IN.m_position.z;
|
||||
|
||||
bool displacementIsClipped = false;
|
||||
|
||||
// Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scatteirng is enabled
|
||||
if(!o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useDepthMap)
|
||||
if(ShouldHandleParallax())
|
||||
{
|
||||
// GetParallaxInput applies an tangent offset to the UV. We want to apply the same offset to the detailUv (note: this needs to be tested with content)
|
||||
// The math is: offset = newUv - oldUv; detailUv += offset;
|
||||
@@ -143,9 +148,9 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
|
||||
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
|
||||
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
|
||||
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor,
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset,
|
||||
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth);
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth, displacementIsClipped);
|
||||
|
||||
// Apply second part of the offset to the detail UV (see comment above)
|
||||
IN.m_detailUv[MaterialSrg::m_parallaxUvIndex] -= IN.m_uv[MaterialSrg::m_parallaxUvIndex];
|
||||
@@ -206,6 +211,11 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
|
||||
float3 baseColor = GetDetailedBaseColorInput(
|
||||
MaterialSrg::m_baseColorMap, MaterialSrg::m_sampler, baseColorUv, o_baseColor_useTexture, MaterialSrg::m_baseColor, MaterialSrg::m_baseColorFactor, o_baseColorTextureBlendMode,
|
||||
MaterialSrg::m_detail_baseColor_texture, MaterialSrg::m_sampler, detailUv, o_detail_baseColor_useTexture, detailLayerBaseColorFactor);
|
||||
|
||||
if(o_parallax_highlightClipping && displacementIsClipped)
|
||||
{
|
||||
ApplyParallaxClippingHighlight(baseColor);
|
||||
}
|
||||
|
||||
// ------- Metallic -------
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
#include <scenesrg.srgi>
|
||||
#include "EnhancedPBR_Common.azsli"
|
||||
#include <Atom/Features/PBR/AlphaUtils.azsli>
|
||||
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
|
||||
#include <Atom/Features/ParallaxMapping.azsli>
|
||||
#include <Atom/Features/MatrixUtility.azsli>
|
||||
@@ -54,8 +53,8 @@ VertexOutput MainVS(VertexInput IN)
|
||||
// By design, only UV0 is allowed to apply transforms.
|
||||
OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy;
|
||||
OUT.m_uv[1] = IN.m_uv1;
|
||||
|
||||
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
|
||||
|
||||
if(ShouldHandleParallaxInDepthShaders())
|
||||
{
|
||||
OUT.m_worldPosition = worldPosition.xyz;
|
||||
|
||||
@@ -75,57 +74,32 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace)
|
||||
{
|
||||
PSDepthOutput OUT;
|
||||
|
||||
OUT.m_depth = IN.m_position.z;
|
||||
|
||||
if(ShouldHandleParallaxInDepthShaders())
|
||||
{
|
||||
static const float ShadowMapDepthBias = 0.000001;
|
||||
|
||||
// We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space.
|
||||
float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) };
|
||||
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) };
|
||||
PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1);
|
||||
|
||||
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
|
||||
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
|
||||
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset,
|
||||
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth);
|
||||
|
||||
OUT.m_depth += ShadowMapDepthBias;
|
||||
}
|
||||
|
||||
// Clip Alpha
|
||||
float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex];
|
||||
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
|
||||
float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
|
||||
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
|
||||
|
||||
OUT.m_depth = IN.m_position.z;
|
||||
|
||||
float3 dirToCamera;
|
||||
if(ViewSrg::m_projectionMatrix[0].w)
|
||||
{
|
||||
// orthographic projection (directional light)
|
||||
// No view position, use light direction
|
||||
dirToCamera = ViewSrg::m_viewMatrix[2].xyz;
|
||||
}
|
||||
else
|
||||
{
|
||||
dirToCamera = ViewSrg::m_worldPosition.xyz - IN.m_worldPosition;
|
||||
}
|
||||
|
||||
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
|
||||
{
|
||||
// We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space.
|
||||
float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) };
|
||||
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) };
|
||||
PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1);
|
||||
|
||||
float3 tangent = tangents[MaterialSrg::m_parallaxUvIndex];
|
||||
float3 bitangent = bitangents[MaterialSrg::m_parallaxUvIndex];
|
||||
|
||||
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
|
||||
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
|
||||
|
||||
float3 tangentOffset = GetParallaxOffset( MaterialSrg::m_depthFactor,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex],
|
||||
dirToCamera,
|
||||
tangent,
|
||||
bitangent,
|
||||
IN.m_normal,
|
||||
uvMatrix);
|
||||
|
||||
PixelDepthOffset pdo = CalcPixelDepthOffset(MaterialSrg::m_depthFactor,
|
||||
tangentOffset,
|
||||
IN.m_worldPosition,
|
||||
tangent,
|
||||
bitangent,
|
||||
IN.m_normal,
|
||||
uvMatrixInverse,
|
||||
ObjectSrg::GetWorldMatrix(),
|
||||
ViewSrg::m_viewProjectionMatrix);
|
||||
OUT.m_depth = pdo.m_depth;
|
||||
}
|
||||
return OUT;
|
||||
}
|
||||
|
||||
+25
-13
@@ -24,16 +24,15 @@
|
||||
#define COMMON_SRG_INPUTS_PARALLAX(prefix) \
|
||||
Texture2D prefix##m_depthMap; \
|
||||
float prefix##m_depthFactor; \
|
||||
float prefix##m_depthOffset; \
|
||||
bool prefix##m_depthInverted;
|
||||
|
||||
#define COMMON_OPTIONS_PARALLAX(prefix) \
|
||||
option bool prefix##o_useDepthMap;
|
||||
|
||||
option bool o_parallax_feature_enabled;
|
||||
|
||||
void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float depthFactor,
|
||||
void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float depthFactor, float depthOffset,
|
||||
float4x4 objectWorldMatrix, float3x3 uvMatrix, float3x3 uvMatrixInverse,
|
||||
inout float2 uv, inout float3 worldPosition, inout float depth)
|
||||
inout float2 uv, inout float3 worldPosition, inout float depth, out bool isClipped)
|
||||
{
|
||||
if(o_parallax_feature_enabled)
|
||||
{
|
||||
@@ -49,20 +48,22 @@ void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float dep
|
||||
dirToCamera = ViewSrg::m_worldPosition.xyz - worldPosition;
|
||||
}
|
||||
|
||||
float3 tangentOffset = GetParallaxOffset( depthFactor,
|
||||
uv,
|
||||
dirToCamera,
|
||||
tangent,
|
||||
bitangent,
|
||||
normal,
|
||||
uvMatrix);
|
||||
ParallaxOffset tangentOffset = GetParallaxOffset( depthFactor,
|
||||
depthOffset,
|
||||
uv,
|
||||
dirToCamera,
|
||||
tangent,
|
||||
bitangent,
|
||||
normal,
|
||||
uvMatrix);
|
||||
|
||||
uv += tangentOffset.xy;
|
||||
uv += tangentOffset.m_offsetTS.xy;
|
||||
isClipped = tangentOffset.m_isClipped;
|
||||
|
||||
if(o_parallax_enablePixelDepthOffset)
|
||||
{
|
||||
PixelDepthOffset pdo = CalcPixelDepthOffset(depthFactor,
|
||||
tangentOffset,
|
||||
tangentOffset.m_offsetTS,
|
||||
worldPosition,
|
||||
tangent,
|
||||
bitangent,
|
||||
@@ -70,9 +71,20 @@ void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float dep
|
||||
uvMatrixInverse,
|
||||
objectWorldMatrix,
|
||||
ViewSrg::m_viewProjectionMatrix);
|
||||
|
||||
depth = pdo.m_depth;
|
||||
|
||||
worldPosition = pdo.m_worldPosition;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float depthFactor, float depthOffset,
|
||||
float4x4 objectWorldMatrix, float3x3 uvMatrix, float3x3 uvMatrixInverse,
|
||||
inout float2 uv, inout float3 worldPosition, inout float depth)
|
||||
{
|
||||
bool isClipped;
|
||||
GetParallaxInput(normal, tangent, bitangent, depthFactor, depthOffset, objectWorldMatrix, uvMatrix, uvMatrixInverse, uv, worldPosition, depth, isClipped);
|
||||
}
|
||||
|
||||
|
||||
+54
-119
@@ -18,11 +18,6 @@
|
||||
"displayName": "Parallax Settings",
|
||||
"description": "Properties for configuring the parallax effect, applied to all layers."
|
||||
},
|
||||
{
|
||||
"id": "opacity",
|
||||
"displayName": "Opacity",
|
||||
"description": "Properties for configuring the materials transparency."
|
||||
},
|
||||
{
|
||||
"id": "uv",
|
||||
"displayName": "UVs",
|
||||
@@ -361,19 +356,6 @@
|
||||
"id": "m_parallaxUvIndex"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "factor",
|
||||
"displayName": "Factor",
|
||||
"description": "Strength factor for scaling the depth values for all layers.",
|
||||
"type": "Float",
|
||||
"defaultValue": 1.0,
|
||||
"min": 0.0,
|
||||
"softMax": 2.0,
|
||||
"connection": {
|
||||
"type": "ShaderInput",
|
||||
"id": "m_parallaxMainDepthFactor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "algorithm",
|
||||
"displayName": "Algorithm",
|
||||
@@ -408,73 +390,17 @@
|
||||
"type": "ShaderOption",
|
||||
"id": "o_parallax_enablePixelDepthOffset"
|
||||
}
|
||||
}
|
||||
],
|
||||
"opacity": [
|
||||
},
|
||||
{
|
||||
"id": "mode",
|
||||
"displayName": "Opacity Mode",
|
||||
"description": "Opacity mode for this texture.",
|
||||
"type": "Enum",
|
||||
"enumValues": [ "Opaque", "Cutout", "Blended" ],
|
||||
"defaultValue": "Opaque",
|
||||
"id": "showClipping",
|
||||
"displayName": "Show Clipping",
|
||||
"description": "Highlight areas where the heightmap is clipped by the mesh surface.",
|
||||
"type": "Bool",
|
||||
"defaultValue": false,
|
||||
"connection": {
|
||||
"type": "ShaderOption",
|
||||
"id": "o_opacity_mode"
|
||||
"id": "o_parallax_highlightClipping"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "alphaSource",
|
||||
"displayName": "Alpha Source",
|
||||
"description": "Source texture of alpha value.",
|
||||
"type": "Enum",
|
||||
"enumValues": [ "Packed", "Split", "None" ],
|
||||
"defaultValue": "Packed",
|
||||
"connection": {
|
||||
"type": "ShaderOption",
|
||||
"id": "o_opacity_source"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "textureMap",
|
||||
"displayName": "Texture Map",
|
||||
"description": "Texture map for defining surface opacity.",
|
||||
"type": "Image",
|
||||
"connection": {
|
||||
"type": "ShaderInput",
|
||||
"id": "m_opacityMap"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "textureMapUv",
|
||||
"displayName": "UV",
|
||||
"description": "Opacity texture map UV set",
|
||||
"type": "Enum",
|
||||
"enumIsUv": true,
|
||||
"defaultValue": "Tiled",
|
||||
"connection": {
|
||||
"type": "ShaderInput",
|
||||
"id": "m_opacityMapUvIndex"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "factor",
|
||||
"displayName": "Factor",
|
||||
"description": "Factor for cutout threshold and blending",
|
||||
"type": "Float",
|
||||
"min": 0.0,
|
||||
"max": 1.0,
|
||||
"defaultValue": 0.5,
|
||||
"connection": {
|
||||
"type": "ShaderInput",
|
||||
"id": "m_opacityFactor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "doubleSided",
|
||||
"displayName": "Double-sided",
|
||||
"description": "Whether to render back-faces or just front-faces.",
|
||||
"type": "Bool"
|
||||
}
|
||||
],
|
||||
"uv": [
|
||||
@@ -1343,8 +1269,8 @@
|
||||
},
|
||||
{
|
||||
"id": "factor",
|
||||
"displayName": "Factor",
|
||||
"description": "Strength factor for scaling the depth values",
|
||||
"displayName": "Heightmap Scale",
|
||||
"description": "The total height of the heightmap in local model units.",
|
||||
"type": "Float",
|
||||
"defaultValue": 0.0,
|
||||
"min": 0.0,
|
||||
@@ -1354,6 +1280,19 @@
|
||||
"id": "m_layer1_m_depthFactor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "offset",
|
||||
"displayName": "Offset",
|
||||
"description": "Adjusts the overall displacement amount in local model units.",
|
||||
"type": "Float",
|
||||
"defaultValue": 0.0,
|
||||
"softMin": -0.1,
|
||||
"softMax": 0.1,
|
||||
"connection": {
|
||||
"type": "ShaderInput",
|
||||
"id": "m_layer1_m_depthOffset"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "invert",
|
||||
"displayName": "Invert",
|
||||
@@ -2036,8 +1975,8 @@
|
||||
},
|
||||
{
|
||||
"id": "factor",
|
||||
"displayName": "Factor",
|
||||
"description": "Strength factor for scaling the depth values",
|
||||
"displayName": "Heightmap Scale",
|
||||
"description": "The total height of the heightmap in local model units.",
|
||||
"type": "Float",
|
||||
"defaultValue": 0.0,
|
||||
"min": 0.0,
|
||||
@@ -2047,6 +1986,19 @@
|
||||
"id": "m_layer2_m_depthFactor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "offset",
|
||||
"displayName": "Offset",
|
||||
"description": "Adjusts the overall displacement amount in local model units.",
|
||||
"type": "Float",
|
||||
"defaultValue": 0.0,
|
||||
"softMin": -0.1,
|
||||
"softMax": 0.1,
|
||||
"connection": {
|
||||
"type": "ShaderInput",
|
||||
"id": "m_layer2_m_depthOffset"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "invert",
|
||||
"displayName": "Invert",
|
||||
@@ -2729,8 +2681,8 @@
|
||||
},
|
||||
{
|
||||
"id": "factor",
|
||||
"displayName": "Factor",
|
||||
"description": "Strength factor for scaling the depth values",
|
||||
"displayName": "Heightmap Scale",
|
||||
"description": "The total height of the heightmap in local model units.",
|
||||
"type": "Float",
|
||||
"defaultValue": 0.0,
|
||||
"min": 0.0,
|
||||
@@ -2740,6 +2692,19 @@
|
||||
"id": "m_layer3_m_depthFactor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "offset",
|
||||
"displayName": "Offset",
|
||||
"description": "Adjusts the overall displacement amount in local model units.",
|
||||
"type": "Float",
|
||||
"defaultValue": 0.0,
|
||||
"softMin": -0.1,
|
||||
"softMax": 0.1,
|
||||
"connection": {
|
||||
"type": "ShaderInput",
|
||||
"id": "m_layer3_m_depthOffset"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "invert",
|
||||
"displayName": "Invert",
|
||||
@@ -2851,16 +2816,7 @@
|
||||
{
|
||||
"file": "Shaders/MotionVector/SkinnedMeshMotionVector.shader",
|
||||
"tag": "SkinnedMeshMotionVector"
|
||||
},
|
||||
// Used by the light culling system to produce accurate depth bounds for this object when it uses blended transparency
|
||||
{
|
||||
"file": "Shaders/Depth/DepthPassTransparentMin.shader",
|
||||
"tag": "DepthPassTransparentMin"
|
||||
},
|
||||
{
|
||||
"file": "Shaders/Depth/DepthPassTransparentMax.shader",
|
||||
"tag": "DepthPassTransparentMax"
|
||||
}
|
||||
}
|
||||
],
|
||||
"functors": [
|
||||
//##############################################################################################
|
||||
@@ -2885,7 +2841,7 @@
|
||||
{
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
"file": "StandardPBR_ShaderEnable.lua"
|
||||
"file": "StandardMultilayerPBR_ShaderEnable.lua"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2925,27 +2881,6 @@
|
||||
"file": "StandardPBR_SubsurfaceState.lua"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
"file": "StandardPBR_HandleOpacityDoubleSided.lua"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "OverrideDrawList",
|
||||
"args": {
|
||||
"triggerProperty": "opacity.mode",
|
||||
"triggerValue": "Blended",
|
||||
"shaderIndex": 1,
|
||||
"drawList": "transparent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
"file": "StandardPBR_HandleOpacityMode.lua"
|
||||
}
|
||||
},
|
||||
//##############################################################################################
|
||||
// Layer 1 Functors
|
||||
//##############################################################################################
|
||||
|
||||
+36
-12
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <Atom/Features/SrgSemantics.azsli>
|
||||
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
|
||||
#include <Atom/Features/PBR/LightingOptions.azsli>
|
||||
|
||||
#include "MaterialInputs/BaseColorInput.azsli"
|
||||
#include "MaterialInputs/RoughnessInput.azsli"
|
||||
@@ -26,6 +27,8 @@
|
||||
#include "MaterialInputs/ParallaxInput.azsli"
|
||||
#include "MaterialInputs/UvSetCount.azsli"
|
||||
|
||||
// ------ ShaderResourceGroup ----------------------------------------
|
||||
|
||||
#define DEFINE_LAYER_SRG_INPUTS(prefix) \
|
||||
COMMON_SRG_INPUTS_BASE_COLOR(prefix) \
|
||||
COMMON_SRG_INPUTS_ROUGHNESS(prefix) \
|
||||
@@ -57,17 +60,16 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
|
||||
float4 m_pad3; // [GFX TODO][ATOM-14595] This is a workaround for a data stomping bug. Remove once it's fixed.
|
||||
|
||||
uint m_parallaxUvIndex;
|
||||
float m_parallaxMainDepthFactor;
|
||||
|
||||
// These are used to limit the heightmap intersection search range to the narrowest band possible, to give the best quality result.
|
||||
float m_displacementMin; // The lowest displacement value possible from all layers combined
|
||||
float m_displacementMax; // The highest displacement value possible from all layers combined
|
||||
|
||||
float3x3 m_uvMatrix;
|
||||
float4 m_pad4; // [GFX TODO][ATOM-14595] This is a workaround for a data stomping bug. Remove once it's fixed.
|
||||
float3x3 m_uvMatrixInverse;
|
||||
float4 m_pad5; // [GFX TODO][ATOM-14595] This is a workaround for a data stomping bug. Remove once it's fixed.
|
||||
|
||||
float m_opacityFactor;
|
||||
Texture2D m_opacityMap;
|
||||
uint m_opacityMapUvIndex;
|
||||
|
||||
Sampler m_sampler
|
||||
{
|
||||
AddressU = Wrap;
|
||||
@@ -109,6 +111,8 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
|
||||
uint m_transmissionThicknessMapUvIndex;
|
||||
}
|
||||
|
||||
// ------ Shader Options ----------------------------------------
|
||||
|
||||
enum class DebugDrawMode { None, BlendMaskValues, DepthMaps };
|
||||
option DebugDrawMode o_debugDrawMode;
|
||||
|
||||
@@ -121,6 +125,8 @@ option BlendMaskSource o_blendSource;
|
||||
// [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream.
|
||||
option bool o_blendMask_isBound;
|
||||
|
||||
// ------ Blend Utilities ----------------------------------------
|
||||
|
||||
//! Returns the BlendMaskSource that will actually be used when rendering (not necessarily the same BlendMaskSource specified by the user)
|
||||
BlendMaskSource GetFinalBlendMaskSource()
|
||||
{
|
||||
@@ -181,6 +187,22 @@ float3 BlendLayers(float3 layer1, float3 layer2, float3 layer3, float3 blendMask
|
||||
return layer1 * blendMaskValues.r + layer2 * blendMaskValues.g + layer3 * blendMaskValues.b;
|
||||
}
|
||||
|
||||
// ------ Parallax Utilities ----------------------------------------
|
||||
|
||||
bool ShouldHandleParallax()
|
||||
{
|
||||
// Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scattering is enabled.
|
||||
// Also, all the debug draw modes avoid parallax (they early-return before parallax code actually) so you can see exactly where the various maps appear on the surface UV space.
|
||||
return !o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_debugDrawMode == DebugDrawMode::None;
|
||||
}
|
||||
|
||||
bool ShouldHandleParallaxInDepthShaders()
|
||||
{
|
||||
// The depth pass shaders need to calculate parallax when the result could affect the depth buffer (or when
|
||||
// parallax could affect texel clipping but we don't have alpha/clipping support in multilayer PBR).
|
||||
return ShouldHandleParallax() && o_parallax_enablePixelDepthOffset;
|
||||
}
|
||||
|
||||
// These static values are used to pass extra data to the GetDepth callback function during the parallax depth search.
|
||||
static float3 s_blendMaskFromVertexStream;
|
||||
|
||||
@@ -192,7 +214,7 @@ void GetDepth_Setup(float3 vertexBlendMask)
|
||||
}
|
||||
|
||||
// Callback function for ParallaxMapping.azsli
|
||||
float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
{
|
||||
float3 layerDepthValues = float3(0,0,0);
|
||||
|
||||
@@ -204,8 +226,9 @@ float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
layerUv = mul(MaterialSrg::m_layer1_m_uvMatrix, float3(uv, 1.0)).xy;
|
||||
}
|
||||
|
||||
layerDepthValues.r = SampleDepthOrHeightMap(MaterialSrg::m_layer1_m_depthInverted, MaterialSrg::m_layer1_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy);
|
||||
layerDepthValues.r = SampleDepthOrHeightMap(MaterialSrg::m_layer1_m_depthInverted, MaterialSrg::m_layer1_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth;
|
||||
layerDepthValues.r *= MaterialSrg::m_layer1_m_depthFactor;
|
||||
layerDepthValues.r -= MaterialSrg::m_layer1_m_depthOffset;
|
||||
}
|
||||
|
||||
if(o_layer2_o_useDepthMap)
|
||||
@@ -216,8 +239,9 @@ float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
layerUv = mul(MaterialSrg::m_layer2_m_uvMatrix, float3(uv, 1.0)).xy;
|
||||
}
|
||||
|
||||
layerDepthValues.g = SampleDepthOrHeightMap(MaterialSrg::m_layer2_m_depthInverted, MaterialSrg::m_layer2_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy);
|
||||
layerDepthValues.g = SampleDepthOrHeightMap(MaterialSrg::m_layer2_m_depthInverted, MaterialSrg::m_layer2_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth;
|
||||
layerDepthValues.g *= MaterialSrg::m_layer2_m_depthFactor;
|
||||
layerDepthValues.g -= MaterialSrg::m_layer2_m_depthOffset;
|
||||
}
|
||||
|
||||
if(o_layer3_o_useDepthMap)
|
||||
@@ -228,8 +252,9 @@ float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
layerUv = mul(MaterialSrg::m_layer3_m_uvMatrix, float3(uv, 1.0)).xy;
|
||||
}
|
||||
|
||||
layerDepthValues.b = SampleDepthOrHeightMap(MaterialSrg::m_layer3_m_depthInverted, MaterialSrg::m_layer3_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy);
|
||||
layerDepthValues.b = SampleDepthOrHeightMap(MaterialSrg::m_layer3_m_depthInverted, MaterialSrg::m_layer3_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth;
|
||||
layerDepthValues.b *= MaterialSrg::m_layer3_m_depthFactor;
|
||||
layerDepthValues.b -= MaterialSrg::m_layer3_m_depthOffset;
|
||||
}
|
||||
|
||||
// Note, when the blend source is BlendMaskSource::VertexColors, parallax will not be able to blend correctly between layers. It will end up using the same blend mask values
|
||||
@@ -237,7 +262,6 @@ float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
// you have a small depth factor relative to the size of the blend transition.
|
||||
float3 blendMaskValues = GetBlendMaskValues(uv, s_blendMaskFromVertexStream);
|
||||
|
||||
float3 depth = BlendLayers(layerDepthValues.r, layerDepthValues.g, layerDepthValues.b, blendMaskValues);
|
||||
|
||||
return depth;
|
||||
float depth = BlendLayers(layerDepthValues.r, layerDepthValues.g, layerDepthValues.b, blendMaskValues);
|
||||
return DepthResultAbsolute(depth);
|
||||
}
|
||||
|
||||
+5
-14
@@ -11,12 +11,10 @@
|
||||
*/
|
||||
|
||||
#include <viewsrg.srgi>
|
||||
#include <Atom/Features/PBR/AlphaUtils.azsli>
|
||||
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
|
||||
#include <Atom/Features/ParallaxMapping.azsli>
|
||||
#include <Atom/Features/MatrixUtility.azsli>
|
||||
|
||||
#include "MaterialInputs/AlphaInput.azsli"
|
||||
#include "MaterialInputs/ParallaxInput.azsli"
|
||||
|
||||
|
||||
@@ -72,7 +70,7 @@ VSDepthOutput MainVS(VSInput IN)
|
||||
OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy;
|
||||
OUT.m_uv[1] = IN.m_uv1;
|
||||
|
||||
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
|
||||
if(ShouldHandleParallaxInDepthShaders())
|
||||
{
|
||||
OUT.m_worldPosition = worldPosition.xyz;
|
||||
|
||||
@@ -101,18 +99,9 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace)
|
||||
{
|
||||
PSDepthOutput OUT;
|
||||
|
||||
// Alpha
|
||||
float2 layer1_baseColorUV = IN.m_uv[MaterialSrg::m_layer1_m_baseColorMapUvIndex];
|
||||
float2 layer2_baseColorUV = IN.m_uv[MaterialSrg::m_layer2_m_baseColorMapUvIndex];
|
||||
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
|
||||
// [GFX TODO][ATOM-14589] Figure out how to deal with opacity, instead of just hard-coding to layer1
|
||||
float alpha = SampleAlpha(MaterialSrg::m_layer1_m_baseColorMap, MaterialSrg::m_opacityMap, layer1_baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
|
||||
|
||||
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
|
||||
|
||||
OUT.m_depth = IN.m_position.z;
|
||||
|
||||
if(o_debugDrawMode == DebugDrawMode::None && o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
|
||||
if(ShouldHandleParallaxInDepthShaders())
|
||||
{
|
||||
// We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space.
|
||||
float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) };
|
||||
@@ -126,7 +115,9 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace)
|
||||
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
|
||||
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
|
||||
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_parallaxMainDepthFactor,
|
||||
float parallaxOverallOffset = MaterialSrg::m_displacementMax;
|
||||
float parallaxOverallFactor = MaterialSrg::m_displacementMax - MaterialSrg::m_displacementMin;
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], parallaxOverallFactor, parallaxOverallOffset,
|
||||
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth);
|
||||
|
||||
|
||||
+24
-41
@@ -55,7 +55,6 @@ DEFINE_LAYER_OPTIONS(o_layer1_)
|
||||
DEFINE_LAYER_OPTIONS(o_layer2_)
|
||||
DEFINE_LAYER_OPTIONS(o_layer3_)
|
||||
|
||||
#include "MaterialInputs/AlphaInput.azsli"
|
||||
#include "MaterialInputs/SubsurfaceInput.azsli"
|
||||
#include "MaterialInputs/TransmissionInput.azsli"
|
||||
#include "StandardMultilayerPBR_Common.azsli"
|
||||
@@ -121,9 +120,8 @@ VSOutput ForwardPassVS(VSInput IN)
|
||||
OUT.m_blendMask = float3(1,1,1);
|
||||
}
|
||||
|
||||
// We can skip per-vertex shadow coords when parallax is enabled because we need to calculate per-pixel shadow coords anyway.
|
||||
// We cannot skip shadow coords when o_debugDrawMode is on because some debug draw modes return before parallax.
|
||||
bool skipShadowCoords = o_debugDrawMode == DebugDrawMode::None && o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset;
|
||||
// Shadow coords will be calculated in the pixel shader in this case
|
||||
bool skipShadowCoords = ShouldHandleParallax() && o_parallax_enablePixelDepthOffset;
|
||||
VertexHelper(IN, OUT, worldPosition, skipShadowCoords);
|
||||
|
||||
return OUT;
|
||||
@@ -167,23 +165,27 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
|
||||
if(o_debugDrawMode == DebugDrawMode::DepthMaps)
|
||||
{
|
||||
GetDepth_Setup(IN.m_blendMask);
|
||||
float depth = GetDepth(IN.m_uv[MaterialSrg::m_parallaxUvIndex], float2(0,0), float2(0,0));
|
||||
float depth = GetNormalizedDepth(-MaterialSrg::m_displacementMax, -MaterialSrg::m_displacementMin, IN.m_uv[MaterialSrg::m_parallaxUvIndex], float2(0,0), float2(0,0));
|
||||
return DebugOutput(float3(depth,depth,depth));
|
||||
}
|
||||
|
||||
// ------- Parallax -------
|
||||
|
||||
bool displacementIsClipped = false;
|
||||
|
||||
// Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scatteirng is enabled
|
||||
if(!o_enableSubsurfaceScattering && o_parallax_feature_enabled)
|
||||
if(ShouldHandleParallax())
|
||||
{
|
||||
GetDepth_Setup(IN.m_blendMask);
|
||||
|
||||
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
|
||||
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
|
||||
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_parallaxMainDepthFactor,
|
||||
|
||||
float parallaxOverallOffset = MaterialSrg::m_displacementMax;
|
||||
float parallaxOverallFactor = MaterialSrg::m_displacementMax - MaterialSrg::m_displacementMin;
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], parallaxOverallFactor, parallaxOverallOffset,
|
||||
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth);
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth, displacementIsClipped);
|
||||
|
||||
// Adjust directional light shadow coorinates for parallax correction
|
||||
if(o_parallax_enablePixelDepthOffset)
|
||||
@@ -218,15 +220,6 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
|
||||
// Now that any parallax has been calculated, we calculate the blend factors for any layers that are impacted by the parallax.
|
||||
float3 blendMaskValues = GetBlendMaskValues(IN.m_uv[MaterialSrg::m_blendMaskUvIndex], IN.m_blendMask);
|
||||
|
||||
// ------- Alpha & Clip -------
|
||||
|
||||
float2 layer1_baseColorUv = uvLayer1[MaterialSrg::m_layer1_m_baseColorMapUvIndex];
|
||||
float2 layer2_baseColorUv = uvLayer2[MaterialSrg::m_layer2_m_baseColorMapUvIndex];
|
||||
float2 layer3_baseColorUv = uvLayer3[MaterialSrg::m_layer3_m_baseColorMapUvIndex];
|
||||
float2 opacityUv = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
|
||||
// [GFX TODO][ATOM-14589] Figure out how to deal with opacity, instead of just hard-coding to layer1
|
||||
float alpha = GetAlphaInputAndClip(MaterialSrg::m_layer1_m_baseColorMap, MaterialSrg::m_opacityMap, layer1_baseColorUv, opacityUv, MaterialSrg::m_sampler, MaterialSrg::m_opacityFactor, o_opacity_source);
|
||||
|
||||
// ------- Normal -------
|
||||
|
||||
float3 layer1_normalFactor = MaterialSrg::m_layer1_m_normalFactor * blendMaskValues.r;
|
||||
@@ -245,6 +238,10 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
|
||||
surface.normal = normalize(TangentSpaceToWorld(normalTS, IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex]));
|
||||
|
||||
// ------- Base Color -------
|
||||
|
||||
float2 layer1_baseColorUv = uvLayer1[MaterialSrg::m_layer1_m_baseColorMapUvIndex];
|
||||
float2 layer2_baseColorUv = uvLayer2[MaterialSrg::m_layer2_m_baseColorMapUvIndex];
|
||||
float2 layer3_baseColorUv = uvLayer3[MaterialSrg::m_layer3_m_baseColorMapUvIndex];
|
||||
|
||||
float3 layer1_sampledColor = GetBaseColorInput(MaterialSrg::m_layer1_m_baseColorMap, MaterialSrg::m_sampler, layer1_baseColorUv, MaterialSrg::m_layer1_m_baseColor.rgb, o_layer1_o_baseColor_useTexture);
|
||||
float3 layer2_sampledColor = GetBaseColorInput(MaterialSrg::m_layer2_m_baseColorMap, MaterialSrg::m_sampler, layer2_baseColorUv, MaterialSrg::m_layer2_m_baseColor.rgb, o_layer2_o_baseColor_useTexture);
|
||||
@@ -253,6 +250,11 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
|
||||
float3 layer2_baseColor = BlendBaseColor(layer2_sampledColor, MaterialSrg::m_layer2_m_baseColor.rgb, MaterialSrg::m_layer2_m_baseColorFactor, o_layer2_o_baseColorTextureBlendMode, o_layer2_o_baseColor_useTexture);
|
||||
float3 layer3_baseColor = BlendBaseColor(layer3_sampledColor, MaterialSrg::m_layer3_m_baseColor.rgb, MaterialSrg::m_layer3_m_baseColorFactor, o_layer3_o_baseColorTextureBlendMode, o_layer3_o_baseColor_useTexture);
|
||||
float3 baseColor = BlendLayers(layer1_baseColor, layer2_baseColor, layer3_baseColor, blendMaskValues);
|
||||
|
||||
if(o_parallax_highlightClipping && displacementIsClipped)
|
||||
{
|
||||
ApplyParallaxClippingHighlight(baseColor);
|
||||
}
|
||||
|
||||
// ------- Metallic -------
|
||||
|
||||
@@ -427,32 +429,13 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
|
||||
lightingData.FinalizeLighting(surface.transmission.tint);
|
||||
|
||||
|
||||
if (o_opacity_mode == OpacityMode::Blended || o_opacity_mode == OpacityMode::TintedTransparent)
|
||||
{
|
||||
alpha = FresnelSchlickWithRoughness(lightingData.NdotV, alpha, surface.roughnessLinear).x; // Increase opacity at grazing angles.
|
||||
}
|
||||
const float alpha = 1.0;
|
||||
|
||||
PbrLightingOutput lightingOutput = GetPbrLightingOutput(surface, lightingData, alpha);
|
||||
|
||||
// ------- Opacity -------
|
||||
|
||||
if (o_opacity_mode == OpacityMode::Blended)
|
||||
{
|
||||
// [GFX_TODO ATOM-13187] PbrLighting shouldn't be writing directly to render targets. It's confusing when
|
||||
// specular is being added to diffuse just because we're calling render target 0 "diffuse".
|
||||
|
||||
// For blended mode, we do (dest * alpha) + (source * 1.0). This allows the specular
|
||||
// to be added on top of the diffuse, but then the diffuse must be pre-multiplied.
|
||||
// It's done this way because surface transparency doesn't really change specular response (eg, glass).
|
||||
lightingOutput.m_diffuseColor.rgb *= lightingOutput.m_diffuseColor.w; // pre-multiply diffuse
|
||||
lightingOutput.m_diffuseColor.rgb += lightingOutput.m_specularColor.rgb; // add specular
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pack factor and quality, drawback: because of precision limit of float16 cannot represent exact 1, maximum representable value is 0.9961
|
||||
uint factorAndQuality = dot(round(float2(saturate(surfaceScatteringFactor), MaterialSrg::m_subsurfaceScatteringQuality) * 255), float2(256, 1));
|
||||
lightingOutput.m_diffuseColor.w = factorAndQuality * (o_enableSubsurfaceScattering ? 1.0 : -1.0);
|
||||
}
|
||||
// Pack factor and quality, drawback: because of precision limit of float16 cannot represent exact 1, maximum representable value is 0.9961
|
||||
uint factorAndQuality = dot(round(float2(saturate(surfaceScatteringFactor), MaterialSrg::m_subsurfaceScatteringQuality) * 255), float2(256, 1));
|
||||
lightingOutput.m_diffuseColor.w = factorAndQuality * (o_enableSubsurfaceScattering ? 1.0 : -1.0);
|
||||
|
||||
|
||||
return lightingOutput;
|
||||
|
||||
+33
-19
@@ -20,10 +20,12 @@ function GetMaterialPropertyDependencies()
|
||||
"layer1_parallax.enable",
|
||||
"layer2_parallax.enable",
|
||||
"layer3_parallax.enable",
|
||||
"parallax.factor",
|
||||
"layer1_parallax.factor",
|
||||
"layer2_parallax.factor",
|
||||
"layer3_parallax.factor"
|
||||
"layer3_parallax.factor",
|
||||
"layer1_parallax.offset",
|
||||
"layer2_parallax.offset",
|
||||
"layer3_parallax.offset"
|
||||
}
|
||||
end
|
||||
|
||||
@@ -31,6 +33,23 @@ function GetShaderOptionDependencies()
|
||||
return {"o_parallax_feature_enabled"}
|
||||
end
|
||||
|
||||
function MergeRange(heightMinMax, offset, factor)
|
||||
top = offset
|
||||
bottom = offset - factor
|
||||
|
||||
if(heightMinMax[1] == nil) then
|
||||
heightMinMax[1] = top
|
||||
else
|
||||
heightMinMax[1] = math.max(heightMinMax[1], top)
|
||||
end
|
||||
|
||||
if(heightMinMax[0] == nil) then
|
||||
heightMinMax[0] = bottom
|
||||
else
|
||||
heightMinMax[0] = math.min(heightMinMax[0], bottom)
|
||||
end
|
||||
end
|
||||
|
||||
function Process(context)
|
||||
local enableParallax = context:GetMaterialPropertyValue_bool("parallax.enable")
|
||||
local enable1 = context:GetMaterialPropertyValue_bool("layer1_parallax.enable")
|
||||
@@ -39,30 +58,25 @@ function Process(context)
|
||||
enableParallax = enableParallax and (enable1 or enable2 or enable3)
|
||||
context:SetShaderOptionValue_bool("o_parallax_feature_enabled", enableParallax)
|
||||
|
||||
-- Smaller values for the main parallax factor used in GetParallaxOffset() give better quality.
|
||||
-- So increase the per-layer parallax factors by normalizing them, and reduce the main factor accordingly.
|
||||
if(enableParallax) then
|
||||
local factorLayer1 = context:GetMaterialPropertyValue_float("layer1_parallax.factor")
|
||||
local factorLayer2 = context:GetMaterialPropertyValue_float("layer2_parallax.factor")
|
||||
local factorLayer3 = context:GetMaterialPropertyValue_float("layer3_parallax.factor")
|
||||
local mainFactor = context:GetMaterialPropertyValue_float("parallax.factor")
|
||||
|
||||
maxLayerFactor = 0.0
|
||||
if(enable1) then maxLayerFactor = math.max(maxLayerFactor, factorLayer1) end
|
||||
if(enable2) then maxLayerFactor = math.max(maxLayerFactor, factorLayer2) end
|
||||
if(enable3) then maxLayerFactor = math.max(maxLayerFactor, factorLayer3) end
|
||||
local offsetLayer1 = context:GetMaterialPropertyValue_float("layer1_parallax.offset")
|
||||
local offsetLayer2 = context:GetMaterialPropertyValue_float("layer2_parallax.offset")
|
||||
local offsetLayer3 = context:GetMaterialPropertyValue_float("layer3_parallax.offset")
|
||||
|
||||
if(maxLayerFactor < 0.0001) then
|
||||
local heightMinMax = {nil, nil}
|
||||
if(enable1) then MergeRange(heightMinMax, offsetLayer1, factorLayer1) end
|
||||
if(enable2) then MergeRange(heightMinMax, offsetLayer2, factorLayer2) end
|
||||
if(enable3) then MergeRange(heightMinMax, offsetLayer3, factorLayer3) end
|
||||
|
||||
if(heightMinMax[1] - heightMinMax[0] < 0.0001) then
|
||||
context:SetShaderOptionValue_bool("o_parallax_feature_enabled", false)
|
||||
else
|
||||
factorLayer1 = factorLayer1 / maxLayerFactor
|
||||
factorLayer2 = factorLayer2 / maxLayerFactor
|
||||
factorLayer3 = factorLayer3 / maxLayerFactor
|
||||
mainFactor = mainFactor * maxLayerFactor;
|
||||
context:SetShaderConstant_float("m_layer1_m_depthFactor", factorLayer1)
|
||||
context:SetShaderConstant_float("m_layer2_m_depthFactor", factorLayer2)
|
||||
context:SetShaderConstant_float("m_layer3_m_depthFactor", factorLayer3)
|
||||
context:SetShaderConstant_float("m_parallaxMainDepthFactor", mainFactor)
|
||||
context:SetShaderConstant_float("m_displacementMin", heightMinMax[0])
|
||||
context:SetShaderConstant_float("m_displacementMax", heightMinMax[1])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -76,8 +90,8 @@ function ProcessEditor(context)
|
||||
end
|
||||
|
||||
context:SetMaterialPropertyVisibility("parallax.parallaxUv", visibility)
|
||||
context:SetMaterialPropertyVisibility("parallax.factor", visibility)
|
||||
context:SetMaterialPropertyVisibility("parallax.algorithm", visibility)
|
||||
context:SetMaterialPropertyVisibility("parallax.quality", visibility)
|
||||
context:SetMaterialPropertyVisibility("parallax.pdo", visibility)
|
||||
context:SetMaterialPropertyVisibility("parallax.showClipping", visibility)
|
||||
end
|
||||
|
||||
+2
-1
@@ -42,7 +42,8 @@ function ProcessEditor(context)
|
||||
if(not enable or textureMap == nil) then
|
||||
visibility = MaterialPropertyVisibility_Hidden
|
||||
end
|
||||
|
||||
|
||||
context:SetMaterialPropertyVisibility("parallax.factor", visibility)
|
||||
context:SetMaterialPropertyVisibility("parallax.offset", visibility)
|
||||
context:SetMaterialPropertyVisibility("parallax.invert", visibility)
|
||||
end
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
--------------------------------------------------------------------------------------
|
||||
--
|
||||
-- All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
-- its licensors.
|
||||
--
|
||||
-- For complete copyright and license terms please see the LICENSE at the root of this
|
||||
-- distribution (the "License"). All use of this software is governed by the License,
|
||||
-- or, if provided, by the license below or the license accompanying this file. Do not
|
||||
-- remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
--
|
||||
--
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
function GetMaterialPropertyDependencies()
|
||||
return {"parallax.enable", "parallax.pdo"}
|
||||
end
|
||||
|
||||
function Process(context)
|
||||
local parallaxEnabled = context:GetMaterialPropertyValue_bool("parallax.enable")
|
||||
local parallaxPdoEnabled = context:GetMaterialPropertyValue_bool("parallax.pdo")
|
||||
|
||||
local depthPass = context:GetShaderByTag("DepthPass")
|
||||
local shadowMap = context:GetShaderByTag("Shadowmap")
|
||||
local forwardPassEDS = context:GetShaderByTag("ForwardPass_EDS")
|
||||
local depthPassWithPS = context:GetShaderByTag("DepthPass_WithPS")
|
||||
local shadowMapWitPS = context:GetShaderByTag("Shadowmap_WithPS")
|
||||
local forwardPass = context:GetShaderByTag("ForwardPass")
|
||||
|
||||
local shadingAffectsDepth = parallaxEnabled and parallaxPdoEnabled;
|
||||
|
||||
depthPass:SetEnabled(not shadingAffectsDepth)
|
||||
shadowMap:SetEnabled(not shadingAffectsDepth)
|
||||
forwardPassEDS:SetEnabled(not shadingAffectsDepth)
|
||||
|
||||
depthPassWithPS:SetEnabled(shadingAffectsDepth)
|
||||
shadowMapWitPS:SetEnabled(shadingAffectsDepth)
|
||||
forwardPass:SetEnabled(shadingAffectsDepth)
|
||||
end
|
||||
+7
-16
@@ -12,12 +12,10 @@
|
||||
|
||||
#include <scenesrg.srgi>
|
||||
#include <viewsrg.srgi>
|
||||
#include <Atom/Features/PBR/AlphaUtils.azsli>
|
||||
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
|
||||
#include <Atom/Features/ParallaxMapping.azsli>
|
||||
#include <Atom/Features/MatrixUtility.azsli>
|
||||
|
||||
#include "MaterialInputs/AlphaInput.azsli"
|
||||
#include "MaterialInputs/ParallaxInput.azsli"
|
||||
|
||||
#include "MaterialInputs/ParallaxInput.azsli"
|
||||
@@ -71,7 +69,7 @@ VertexOutput MainVS(VertexInput IN)
|
||||
OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy;
|
||||
OUT.m_uv[1] = IN.m_uv1;
|
||||
|
||||
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
|
||||
if(ShouldHandleParallaxInDepthShaders())
|
||||
{
|
||||
OUT.m_worldPosition = worldPosition.xyz;
|
||||
|
||||
@@ -100,18 +98,9 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace)
|
||||
{
|
||||
PSDepthOutput OUT;
|
||||
|
||||
// Alpha
|
||||
float2 layer1_baseColorUV = IN.m_uv[MaterialSrg::m_layer1_m_baseColorMapUvIndex];
|
||||
float2 layer2_baseColorUV = IN.m_uv[MaterialSrg::m_layer2_m_baseColorMapUvIndex];
|
||||
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
|
||||
// [GFX TODO][ATOM-14589] Figure out how to deal with opacity, instead of just hard-coding to layer1
|
||||
float alpha = SampleAlpha(MaterialSrg::m_layer1_m_baseColorMap, MaterialSrg::m_opacityMap, layer1_baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
|
||||
|
||||
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
|
||||
|
||||
OUT.m_depth = IN.m_position.z;
|
||||
|
||||
if(o_debugDrawMode == DebugDrawMode::None && o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
|
||||
if(ShouldHandleParallaxInDepthShaders())
|
||||
{
|
||||
// We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space.
|
||||
float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) };
|
||||
@@ -124,13 +113,15 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace)
|
||||
|
||||
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
|
||||
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
|
||||
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_parallaxMainDepthFactor,
|
||||
|
||||
float parallaxOverallOffset = MaterialSrg::m_displacementMax;
|
||||
float parallaxOverallFactor = MaterialSrg::m_displacementMax - MaterialSrg::m_displacementMin;
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], parallaxOverallFactor, parallaxOverallOffset,
|
||||
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth);
|
||||
|
||||
OUT.m_depth = depth;
|
||||
}
|
||||
|
||||
|
||||
return OUT;
|
||||
}
|
||||
|
||||
@@ -910,8 +910,8 @@
|
||||
},
|
||||
{
|
||||
"id": "factor",
|
||||
"displayName": "Factor",
|
||||
"description": "Strength factor for scaling the depth values",
|
||||
"displayName": "Heightmap Scale",
|
||||
"description": "The total height of the heightmap in local model units.",
|
||||
"type": "Float",
|
||||
"defaultValue": 0.0,
|
||||
"min": 0.0,
|
||||
@@ -921,6 +921,19 @@
|
||||
"id": "m_depthFactor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "offset",
|
||||
"displayName": "Offset",
|
||||
"description": "Adjusts the overall displacement amount in local model units.",
|
||||
"type": "Float",
|
||||
"defaultValue": 0.0,
|
||||
"softMin": -0.1,
|
||||
"softMax": 0.1,
|
||||
"connection": {
|
||||
"type": "ShaderInput",
|
||||
"id": "m_depthOffset"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "invert",
|
||||
"displayName": "Invert",
|
||||
@@ -966,6 +979,17 @@
|
||||
"type": "ShaderOption",
|
||||
"id": "o_parallax_enablePixelDepthOffset"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "showClipping",
|
||||
"displayName": "Show Clipping",
|
||||
"description": "Highlight areas where the heightmap is clipped by the mesh surface.",
|
||||
"type": "Bool",
|
||||
"defaultValue": false,
|
||||
"connection": {
|
||||
"type": "ShaderOption",
|
||||
"id": "o_parallax_highlightClipping"
|
||||
}
|
||||
}
|
||||
],
|
||||
"subsurfaceScattering": [
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <Atom/Features/SrgSemantics.azsli>
|
||||
#include <viewsrg.srgi>
|
||||
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
|
||||
#include <Atom/Features/PBR/LightingOptions.azsli>
|
||||
#include <Atom/Features/PBR/AlphaUtils.azsli>
|
||||
|
||||
#include "MaterialInputs/BaseColorInput.azsli"
|
||||
#include "MaterialInputs/RoughnessInput.azsli"
|
||||
@@ -93,8 +95,23 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
|
||||
}
|
||||
|
||||
// Callback function for ParallaxMapping.azsli
|
||||
float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
{
|
||||
return SampleDepthOrHeightMap(MaterialSrg::m_depthInverted, MaterialSrg::m_depthMap, MaterialSrg::m_sampler, uv, uv_ddx, uv_ddy);
|
||||
}
|
||||
|
||||
|
||||
COMMON_OPTIONS_PARALLAX()
|
||||
|
||||
bool ShouldHandleParallax()
|
||||
{
|
||||
// Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scattering is enabled.
|
||||
return !o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useDepthMap;
|
||||
}
|
||||
|
||||
bool ShouldHandleParallaxInDepthShaders()
|
||||
{
|
||||
// The depth pass shaders need to calculate parallax when the result could affect the depth buffer, or when
|
||||
// parallax could affect texel clipping.
|
||||
return ShouldHandleParallax() && (o_parallax_enablePixelDepthOffset || o_opacity_mode == OpacityMode::Cutout);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Atom/Features/PBR/AlphaUtils.azsli>
|
||||
#include "./StandardPBR_Common.azsli"
|
||||
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
|
||||
#include <Atom/Features/ParallaxMapping.azsli>
|
||||
@@ -56,7 +55,7 @@ VSDepthOutput MainVS(VSInput IN)
|
||||
OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy;
|
||||
OUT.m_uv[1] = IN.m_uv1;
|
||||
|
||||
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
|
||||
if(ShouldHandleParallaxInDepthShaders())
|
||||
{
|
||||
OUT.m_worldPosition = worldPosition.xyz;
|
||||
|
||||
@@ -75,6 +74,23 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace)
|
||||
{
|
||||
PSDepthOutput OUT;
|
||||
|
||||
OUT.m_depth = IN.m_position.z;
|
||||
|
||||
if(ShouldHandleParallaxInDepthShaders())
|
||||
{
|
||||
// We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space.
|
||||
float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) };
|
||||
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) };
|
||||
PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1);
|
||||
|
||||
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
|
||||
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
|
||||
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset,
|
||||
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth);
|
||||
}
|
||||
|
||||
// Alpha
|
||||
float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex];
|
||||
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
|
||||
@@ -82,39 +98,5 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace)
|
||||
|
||||
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
|
||||
|
||||
OUT.m_depth = IN.m_position.z;
|
||||
|
||||
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
|
||||
{
|
||||
// We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space.
|
||||
float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) };
|
||||
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) };
|
||||
PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1);
|
||||
|
||||
float3 tangent = tangents[MaterialSrg::m_parallaxUvIndex];
|
||||
float3 bitangent = bitangents[MaterialSrg::m_parallaxUvIndex];
|
||||
|
||||
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
|
||||
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
|
||||
|
||||
float3 tangentOffset = GetParallaxOffset( MaterialSrg::m_depthFactor,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex],
|
||||
ViewSrg::m_worldPosition.xyz - IN.m_worldPosition,
|
||||
tangent,
|
||||
bitangent,
|
||||
IN.m_normal,
|
||||
uvMatrix);
|
||||
|
||||
PixelDepthOffset pdo = CalcPixelDepthOffset(MaterialSrg::m_depthFactor,
|
||||
tangentOffset,
|
||||
IN.m_worldPosition,
|
||||
tangent,
|
||||
bitangent,
|
||||
IN.m_normal,
|
||||
uvMatrixInverse,
|
||||
ObjectSrg::GetWorldMatrix(),
|
||||
ViewSrg::m_viewProjectionMatrix);
|
||||
OUT.m_depth = pdo.m_depth;
|
||||
}
|
||||
return OUT;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ COMMON_OPTIONS_NORMAL()
|
||||
COMMON_OPTIONS_CLEAR_COAT()
|
||||
COMMON_OPTIONS_OCCLUSION()
|
||||
COMMON_OPTIONS_EMISSIVE()
|
||||
COMMON_OPTIONS_PARALLAX()
|
||||
// Note COMMON_OPTIONS_PARALLAX is in StandardPBR_Common.azsli because it's needed by all StandardPBR shaders.
|
||||
|
||||
// Alpha
|
||||
#include "MaterialInputs/AlphaInput.azsli"
|
||||
@@ -94,7 +94,10 @@ VSOutput StandardPbr_ForwardPassVS(VSInput IN)
|
||||
OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy;
|
||||
OUT.m_uv[1] = IN.m_uv1;
|
||||
|
||||
VertexHelper(IN, OUT, worldPosition, o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset);
|
||||
// Shadow coords will be calculated in the pixel shader in this case
|
||||
bool skipShadowCoords = ShouldHandleParallax() && o_parallax_enablePixelDepthOffset;
|
||||
|
||||
VertexHelper(IN, OUT, worldPosition, skipShadowCoords);
|
||||
|
||||
return OUT;
|
||||
}
|
||||
@@ -123,15 +126,18 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
|
||||
// ------- Depth & Parallax -------
|
||||
|
||||
depth = IN.m_position.z;
|
||||
|
||||
bool displacementIsClipped = false;
|
||||
|
||||
// Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scatteirng is enabled
|
||||
if(!o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useDepthMap)
|
||||
if(ShouldHandleParallax())
|
||||
{
|
||||
|
||||
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
|
||||
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor,
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset,
|
||||
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth);
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth, displacementIsClipped);
|
||||
|
||||
// Adjust directional light shadow coorinates for parallax correction
|
||||
if(o_parallax_enablePixelDepthOffset)
|
||||
@@ -166,6 +172,11 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
|
||||
float3 sampledColor = GetBaseColorInput(MaterialSrg::m_baseColorMap, MaterialSrg::m_sampler, baseColorUv, MaterialSrg::m_baseColor.rgb, o_baseColor_useTexture);
|
||||
float3 baseColor = BlendBaseColor(sampledColor, MaterialSrg::m_baseColor.rgb, MaterialSrg::m_baseColorFactor, o_baseColorTextureBlendMode, o_baseColor_useTexture);
|
||||
|
||||
if(o_parallax_highlightClipping && displacementIsClipped)
|
||||
{
|
||||
ApplyParallaxClippingHighlight(baseColor);
|
||||
}
|
||||
|
||||
// ------- Metallic -------
|
||||
|
||||
float metallic = 0;
|
||||
|
||||
@@ -41,8 +41,10 @@ function ProcessEditor(context)
|
||||
if(not enable or textureMap == nil) then
|
||||
visibility = MaterialPropertyVisibility_Hidden
|
||||
end
|
||||
|
||||
|
||||
context:SetMaterialPropertyVisibility("parallax.factor", visibility)
|
||||
context:SetMaterialPropertyVisibility("parallax.offset", visibility)
|
||||
context:SetMaterialPropertyVisibility("parallax.showClipping", visibility)
|
||||
context:SetMaterialPropertyVisibility("parallax.invert", visibility)
|
||||
context:SetMaterialPropertyVisibility("parallax.algorithm", visibility)
|
||||
context:SetMaterialPropertyVisibility("parallax.quality", visibility)
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
#include <scenesrg.srgi>
|
||||
#include "StandardPBR_Common.azsli"
|
||||
#include <Atom/Features/PBR/AlphaUtils.azsli>
|
||||
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
|
||||
#include <Atom/Features/ParallaxMapping.azsli>
|
||||
#include <Atom/Features/MatrixUtility.azsli>
|
||||
@@ -56,7 +55,7 @@ VertexOutput MainVS(VertexInput IN)
|
||||
OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy;
|
||||
OUT.m_uv[1] = IN.m_uv1;
|
||||
|
||||
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
|
||||
if(ShouldHandleParallaxInDepthShaders())
|
||||
{
|
||||
OUT.m_worldPosition = worldPosition.xyz;
|
||||
|
||||
@@ -76,28 +75,9 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace)
|
||||
{
|
||||
PSDepthOutput OUT;
|
||||
|
||||
// Alpha
|
||||
float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex];
|
||||
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
|
||||
float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
|
||||
|
||||
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
|
||||
|
||||
OUT.m_depth = IN.m_position.z;
|
||||
|
||||
float3 dirToCamera;
|
||||
if(ViewSrg::m_projectionMatrix[0].w)
|
||||
{
|
||||
// orthographic projection (directional light)
|
||||
// No view position, use light direction
|
||||
dirToCamera = ViewSrg::m_viewMatrix[2].xyz;
|
||||
}
|
||||
else
|
||||
{
|
||||
dirToCamera = ViewSrg::m_worldPosition.xyz - IN.m_worldPosition;
|
||||
}
|
||||
|
||||
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
|
||||
if(ShouldHandleParallaxInDepthShaders())
|
||||
{
|
||||
static const float ShadowMapDepthBias = 0.000001;
|
||||
|
||||
@@ -106,31 +86,22 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace)
|
||||
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) };
|
||||
PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1);
|
||||
|
||||
float3 tangent = tangents[MaterialSrg::m_parallaxUvIndex];
|
||||
float3 bitangent = bitangents[MaterialSrg::m_parallaxUvIndex];
|
||||
|
||||
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
|
||||
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
|
||||
|
||||
float3 tangentOffset = GetParallaxOffset( MaterialSrg::m_depthFactor,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex],
|
||||
dirToCamera,
|
||||
tangent,
|
||||
bitangent,
|
||||
IN.m_normal,
|
||||
uvMatrix);
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset,
|
||||
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth);
|
||||
|
||||
PixelDepthOffset pdo = CalcPixelDepthOffset(MaterialSrg::m_depthFactor,
|
||||
tangentOffset,
|
||||
IN.m_worldPosition,
|
||||
tangent,
|
||||
bitangent,
|
||||
IN.m_normal,
|
||||
uvMatrixInverse,
|
||||
ObjectSrg::GetWorldMatrix(),
|
||||
ViewSrg::m_viewProjectionMatrix);
|
||||
|
||||
OUT.m_depth = pdo.m_depth + ShadowMapDepthBias;
|
||||
OUT.m_depth += ShadowMapDepthBias;
|
||||
}
|
||||
|
||||
// Alpha
|
||||
float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex];
|
||||
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
|
||||
float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
|
||||
|
||||
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
|
||||
|
||||
return OUT;
|
||||
}
|
||||
|
||||
@@ -18,39 +18,147 @@ option bool o_parallax_enablePixelDepthOffset;
|
||||
|
||||
option enum class ParallaxAlgorithm {Basic, Steep, POM, Relief, Contact} o_parallax_algorithm;
|
||||
option enum class ParallaxQuality {Low, Medium, High, Ultra} o_parallax_quality;
|
||||
|
||||
option bool o_parallax_feature_enabled;
|
||||
option bool o_parallax_highlightClipping;
|
||||
option bool o_parallax_shadow;
|
||||
|
||||
// I tried to make this an enum class, but ran into some DXC bug when compiling to SPIRV.
|
||||
enum DepthResultCode
|
||||
{
|
||||
DepthResultCode_Invalid,
|
||||
DepthResultCode_Normalized, //!< The result is in range [0,1], where 0 is the top of the heightmap and 1 is the bottom of the heightmap.
|
||||
DepthResultCode_Absolute //!< The result is tangent space units (the same as world units if there's no mesh scaling), where 0 is at the mesh surface and positive values are below the surface.
|
||||
};
|
||||
|
||||
//! The return value for the GetDepth() callback function below.
|
||||
struct DepthResult
|
||||
{
|
||||
DepthResultCode m_resultCode;
|
||||
float m_depth;
|
||||
};
|
||||
|
||||
//! Convenience function for making a DepthResult with Code::Normalized
|
||||
DepthResult DepthResultNormalized(float depth)
|
||||
{
|
||||
DepthResult result;
|
||||
result.m_resultCode = DepthResultCode_Normalized;
|
||||
result.m_depth = depth;
|
||||
return result;
|
||||
}
|
||||
|
||||
//! Convenience function for making a DepthResult with Code::Absolute
|
||||
DepthResult DepthResultAbsolute(float depth)
|
||||
{
|
||||
DepthResult result;
|
||||
result.m_resultCode = DepthResultCode_Absolute;
|
||||
result.m_depth = depth;
|
||||
return result;
|
||||
}
|
||||
|
||||
//! The client shader must define this function.
|
||||
//! This allows the client shader to implement special depth map sampling, for example procedurally generating or blending depth maps.
|
||||
//! In simple cases though, the implementation of GetDepth() can simply call SampleDepthOrHeightMap().
|
||||
//! @param uv the UV coordinates to use for sampling
|
||||
//! @param uv_ddx will be set to ddx_fine(uv)
|
||||
//! @param uv_ddy will be set to ddy_fine(uv)
|
||||
float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy);
|
||||
//! @return see struct DepthResult
|
||||
DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy);
|
||||
|
||||
//! Convenience function that can be used to implement GetDepth().
|
||||
//! @param isHeightmap indicates whether to sample the map is a height map rather than a depth map.
|
||||
float SampleDepthOrHeightMap(bool isHeightmap, Texture2D map, sampler mapSampler, float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
//! @return see struct DepthResult. In this case it will always contain a Code::Normalized result.
|
||||
DepthResult SampleDepthOrHeightMap(bool isHeightmap, Texture2D map, sampler mapSampler, float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
{
|
||||
return abs((isHeightmap * 1.0) - map.SampleGrad(mapSampler, uv, uv_ddx, uv_ddy).r);
|
||||
DepthResult result;
|
||||
result.m_resultCode = DepthResultCode_Normalized;
|
||||
result.m_depth = abs((isHeightmap * 1.0) - map.SampleGrad(mapSampler, uv, uv_ddx, uv_ddy).r);
|
||||
return result;
|
||||
}
|
||||
|
||||
//! Calls GetDepth() and then normalizes the result if it isn't normalized already.
|
||||
//! @param startDepth is the high point, which corresponds to a normalized depth value of 0.
|
||||
//! @param stopDepth is the low point, which corresponds to a normalized depth value of 1.
|
||||
//! @param inverseDepthRange is an optimization, and must be set to "1.0 / (stopDepth - startDepth)".
|
||||
//! @param uv the UV coordinates to use for sampling
|
||||
//! @param uv_ddx must be set to ddx_fine(uv)
|
||||
//! @param uv_ddy must be set to ddy_fine(uv)
|
||||
//! @param a depth value in the range [0,1]
|
||||
float GetNormalizedDepth(float startDepth, float stopDepth, float inverseDepthRange, float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
{
|
||||
// startDepth can be less than 0, representing a displacement above the mesh surface.
|
||||
// But since we don't currently support any vertex displacement, negative depth values would cause various
|
||||
// problems especially when PDO is enabled, like parallax surfaces clipping through foreground geometry, and parallax
|
||||
// surfaces disappearing at low angles. So we clamp all depth values to a minimum of 0.
|
||||
|
||||
float normalizedDepth = 0.0;
|
||||
|
||||
DepthResult depthResult = GetDepth(uv, uv_ddx, uv_ddy);
|
||||
|
||||
if(stopDepth - startDepth > 0.0001)
|
||||
{
|
||||
if(DepthResultCode_Normalized == depthResult.m_resultCode)
|
||||
{
|
||||
float minNormalizedDepth = -startDepth * inverseDepthRange;
|
||||
normalizedDepth = max(depthResult.m_depth, minNormalizedDepth);
|
||||
}
|
||||
else if(DepthResultCode_Absolute == depthResult.m_resultCode)
|
||||
{
|
||||
float clampedAbsoluteDepth = max(depthResult.m_depth, 0.0);
|
||||
normalizedDepth = (clampedAbsoluteDepth - startDepth) * inverseDepthRange;
|
||||
}
|
||||
}
|
||||
|
||||
return normalizedDepth;
|
||||
}
|
||||
|
||||
float GetNormalizedDepth(float startDepth, float stopDepth, float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
{
|
||||
float inverseDepthRange = 1.0 / (stopDepth - startDepth);
|
||||
return GetNormalizedDepth(startDepth, stopDepth, inverseDepthRange, uv, uv_ddx, uv_ddy);
|
||||
}
|
||||
|
||||
void ApplyParallaxClippingHighlight(inout float3 baseColor)
|
||||
{
|
||||
baseColor = lerp(baseColor, float3(1.0, 0.0, 1.0), 0.5);
|
||||
}
|
||||
|
||||
struct ParallaxOffset
|
||||
{
|
||||
float3 m_offsetTS; //!< represents the intersection point relative to the geometry surface, in tangent space.
|
||||
bool m_isClipped; //!< Indicates whether the result is being clipped by the geometry surface, mainly for debug rendering. Only set when o_parallax_highlightClipping is true.
|
||||
};
|
||||
|
||||
// dirToCameraTS should be in tangent space and normalized
|
||||
// From Reat-Time Rendering 3rd edition, p.192
|
||||
float3 BasicParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraTS)
|
||||
ParallaxOffset BasicParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraTS)
|
||||
{
|
||||
// the amount to shift
|
||||
float2 delta = dirToCameraTS.xy * GetDepth(uv, ddx_fine(uv), ddy_fine(uv)) * depthFactor;
|
||||
float2 delta = dirToCameraTS.xy * GetNormalizedDepth(0, depthFactor, uv, ddx_fine(uv), ddy_fine(uv)) * depthFactor;
|
||||
|
||||
float3 offset = float3(0,0,0);
|
||||
offset.xy -= delta;
|
||||
return offset;
|
||||
ParallaxOffset result;
|
||||
|
||||
result.m_offsetTS = float3(0,0,0);
|
||||
result.m_offsetTS.xy -= delta;
|
||||
result.m_isClipped = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
// dirToCameraTS and dirToLightTS should be in tangent space and normalized
|
||||
// Adapt from CryEngine shader shadelib.cfi and POM function in https://github.com/a-riccardi/shader-toy
|
||||
// Performs ray intersection against a surface with a heightmap.
|
||||
// Adapted from CryEngine shader shadelib.cfi and POM function in https://github.com/a-riccardi/shader-toy
|
||||
// check https://github.com/UPBGE/blender/issues/1009 for more details.
|
||||
float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraTS, float3 dirToLightTS, int numSteps, inout float parallaxShadowAttenuation)
|
||||
// @param depthFactor - scales the heightmap in tangent space units (which normally ends up being world units).
|
||||
// @param depthOffset - offsets the heighmap up or down in tangent space units (which normally ends up being world units).
|
||||
// @param uv - the UV coordinates on the surface, where the search will begin, used to sample the heightmap.
|
||||
// @param dirToCameraTS - normalized direction to the camera, in tangent space.
|
||||
// @param dirToLightTS - normalized direction to a light source, in tangent space, for self-shadowing (if enabled via o_parallax_shadow).
|
||||
// @param numSteps - the number of steps to take when marching along the ray searching for intersection.
|
||||
// @param parallaxShadowAttenuation - returns a factor for attenuating a light source, for self-shadowing (if enabled via o_parallax_shadow).
|
||||
ParallaxOffset AdvancedParallaxMapping(float depthFactor, float depthOffset, float2 uv, float3 dirToCameraTS, float3 dirToLightTS, int numSteps, inout float parallaxShadowAttenuation)
|
||||
{
|
||||
ParallaxOffset result;
|
||||
result.m_isClipped = false;
|
||||
|
||||
float dirToCameraZInverse = 1.0 / dirToCameraTS.z;
|
||||
float step = 1.0 / numSteps;
|
||||
float currentStep = 0.0;
|
||||
@@ -61,21 +169,38 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT
|
||||
float2 ddx_uv = ddx_fine(uv);
|
||||
float2 ddy_uv = ddy_fine(uv);
|
||||
|
||||
float currentSample = GetDepth(uv, ddx_uv, ddy_uv);
|
||||
float prevSample;
|
||||
float3 parallaxOffset = float3(0,0,0);
|
||||
float depthSearchStart = -depthOffset;
|
||||
float depthSearchEnd = depthSearchStart + depthFactor;
|
||||
|
||||
float inverseDepthFactor = 1.0 / depthFactor;
|
||||
|
||||
// find the intersect step
|
||||
// This is the relative position at which we begin searching for intersection.
|
||||
// It is adjusted according to the depthOffset, raising or lowering the whole surface by depthOffset units.
|
||||
float3 parallaxOffset = dirToCameraTS.xyz * dirToCameraZInverse * depthOffset;
|
||||
|
||||
// Get an initial heightmap sample to start the intersection search, starting at our initial parallaxOffset position.
|
||||
float currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, uv + parallaxOffset.xy, ddx_uv, ddy_uv);
|
||||
float prevSample;
|
||||
|
||||
// Note that when depthOffset < 0, we could actually narrow the search so that instead of going through the entire [depthSearchStart,depthSearchEnd] range
|
||||
// of the heightmap, we could go through the range [0,depthSearchEnd]. This would give more accurate results and fewer artifacts
|
||||
// in case where the magnitude of depthOffset is significant. But for the sake of simplicity we currently search the whole range in all cases.
|
||||
|
||||
// Do a basic search for the intersect step
|
||||
while(currentSample > currentStep)
|
||||
{
|
||||
currentStep += step;
|
||||
parallaxOffset += delta;
|
||||
|
||||
prevSample = currentSample;
|
||||
currentSample = GetDepth(uv + parallaxOffset.xy, ddx_uv, ddy_uv);
|
||||
currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, uv + parallaxOffset.xy, ddx_uv, ddy_uv);
|
||||
}
|
||||
|
||||
// Depending on the algorithm, we refine the result of the above search
|
||||
switch(o_parallax_algorithm)
|
||||
{
|
||||
case ParallaxAlgorithm::Steep:
|
||||
break; // This algorithm just relies on the course intersection test loop above
|
||||
case ParallaxAlgorithm::POM:
|
||||
{
|
||||
if(currentStep > 0.0)
|
||||
@@ -108,7 +233,7 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT
|
||||
parallaxOffset += reliefDelta * depthSign;
|
||||
currentStep += reliefStep * depthSign;
|
||||
|
||||
currentSample = GetDepth(uv + parallaxOffset.xy, ddx_uv, ddy_uv);
|
||||
currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, uv + parallaxOffset.xy, ddx_uv, ddy_uv);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -136,7 +261,7 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT
|
||||
parallaxOffset += adjustedDelta;
|
||||
prevSample = currentSample;
|
||||
|
||||
currentSample = GetDepth(uv + parallaxOffset.xy, ddx_uv, ddy_uv);
|
||||
currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, uv + parallaxOffset.xy, ddx_uv, ddy_uv);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -144,6 +269,30 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Even though we do a bunch of clamping above when calling GetClampedDepth(), there are still cases where the parallax offset
|
||||
// can be noticeably above the surface and still needs to be clamped here. The main case is when depthFactor==0 and depthOffset>1.
|
||||
if(parallaxOffset.z > 0.0)
|
||||
{
|
||||
parallaxOffset = float3(0,0,0);
|
||||
}
|
||||
|
||||
if (o_parallax_highlightClipping)
|
||||
{
|
||||
// The most accurate way to report clipping is to sample the heightmap one last time at the final adjusted UV.
|
||||
// (trying to do it based on parallaxOffset.z values just leads to too many edge cases)
|
||||
|
||||
DepthResult depthResult = GetDepth(uv + parallaxOffset.xy, ddx_uv, ddy_uv);
|
||||
|
||||
if(DepthResultCode_Normalized == depthResult.m_resultCode)
|
||||
{
|
||||
result.m_isClipped = lerp(depthSearchStart, depthSearchEnd, depthResult.m_depth) < 0;
|
||||
}
|
||||
else if(DepthResultCode_Absolute == depthResult.m_resultCode)
|
||||
{
|
||||
result.m_isClipped = depthResult.m_depth < 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
if(o_parallax_shadow && any(dirToLightTS))
|
||||
{
|
||||
@@ -168,7 +317,7 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT
|
||||
}
|
||||
|
||||
shadowUV += shadowDelta;
|
||||
currentSample = GetDepth(shadowUV, ddx_uv, ddy_uv);
|
||||
currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, shadowUV, ddx_uv, ddy_uv);
|
||||
currentStep -= step;
|
||||
}
|
||||
|
||||
@@ -181,12 +330,13 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT
|
||||
parallaxShadowAttenuation = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return parallaxOffset;
|
||||
|
||||
result.m_offsetTS = parallaxOffset;
|
||||
return result;
|
||||
}
|
||||
|
||||
// return offset in tangent space
|
||||
float3 CalculateParallaxOffset(float depthFactor, float2 uv, float3 dirToCameraTS, float3 dirToLightTS, inout float parallaxShadowAttenuation)
|
||||
ParallaxOffset CalculateParallaxOffset(float depthFactor, float depthOffset, float2 uv, float3 dirToCameraTS, float3 dirToLightTS, inout float parallaxShadowAttenuation)
|
||||
{
|
||||
if(o_parallax_algorithm == ParallaxAlgorithm::Basic)
|
||||
{
|
||||
@@ -194,27 +344,34 @@ float3 CalculateParallaxOffset(float depthFactor, float2 uv, float3 dirToCameraT
|
||||
}
|
||||
else
|
||||
{
|
||||
float3 parallaxOffset;
|
||||
ParallaxOffset parallaxOffset;
|
||||
switch(o_parallax_quality)
|
||||
{
|
||||
case ParallaxQuality::Low:
|
||||
parallaxOffset = AdvancedParallaxMapping(depthFactor, uv, dirToCameraTS, dirToLightTS, 16, parallaxShadowAttenuation);
|
||||
parallaxOffset = AdvancedParallaxMapping(depthFactor, depthOffset, uv, dirToCameraTS, dirToLightTS, 16, parallaxShadowAttenuation);
|
||||
break;
|
||||
case ParallaxQuality::Medium:
|
||||
parallaxOffset = AdvancedParallaxMapping(depthFactor, uv, dirToCameraTS, dirToLightTS, 32, parallaxShadowAttenuation);
|
||||
parallaxOffset = AdvancedParallaxMapping(depthFactor, depthOffset, uv, dirToCameraTS, dirToLightTS, 32, parallaxShadowAttenuation);
|
||||
break;
|
||||
case ParallaxQuality::High:
|
||||
parallaxOffset = AdvancedParallaxMapping(depthFactor, uv, dirToCameraTS, dirToLightTS, 64, parallaxShadowAttenuation);
|
||||
parallaxOffset = AdvancedParallaxMapping(depthFactor, depthOffset, uv, dirToCameraTS, dirToLightTS, 64, parallaxShadowAttenuation);
|
||||
break;
|
||||
case ParallaxQuality::Ultra:
|
||||
parallaxOffset = AdvancedParallaxMapping(depthFactor, uv, dirToCameraTS, dirToLightTS, 128, parallaxShadowAttenuation);
|
||||
parallaxOffset = AdvancedParallaxMapping(depthFactor, depthOffset, uv, dirToCameraTS, dirToLightTS, 128, parallaxShadowAttenuation);
|
||||
break;
|
||||
}
|
||||
return parallaxOffset;
|
||||
}
|
||||
}
|
||||
|
||||
float3 GetParallaxOffset( float depthFactor,
|
||||
// Performs ray intersection against a surface with a heightmap, to determine an offset amount required for a parallax effect.
|
||||
// @param depthFactor - scales the heightmap in tangent space units (which normally ends up being world units).
|
||||
// @param depthOffset - offsets the heighmap up or down in tangent space units (which normally ends up being world units).
|
||||
// @param uv - the UV coordinates on the surface, where the search will begin, used to sample the heightmap.
|
||||
// @param dirToCameraTS - normalized direction to the camera, in tangent space.
|
||||
// @param dirToLightTS - normalized direction to a light source, in tangent space, for self-shadowing (if enabled via o_parallax_shadow).
|
||||
ParallaxOffset GetParallaxOffset( float depthFactor,
|
||||
float depthOffset,
|
||||
float2 uv,
|
||||
float3 dirToCameraWS,
|
||||
float3 tangentWS,
|
||||
@@ -236,7 +393,7 @@ float3 GetParallaxOffset( float depthFactor,
|
||||
float4 dirToCameraTransformed = mul(uv3DTransform, float4(dirToCameraTS, 0.0));
|
||||
|
||||
float dummy = 1;
|
||||
return CalculateParallaxOffset(depthFactor, uv, normalize(dirToCameraTransformed.xyz), float3(0,0,0), dummy);
|
||||
return CalculateParallaxOffset(depthFactor, depthOffset, uv, normalize(dirToCameraTransformed.xyz), float3(0,0,0), dummy);
|
||||
}
|
||||
|
||||
struct PixelDepthOffset
|
||||
|
||||
+3
@@ -88,7 +88,10 @@ namespace AZ
|
||||
|
||||
//! Sets the transform of the decal
|
||||
//! Equivalent to calling SetDecalPosition() + SetDecalOrientation() + SetDecalHalfSize()
|
||||
//! @{
|
||||
virtual void SetDecalTransform(DecalHandle handle, const AZ::Transform& world) = 0;
|
||||
virtual void SetDecalTransform(DecalHandle handle, const AZ::Transform& world, const AZ::Vector3& nonUniformScale) = 0;
|
||||
//! @}
|
||||
|
||||
//! Sets the material information for this decal
|
||||
virtual void SetDecalMaterial(DecalHandle handle, const AZ::Data::AssetId) = 0;
|
||||
|
||||
@@ -264,7 +264,12 @@ namespace AZ
|
||||
|
||||
void DecalFeatureProcessor::SetDecalTransform(DecalHandle handle, const AZ::Transform& world)
|
||||
{
|
||||
// https://jira.agscollab.com/browse/ATOM-4330
|
||||
SetDecalTransform(handle, world, AZ::Vector3::CreateOne());
|
||||
}
|
||||
|
||||
void DecalFeatureProcessor::SetDecalTransform(DecalHandle handle, const AZ::Transform& world, const AZ::Vector3& nonUniformScale)
|
||||
{
|
||||
// ATOM-4330
|
||||
// Original Open 3D Engine uploads a 4x4 matrix rather than quaternion, rotation, scale.
|
||||
// That is more memory but less calculation because it is doing a matrix inverse rather than a polar decomposition
|
||||
// I've done some experiments and uploading a 3x4 transform matrix with 3x3 matrix inverse should be possible
|
||||
@@ -274,7 +279,7 @@ namespace AZ
|
||||
if (handle.IsValid())
|
||||
{
|
||||
Quaternion orientation = world.GetRotation();
|
||||
Vector3 scale = world.GetScale();
|
||||
Vector3 scale = world.GetScale() * nonUniformScale;
|
||||
|
||||
SetDecalHalfSize(handle, scale);
|
||||
SetDecalPosition(handle, world.GetTranslation());
|
||||
|
||||
@@ -73,7 +73,10 @@ namespace AZ
|
||||
|
||||
//! Sets the transform of the decal
|
||||
//! Equivalent to calling SetDecalPosition() + SetDecalOrientation() + SetDecalHalfSize()
|
||||
//! @{
|
||||
void SetDecalTransform(DecalHandle handle, const AZ::Transform& world) override;
|
||||
void SetDecalTransform(DecalHandle handle, const AZ::Transform& world, const AZ::Vector3& nonUniformScale) override;
|
||||
//! @}
|
||||
|
||||
//! Sets the material information for this decal
|
||||
void SetDecalMaterial(DecalHandle handle, const AZ::Data::AssetId) override;
|
||||
|
||||
@@ -270,10 +270,16 @@ namespace AZ
|
||||
}
|
||||
|
||||
void DecalTextureArrayFeatureProcessor::SetDecalTransform(DecalHandle handle, const AZ::Transform& world)
|
||||
{
|
||||
SetDecalTransform(handle, world, AZ::Vector3::CreateOne());
|
||||
}
|
||||
|
||||
void DecalTextureArrayFeatureProcessor::SetDecalTransform(DecalHandle handle, const AZ::Transform& world,
|
||||
const AZ::Vector3& nonUniformScale)
|
||||
{
|
||||
if (handle.IsValid())
|
||||
{
|
||||
SetDecalHalfSize(handle, world.GetScale());
|
||||
SetDecalHalfSize(handle, nonUniformScale * world.GetScale());
|
||||
SetDecalPosition(handle, world.GetTranslation());
|
||||
SetDecalOrientation(handle, world.GetRotation());
|
||||
|
||||
|
||||
@@ -82,7 +82,10 @@ namespace AZ
|
||||
|
||||
//! Sets the transform of the decal
|
||||
//! Equivalent to calling SetDecalPosition() + SetDecalOrientation() + SetDecalHalfSize()
|
||||
//! @{
|
||||
void SetDecalTransform(const DecalHandle handle, const AZ::Transform& world) override;
|
||||
void SetDecalTransform(const DecalHandle handle, const AZ::Transform& world, const AZ::Vector3& nonUniformScale) override;
|
||||
//! @}
|
||||
|
||||
//! Sets the material information for this decal
|
||||
void SetDecalMaterial(const DecalHandle handle, const AZ::Data::AssetId id) override;
|
||||
|
||||
+2
@@ -33,6 +33,8 @@ namespace AZ
|
||||
|
||||
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue,
|
||||
const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context) override;
|
||||
private:
|
||||
BaseJsonSerializer::OperationFlags GetOperationsFlags() const override;
|
||||
};
|
||||
|
||||
} // namespace RPI
|
||||
|
||||
@@ -127,5 +127,10 @@ namespace AZ
|
||||
|
||||
return context.Report(result, "Successfully processed MaterialFunctorSourceData.");
|
||||
}
|
||||
|
||||
BaseJsonSerializer::OperationFlags JsonMaterialFunctorSourceDataSerializer::GetOperationsFlags() const
|
||||
{
|
||||
return OperationFlags::ManualDefault;
|
||||
}
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"description": "",
|
||||
"materialType": "Materials/Types/StandardPBR.materialtype",
|
||||
"parentMaterial": "",
|
||||
"propertyLayoutVersion": 3,
|
||||
"properties": {
|
||||
"baseColor": {
|
||||
"textureMap": "TestData/Textures/TextureHaven/4k_castle_brick_02_red/4k_castle_brick_02_red_bc.png"
|
||||
},
|
||||
"opacity": {
|
||||
"alphaSource": "Split",
|
||||
"mode": "Cutout",
|
||||
"textureMap": "TestData/Textures/checker8x8_512.png"
|
||||
},
|
||||
"parallax": {
|
||||
"algorithm": "POM",
|
||||
"enable": true,
|
||||
"factor": 0.10000000149011612,
|
||||
"quality": "High",
|
||||
"textureMap": "TestData/Textures/TextureHaven/4k_castle_brick_02_red/4k_castle_brick_02_red_disp.png"
|
||||
},
|
||||
"uv": {
|
||||
"scale": 0.5
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,12 +121,12 @@ void GetSurfaceShape(float2 uv, out float depth, out float3 normal)
|
||||
}
|
||||
|
||||
// Callback function for ParallaxMapping.azsli
|
||||
float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
{
|
||||
float depth;
|
||||
float3 normal;
|
||||
GetSurfaceShape(uv, depth, normal);
|
||||
return depth;
|
||||
return DepthResultNormalized(depth);
|
||||
}
|
||||
|
||||
ForwardPassOutput AutoBrick_ForwardPassPS(VSOutput IN)
|
||||
@@ -136,15 +136,18 @@ ForwardPassOutput AutoBrick_ForwardPassPS(VSOutput IN)
|
||||
0,1,0,
|
||||
0,0,1 };
|
||||
|
||||
float3 tangentOffset = GetParallaxOffset( AutoBrickSrg::m_lineDepth,
|
||||
IN.m_uv,
|
||||
ViewSrg::m_worldPosition.xyz - IN.m_worldPosition,
|
||||
IN.m_tangent,
|
||||
IN.m_bitangent,
|
||||
IN.m_normal,
|
||||
identityUvMatrix);
|
||||
float depthOffset = 0.0;
|
||||
|
||||
ParallaxOffset tangentOffset = GetParallaxOffset( AutoBrickSrg::m_lineDepth,
|
||||
depthOffset,
|
||||
IN.m_uv,
|
||||
ViewSrg::m_worldPosition.xyz - IN.m_worldPosition,
|
||||
IN.m_tangent,
|
||||
IN.m_bitangent,
|
||||
IN.m_normal,
|
||||
identityUvMatrix);
|
||||
|
||||
IN.m_uv += tangentOffset.xy;
|
||||
IN.m_uv += tangentOffset.m_offsetTS.xy;
|
||||
|
||||
float3 baseColor = float3(1,1,1);
|
||||
const float noise = AutoBrickSrg::m_noise.Sample(AutoBrickSrg::m_sampler, IN.m_uv).r;
|
||||
|
||||
+5
@@ -15,6 +15,7 @@
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzQtComponents/Components/ExtendedLabel.h>
|
||||
#include <QMouseEvent>
|
||||
#include <QPaintEvent>
|
||||
#endif
|
||||
|
||||
@@ -31,7 +32,11 @@ namespace AtomToolsFramework
|
||||
void SetExpanded(bool expanded);
|
||||
bool IsExpanded() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void clicked(QMouseEvent* event);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
private:
|
||||
|
||||
+6
@@ -55,6 +55,12 @@ namespace AtomToolsFramework
|
||||
|
||||
//! Calls Rebuild for all InspectorGroupWidget, allowing for destructive UI changes
|
||||
virtual void RebuildAll() = 0;
|
||||
|
||||
//! Expands all groups and headers
|
||||
virtual void ExpandAll() = 0;
|
||||
|
||||
//! Collapses all groups and headers
|
||||
virtual void CollapseAll() = 0;
|
||||
};
|
||||
|
||||
using InspectorRequestBus = AZ::EBus<InspectorRequests>;
|
||||
|
||||
+8
-4
@@ -29,11 +29,8 @@ namespace Ui
|
||||
|
||||
namespace AtomToolsFramework
|
||||
{
|
||||
class InspectorPropertyGroupWidget;
|
||||
}
|
||||
class InspectorGroupHeaderWidget;
|
||||
|
||||
namespace AtomToolsFramework
|
||||
{
|
||||
//! Provides controls for viewing and editing object settings.
|
||||
//! The settings can be divided into groups, with each one showing a subset of properties.
|
||||
class InspectorWidget
|
||||
@@ -66,8 +63,15 @@ namespace AtomToolsFramework
|
||||
void RefreshAll() override;
|
||||
void RebuildAll() override;
|
||||
|
||||
void ExpandAll() override;
|
||||
void CollapseAll() override;
|
||||
|
||||
private:
|
||||
void OnHeaderClicked(QMouseEvent* event, InspectorGroupHeaderWidget* groupHeader, QWidget* groupWidget);
|
||||
|
||||
QVBoxLayout* m_layout = nullptr;
|
||||
QScopedPointer<Ui::InspectorWidget> m_ui;
|
||||
AZStd::vector<InspectorGroupHeaderWidget*> m_headers;
|
||||
AZStd::vector<QWidget*> m_groups;
|
||||
};
|
||||
} // namespace AtomToolsFramework
|
||||
|
||||
+1
@@ -104,6 +104,7 @@ namespace AtomToolsFramework
|
||||
void EndCursorCapture() override;
|
||||
AzFramework::ScreenPoint ViewportCursorScreenPosition() override;
|
||||
AZStd::optional<AzFramework::ScreenPoint> PreviousViewportCursorScreenPosition() override;
|
||||
bool IsMouseOver() const override;
|
||||
|
||||
// AzFramework::WindowRequestBus::Handler ...
|
||||
void SetWindowTitle(const AZStd::string& title) override;
|
||||
|
||||
+9
-13
@@ -14,10 +14,10 @@
|
||||
#include <AzQtComponents/Components/StyleManager.h>
|
||||
#include <AzQtComponents/Components/Widgets/Text.h>
|
||||
|
||||
#include <QStyle>
|
||||
#include <QPainter>
|
||||
#include <QApplication>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QStyle>
|
||||
#include <QStyleOptionViewItem>
|
||||
|
||||
namespace AtomToolsFramework
|
||||
@@ -44,6 +44,11 @@ namespace AtomToolsFramework
|
||||
return m_expanded;
|
||||
}
|
||||
|
||||
void InspectorGroupHeaderWidget::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
emit clicked(event);
|
||||
}
|
||||
|
||||
void InspectorGroupHeaderWidget::paintEvent([[maybe_unused]] QPaintEvent* event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
@@ -52,19 +57,10 @@ namespace AtomToolsFramework
|
||||
auto& icon = m_expanded ? m_iconExpanded : m_iconCollapsed;
|
||||
|
||||
const QRect iconRect(5, (geometry().height() / 2) - (iconSize.height() / 2), iconSize.width(), iconSize.height());
|
||||
style->drawItemPixmap(&painter,
|
||||
iconRect,
|
||||
Qt::AlignLeft | Qt::AlignVCenter,
|
||||
icon.scaledToWidth(iconSize.width()));
|
||||
style->drawItemPixmap(&painter, iconRect, Qt::AlignLeft | Qt::AlignVCenter, icon.scaledToWidth(iconSize.width()));
|
||||
|
||||
const auto textRect = QRect(25, 0, geometry().width() - 21, geometry().height());
|
||||
style->drawItemText(&painter,
|
||||
textRect,
|
||||
Qt::AlignLeft | Qt::AlignVCenter,
|
||||
QPalette(),
|
||||
true,
|
||||
text(),
|
||||
QPalette::HighlightedText);
|
||||
style->drawItemText(&painter, textRect, Qt::AlignLeft | Qt::AlignVCenter, QPalette(), true, text(), QPalette::HighlightedText);
|
||||
}
|
||||
} // namespace AtomToolsFramework
|
||||
|
||||
|
||||
@@ -10,12 +10,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <QMenu>
|
||||
#include <QScrollArea>
|
||||
#include <QScrollBar>
|
||||
#include <QSizePolicy>
|
||||
|
||||
#include <AtomToolsFramework/Inspector/InspectorGroupWidget.h>
|
||||
#include <AtomToolsFramework/Inspector/InspectorGroupHeaderWidget.h>
|
||||
#include <AtomToolsFramework/Inspector/InspectorGroupWidget.h>
|
||||
#include <AtomToolsFramework/Inspector/InspectorWidget.h>
|
||||
#include <Source/Inspector/ui_InspectorWidget.h>
|
||||
|
||||
@@ -38,6 +39,8 @@ namespace AtomToolsFramework
|
||||
m_layout = new QVBoxLayout(m_ui->m_propertyContent);
|
||||
m_layout->setContentsMargins(0, 0, 0, 0);
|
||||
m_layout->setSpacing(0);
|
||||
m_headers.clear();
|
||||
m_groups.clear();
|
||||
}
|
||||
|
||||
void InspectorWidget::AddGroupsBegin()
|
||||
@@ -52,8 +55,7 @@ namespace AtomToolsFramework
|
||||
m_layout->addStretch();
|
||||
|
||||
// Scroll to top whenever there is new content
|
||||
m_ui->m_propertyScrollArea->verticalScrollBar()->setValue(
|
||||
m_ui->m_propertyScrollArea->verticalScrollBar()->minimum());
|
||||
m_ui->m_propertyScrollArea->verticalScrollBar()->setValue(m_ui->m_propertyScrollArea->verticalScrollBar()->minimum());
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
}
|
||||
@@ -68,15 +70,15 @@ namespace AtomToolsFramework
|
||||
groupHeader->setText(groupDisplayName.c_str());
|
||||
groupHeader->setToolTip(groupDescription.c_str());
|
||||
m_layout->addWidget(groupHeader);
|
||||
m_headers.push_back(groupHeader);
|
||||
|
||||
groupWidget->setObjectName(groupNameId.c_str());
|
||||
groupWidget->setParent(m_ui->m_propertyContent);
|
||||
m_layout->addWidget(groupWidget);
|
||||
m_groups.push_back(groupWidget);
|
||||
|
||||
connect(groupHeader, &AzQtComponents::ExtendedLabel::clicked, this, [groupHeader, groupWidget]()
|
||||
{
|
||||
groupHeader->SetExpanded(!groupHeader->IsExpanded());
|
||||
groupWidget->setVisible(groupHeader->IsExpanded());
|
||||
connect(groupHeader, &InspectorGroupHeaderWidget::clicked, this, [this, groupHeader, groupWidget](QMouseEvent* event) {
|
||||
OnHeaderClicked(event, groupHeader, groupWidget);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -111,6 +113,57 @@ namespace AtomToolsFramework
|
||||
groupWidget->Rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorWidget::ExpandAll()
|
||||
{
|
||||
for (auto headerWidget : m_headers)
|
||||
{
|
||||
headerWidget->SetExpanded(true);
|
||||
}
|
||||
for (auto groupWidget : m_groups)
|
||||
{
|
||||
groupWidget->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorWidget::CollapseAll()
|
||||
{
|
||||
for (auto headerWidget : m_headers)
|
||||
{
|
||||
headerWidget->SetExpanded(false);
|
||||
}
|
||||
for (auto groupWidget : m_groups)
|
||||
{
|
||||
groupWidget->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorWidget::OnHeaderClicked(QMouseEvent* event, InspectorGroupHeaderWidget* groupHeader, QWidget* groupWidget)
|
||||
{
|
||||
if (event->button() == Qt::MouseButton::LeftButton)
|
||||
{
|
||||
groupHeader->SetExpanded(!groupHeader->IsExpanded());
|
||||
groupWidget->setVisible(groupHeader->IsExpanded());
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->button() == Qt::MouseButton::RightButton)
|
||||
{
|
||||
QMenu menu;
|
||||
menu.addAction("Expand", [groupHeader, groupWidget]() {
|
||||
groupHeader->SetExpanded(true);
|
||||
groupWidget->setVisible(true);
|
||||
})->setEnabled(!groupHeader->IsExpanded());
|
||||
menu.addAction("Collapse", [groupHeader, groupWidget]() {
|
||||
groupHeader->SetExpanded(false);
|
||||
groupWidget->setVisible(false);
|
||||
})->setEnabled(groupHeader->IsExpanded());
|
||||
menu.addAction("Expand All", [this]() { ExpandAll(); });
|
||||
menu.addAction("Collapse All", [this]() { CollapseAll(); });
|
||||
menu.exec(event->globalPos());
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // namespace AtomToolsFramework
|
||||
|
||||
#include <AtomToolsFramework/Inspector/moc_InspectorWidget.cpp>
|
||||
|
||||
@@ -472,6 +472,11 @@ namespace AtomToolsFramework
|
||||
: AZStd::optional<AzFramework::ScreenPoint>{};
|
||||
}
|
||||
|
||||
bool RenderViewportWidget::IsMouseOver() const
|
||||
{
|
||||
return m_mouseOver;
|
||||
}
|
||||
|
||||
void RenderViewportWidget::BeginCursorCapture()
|
||||
{
|
||||
if (m_capturingCursor)
|
||||
|
||||
+15
-2
@@ -17,6 +17,8 @@
|
||||
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
|
||||
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
|
||||
#include <AzFramework/Components/CameraBus.h>
|
||||
#include <AzFramework/Viewport/ScreenGeometry.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
|
||||
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
|
||||
#include <Atom/RPI.Public/RPISystemInterface.h>
|
||||
@@ -136,6 +138,11 @@ namespace MaterialEditor
|
||||
const InputChannel::State state = event.m_inputChannel.GetState();
|
||||
const KeyMask keysOld = m_keys;
|
||||
|
||||
bool mouseOver = false;
|
||||
AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::EventResult(
|
||||
mouseOver, GetViewportId(),
|
||||
&AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Events::IsMouseOver);
|
||||
|
||||
if (!m_behavior)
|
||||
{
|
||||
EvaluateControlBehavior();
|
||||
@@ -178,7 +185,10 @@ namespace MaterialEditor
|
||||
}
|
||||
else if (inputChannelId == InputDeviceMouse::Movement::Z)
|
||||
{
|
||||
m_behavior->MoveZ(event.m_inputChannel.GetValue());
|
||||
if (mouseOver)
|
||||
{
|
||||
m_behavior->MoveZ(event.m_inputChannel.GetValue());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case InputChannel::State::Ended:
|
||||
@@ -222,7 +232,10 @@ namespace MaterialEditor
|
||||
}
|
||||
else if (inputChannelId == InputDeviceMouse::Movement::Z)
|
||||
{
|
||||
m_behavior->MoveZ(event.m_inputChannel.GetValue());
|
||||
if (mouseOver)
|
||||
{
|
||||
m_behavior->MoveZ(event.m_inputChannel.GetValue());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -101,6 +101,11 @@ AZ::RPI::WindowContextSharedPtr AZ::FFont::GetDefaultWindowContext() const
|
||||
|
||||
bool AZ::FFont::InitFont(AZ::RPI::Scene* renderScene)
|
||||
{
|
||||
if (!renderScene)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto initializationState = InitializationState::Uninitialized;
|
||||
// Do an atomic transition to Initializing if we're in the Uninitialized state.
|
||||
// Otherwise, check the current state.
|
||||
@@ -111,11 +116,6 @@ bool AZ::FFont::InitFont(AZ::RPI::Scene* renderScene)
|
||||
return initializationState == InitializationState::Initialized;
|
||||
}
|
||||
|
||||
if (!renderScene)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create and initialize DynamicDrawContext for font draw
|
||||
AZ::RPI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = m_atomFont->GetOrCreateDynamicDrawForScene(renderScene);
|
||||
|
||||
|
||||
+24
-1
@@ -75,6 +75,12 @@ namespace AZ
|
||||
incompatible.push_back(AZ_CRC_CE("DecalService"));
|
||||
}
|
||||
|
||||
void DecalComponentController::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
|
||||
{
|
||||
dependent.push_back(AZ_CRC_CE("TransformService"));
|
||||
dependent.push_back(AZ_CRC_CE("NonUniformScaleService"));
|
||||
}
|
||||
|
||||
DecalComponentController::DecalComponentController(const DecalComponentConfig& config)
|
||||
: m_configuration(config)
|
||||
{
|
||||
@@ -90,6 +96,11 @@ namespace AZ
|
||||
m_handle = m_featureProcessor->AcquireDecal();
|
||||
}
|
||||
|
||||
m_cachedNonUniformScale = AZ::Vector3::CreateOne();
|
||||
AZ::NonUniformScaleRequestBus::EventResult(m_cachedNonUniformScale, m_entityId, &AZ::NonUniformScaleRequests::GetScale);
|
||||
AZ::NonUniformScaleRequestBus::Event(m_entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent,
|
||||
m_nonUniformScaleChangedHandler);
|
||||
|
||||
AZ::Transform local, world;
|
||||
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::GetLocalAndWorld, local, world);
|
||||
OnTransformChanged(local, world);
|
||||
@@ -103,6 +114,7 @@ namespace AZ
|
||||
{
|
||||
DecalRequestBus::Handler::BusDisconnect(m_entityId);
|
||||
TransformNotificationBus::Handler::BusDisconnect(m_entityId);
|
||||
m_nonUniformScaleChangedHandler.Disconnect();
|
||||
if (m_featureProcessor)
|
||||
{
|
||||
m_featureProcessor->ReleaseDecal(m_handle);
|
||||
@@ -125,7 +137,18 @@ namespace AZ
|
||||
{
|
||||
if (m_featureProcessor)
|
||||
{
|
||||
m_featureProcessor->SetDecalTransform(m_handle, world);
|
||||
m_featureProcessor->SetDecalTransform(m_handle, world, m_cachedNonUniformScale);
|
||||
}
|
||||
}
|
||||
|
||||
void DecalComponentController::HandleNonUniformScaleChange(const AZ::Vector3& nonUniformScale)
|
||||
{
|
||||
m_cachedNonUniformScale = nonUniformScale;
|
||||
if (m_featureProcessor)
|
||||
{
|
||||
AZ::Transform world = AZ::Transform::CreateIdentity();
|
||||
AZ::TransformBus::EventResult(world, m_entityId, &AZ::TransformBus::Events::GetWorldTM);
|
||||
m_featureProcessor->SetDecalTransform(m_handle, world, nonUniformScale);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Component/NonUniformScaleBus.h>
|
||||
#include <AtomLyIntegration/CommonFeatures/Decals/DecalBus.h>
|
||||
#include <AtomLyIntegration/CommonFeatures/Decals/DecalComponentConfig.h>
|
||||
#include <Atom/Feature/Decals/DecalFeatureProcessorInterface.h>
|
||||
@@ -33,6 +34,7 @@ namespace AZ
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
|
||||
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
|
||||
|
||||
DecalComponentController() = default;
|
||||
DecalComponentController(const DecalComponentConfig& config);
|
||||
@@ -64,11 +66,18 @@ namespace AZ
|
||||
void OpacityChanged();
|
||||
void SortKeyChanged();
|
||||
void MaterialChanged();
|
||||
void HandleNonUniformScaleChange(const AZ::Vector3& nonUniformScale);
|
||||
|
||||
DecalComponentConfig m_configuration;
|
||||
DecalFeatureProcessorInterface* m_featureProcessor = nullptr;
|
||||
DecalFeatureProcessorInterface::DecalHandle m_handle;
|
||||
EntityId m_entityId;
|
||||
AZ::Vector3 m_cachedNonUniformScale = AZ::Vector3::CreateOne();
|
||||
|
||||
AZ::NonUniformScaleChangedEvent::Handler m_nonUniformScaleChangedHandler
|
||||
{
|
||||
[&](const AZ::Vector3& nonUniformScale) { HandleNonUniformScaleChange(nonUniformScale); }
|
||||
};
|
||||
};
|
||||
} // namespace Render
|
||||
} // AZ namespace
|
||||
|
||||
@@ -131,6 +131,7 @@ namespace AZ
|
||||
void MeshComponentController::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
|
||||
{
|
||||
dependent.push_back(AZ_CRC("TransformService", 0x8ee22c50));
|
||||
dependent.push_back(AZ_CRC_CE("NonUniformScaleService"));
|
||||
}
|
||||
|
||||
void MeshComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
|
||||
+1
@@ -164,6 +164,7 @@ namespace AZ
|
||||
if (m_featureProcessor)
|
||||
{
|
||||
m_featureProcessor->RemoveProbe(m_handle);
|
||||
m_handle = nullptr;
|
||||
}
|
||||
|
||||
LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect();
|
||||
|
||||
@@ -739,7 +739,9 @@ namespace ImageProcessing
|
||||
|
||||
if (preset == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "preset should always exist");
|
||||
AZStd::string uuidStr;
|
||||
textureSettings.m_preset.ToString(uuidStr);
|
||||
AZ_Assert(false, "%s cannot find image preset with ID %s.", imageFilePath.c_str(), uuidStr.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ namespace LandscapeCanvasEditor
|
||||
|
||||
static const QStringList preferredCategories = {
|
||||
"Vegetation",
|
||||
"Rendering"
|
||||
"Atom"
|
||||
};
|
||||
|
||||
// There are a couple of cases where we prefer certain categories of Components
|
||||
|
||||
@@ -54,11 +54,6 @@ namespace LmbrCentral
|
||||
return "Icons/Components/Decal.svg";
|
||||
}
|
||||
|
||||
AZ::Uuid MaterialAssetTypeInfo::GetComponentTypeId() const
|
||||
{
|
||||
return AZ::Uuid("{BA3890BD-D2E7-4DB6-95CD-7E7D5525567A}");
|
||||
}
|
||||
|
||||
// DccMaterialAssetTypeInfo
|
||||
|
||||
DccMaterialAssetTypeInfo::~DccMaterialAssetTypeInfo()
|
||||
|
||||
@@ -30,7 +30,6 @@ namespace LmbrCentral
|
||||
const char* GetAssetTypeDisplayName() const override;
|
||||
const char* GetGroup() const override;
|
||||
const char* GetBrowserIcon() const override;
|
||||
AZ::Uuid GetComponentTypeId() const override;
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Register();
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace ScriptCanvasEditor
|
||||
}
|
||||
else
|
||||
{
|
||||
resultHash = ScriptCanvas::NodeUtils::ConstructMethodNodeIdentifier(classMethodTreeItem->GetClassMethodName(), classMethodTreeItem->GetMethodName());
|
||||
resultHash = ScriptCanvas::NodeUtils::ConstructMethodNodeIdentifier(classMethodTreeItem->GetClassMethodName(), classMethodTreeItem->GetMethodName(), classMethodTreeItem->GetPropertyStatus());
|
||||
}
|
||||
}
|
||||
else if (auto globalMethodTreeItem = azrtti_cast<const GlobalMethodEventPaletteTreeItem*>(treeItem); globalMethodTreeItem != nullptr)
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace ScriptCanvasEditor::Nodes
|
||||
return nodeIdPair;
|
||||
}
|
||||
|
||||
NodeIdPair CreateObjectMethodNode(AZStd::string_view className, AZStd::string_view methodName, const ScriptCanvas::ScriptCanvasId& scriptCanvasId)
|
||||
NodeIdPair CreateObjectMethodNode(AZStd::string_view className, AZStd::string_view methodName, const ScriptCanvas::ScriptCanvasId& scriptCanvasId, ScriptCanvas::PropertyStatus propertyStatus)
|
||||
{
|
||||
AZ_PROFILE_TIMER("ScriptCanvas", __FUNCTION__);
|
||||
NodeIdPair nodeIds;
|
||||
@@ -181,7 +181,7 @@ namespace ScriptCanvasEditor::Nodes
|
||||
auto* methodNode = azrtti_cast<ScriptCanvas::Nodes::Core::Method*>(node);
|
||||
|
||||
ScriptCanvas::NamespacePath emptyNamespacePath;
|
||||
methodNode->InitializeBehaviorMethod(emptyNamespacePath, className, methodName);
|
||||
methodNode->InitializeBehaviorMethod(emptyNamespacePath, className, methodName, propertyStatus);
|
||||
|
||||
AZStd::string_view displayName = methodNode->GetName();
|
||||
scriptCanvasEntity->SetName(AZStd::string::format("SC-Node(%.*s)", aznumeric_cast<int>(displayName.size()), displayName.data()));
|
||||
@@ -208,7 +208,7 @@ namespace ScriptCanvasEditor::Nodes
|
||||
auto* methodNode = azrtti_cast<ScriptCanvas::Nodes::Core::MethodOverloaded*>(node);
|
||||
|
||||
ScriptCanvas::NamespacePath emptyNamespacePath;
|
||||
methodNode->InitializeBehaviorMethod(emptyNamespacePath, className, methodName);
|
||||
methodNode->InitializeBehaviorMethod(emptyNamespacePath, className, methodName, ScriptCanvas::PropertyStatus::None);
|
||||
|
||||
AZStd::string_view displayName = methodNode->GetName();
|
||||
scriptCanvasEntity->SetName(AZStd::string::format("SC-Node(%.*s)", aznumeric_cast<int>(displayName.size()), displayName.data()));
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace ScriptCanvasEditor::Nodes
|
||||
AZStd::pair<ScriptCanvas::Node*, NodeIdPair> CreateAndGetNode(const AZ::Uuid& classData, const ScriptCanvas::ScriptCanvasId& scriptCanvasId, const StyleConfiguration& styleConfiguration, AZStd::function<void(ScriptCanvas::Node*)> = nullptr);
|
||||
NodeIdPair CreateNode(const AZ::Uuid& classData, const ScriptCanvas::ScriptCanvasId& scriptCanvasId, const StyleConfiguration& styleConfiguration);
|
||||
NodeIdPair CreateEntityNode(const AZ::EntityId& sourceId, const ScriptCanvas::ScriptCanvasId& scriptCanvasId);
|
||||
NodeIdPair CreateObjectMethodNode(AZStd::string_view className, AZStd::string_view methodName, const ScriptCanvas::ScriptCanvasId& scriptCanvasId);
|
||||
NodeIdPair CreateObjectMethodNode(AZStd::string_view className, AZStd::string_view methodName, const ScriptCanvas::ScriptCanvasId& scriptCanvasId, ScriptCanvas::PropertyStatus propertyStatus);
|
||||
NodeIdPair CreateObjectMethodOverloadNode(AZStd::string_view className, AZStd::string_view methodName, const ScriptCanvas::ScriptCanvasId& scriptCanvasGraphId);
|
||||
NodeIdPair CreateGlobalMethodNode(AZStd::string_view methodName, const ScriptCanvas::ScriptCanvasId& scriptCanvasId);
|
||||
NodeIdPair CreateEbusWrapperNode(AZStd::string_view busName, const ScriptCanvas::ScriptCanvasId& scriptCanvasId);
|
||||
|
||||
@@ -327,12 +327,14 @@ namespace ScriptCanvasEditor::Nodes
|
||||
contextGroup = TranslationContextGroup::EbusSender;
|
||||
break;
|
||||
case ScriptCanvas::MethodType::Member:
|
||||
case ScriptCanvas::MethodType::Getter:
|
||||
case ScriptCanvas::MethodType::Setter:
|
||||
case ScriptCanvas::MethodType::Free:
|
||||
graphCanvasEntity->CreateComponent<ClassMethodNodeDescriptorComponent>();
|
||||
contextGroup = TranslationContextGroup::ClassMethod;
|
||||
break;
|
||||
default:
|
||||
AZ_Error("ScriptCanvas", false, "Invalid method node type, node creation failed. This node nodes to be deleted.");
|
||||
AZ_Error("ScriptCanvas", false, "Invalid method node type, node creation failed. This node needs to be deleted.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+12
-4
@@ -52,14 +52,16 @@ namespace ScriptCanvasEditor
|
||||
->Field("BusName", &CreateEBusSenderMimeEvent::m_busName)
|
||||
->Field("EventName", &CreateEBusSenderMimeEvent::m_eventName)
|
||||
->Field("IsOverload", &CreateEBusSenderMimeEvent::m_isOverload)
|
||||
->Field("propertyStatus", &CreateEBusSenderMimeEvent::m_propertyStatus)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
CreateEBusSenderMimeEvent::CreateEBusSenderMimeEvent(AZStd::string_view busName, AZStd::string_view eventName, bool isOverload)
|
||||
CreateEBusSenderMimeEvent::CreateEBusSenderMimeEvent(AZStd::string_view busName, AZStd::string_view eventName, bool isOverload, ScriptCanvas::PropertyStatus propertyStatus)
|
||||
: m_busName(busName.data())
|
||||
, m_eventName(eventName.data())
|
||||
, m_isOverload(isOverload)
|
||||
, m_propertyStatus(propertyStatus)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -71,7 +73,7 @@ namespace ScriptCanvasEditor
|
||||
}
|
||||
else
|
||||
{
|
||||
return Nodes::CreateObjectMethodNode(m_busName, m_eventName, scriptCanvasId);
|
||||
return Nodes::CreateObjectMethodNode(m_busName, m_eventName, scriptCanvasId, m_propertyStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,13 +93,14 @@ namespace ScriptCanvasEditor
|
||||
return defaultIcon;
|
||||
}
|
||||
|
||||
EBusSendEventPaletteTreeItem::EBusSendEventPaletteTreeItem(AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busIdentifier, const ScriptCanvas::EBusEventId& eventIdentifier, bool isOverload)
|
||||
EBusSendEventPaletteTreeItem::EBusSendEventPaletteTreeItem(AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busIdentifier, const ScriptCanvas::EBusEventId& eventIdentifier, bool isOverload, ScriptCanvas::PropertyStatus propertyStatus)
|
||||
: DraggableNodePaletteTreeItem(eventName, ScriptCanvasEditor::AssetEditorId)
|
||||
, m_busName(busName.data())
|
||||
, m_eventName(eventName.data())
|
||||
, m_busId(busIdentifier)
|
||||
, m_eventId(eventIdentifier)
|
||||
, m_isOverload(isOverload)
|
||||
, m_propertyStatus(propertyStatus)
|
||||
{
|
||||
AZStd::string displayEventName = TranslationHelper::GetKeyTranslation(TranslationContextGroup::EbusSender, m_busName.toUtf8().data(), m_eventName.toUtf8().data(), TranslationItemType::Node, TranslationKeyId::Name);
|
||||
|
||||
@@ -122,7 +125,7 @@ namespace ScriptCanvasEditor
|
||||
|
||||
GraphCanvas::GraphCanvasMimeEvent* EBusSendEventPaletteTreeItem::CreateMimeEvent() const
|
||||
{
|
||||
return aznew CreateEBusSenderMimeEvent(m_busName.toUtf8().data(), m_eventName.toUtf8().data(), m_isOverload);
|
||||
return aznew CreateEBusSenderMimeEvent(m_busName.toUtf8().data(), m_eventName.toUtf8().data(), m_isOverload, ScriptCanvas::PropertyStatus::None);
|
||||
}
|
||||
|
||||
AZStd::string EBusSendEventPaletteTreeItem::GetBusName() const
|
||||
@@ -145,6 +148,11 @@ namespace ScriptCanvasEditor
|
||||
return m_eventId;
|
||||
}
|
||||
|
||||
ScriptCanvas::PropertyStatus EBusSendEventPaletteTreeItem::GetPropertyStatus() const
|
||||
{
|
||||
return m_propertyStatus;
|
||||
}
|
||||
|
||||
bool EBusSendEventPaletteTreeItem::IsOverload() const
|
||||
{
|
||||
return m_isOverload;
|
||||
|
||||
+6
-2
@@ -28,7 +28,7 @@ namespace ScriptCanvasEditor
|
||||
static void Reflect(AZ::ReflectContext* reflectContext);
|
||||
|
||||
CreateEBusSenderMimeEvent() = default;
|
||||
CreateEBusSenderMimeEvent(AZStd::string_view busName, AZStd::string_view eventName, bool isOverload);
|
||||
CreateEBusSenderMimeEvent(AZStd::string_view busName, AZStd::string_view eventName, bool isOverload, ScriptCanvas::PropertyStatus propertyStatus);
|
||||
~CreateEBusSenderMimeEvent() = default;
|
||||
|
||||
protected:
|
||||
@@ -36,6 +36,7 @@ namespace ScriptCanvasEditor
|
||||
|
||||
private:
|
||||
bool m_isOverload;
|
||||
ScriptCanvas::PropertyStatus m_propertyStatus = ScriptCanvas::PropertyStatus::None;
|
||||
AZStd::string m_busName;
|
||||
AZStd::string m_eventName;
|
||||
};
|
||||
@@ -50,7 +51,7 @@ namespace ScriptCanvasEditor
|
||||
AZ_CLASS_ALLOCATOR(EBusSendEventPaletteTreeItem, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(EBusSendEventPaletteTreeItem, "{26258B0A-8E2C-434D-ACAD-3DE85E64A4F8}", GraphCanvas::DraggableNodePaletteTreeItem);
|
||||
|
||||
EBusSendEventPaletteTreeItem(AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const ScriptCanvas::EBusEventId& eventIdentifier, bool isOverload);
|
||||
EBusSendEventPaletteTreeItem(AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const ScriptCanvas::EBusEventId& eventIdentifier, bool isOverload, ScriptCanvas::PropertyStatus propertyStatus);
|
||||
~EBusSendEventPaletteTreeItem() = default;
|
||||
|
||||
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override;
|
||||
@@ -63,6 +64,8 @@ namespace ScriptCanvasEditor
|
||||
|
||||
bool IsOverload() const;
|
||||
|
||||
ScriptCanvas::PropertyStatus GetPropertyStatus() const;
|
||||
|
||||
private:
|
||||
bool m_isOverload;
|
||||
QString m_busName;
|
||||
@@ -70,6 +73,7 @@ namespace ScriptCanvasEditor
|
||||
|
||||
ScriptCanvas::EBusBusId m_busId;
|
||||
ScriptCanvas::EBusEventId m_eventId;
|
||||
ScriptCanvas::PropertyStatus m_propertyStatus = ScriptCanvas::PropertyStatus::None;
|
||||
};
|
||||
|
||||
// </EbusSender>
|
||||
|
||||
+21
-4
@@ -51,14 +51,16 @@ namespace ScriptCanvasEditor
|
||||
->Field("ClassName", &CreateClassMethodMimeEvent::m_className)
|
||||
->Field("MethodName", &CreateClassMethodMimeEvent::m_methodName)
|
||||
->Field("IsOverload", &CreateClassMethodMimeEvent::m_isOverload)
|
||||
->Field("propertyStatus", &CreateClassMethodMimeEvent::m_propertyStatus)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
CreateClassMethodMimeEvent::CreateClassMethodMimeEvent(const QString& className, const QString& methodName, bool isOverload)
|
||||
CreateClassMethodMimeEvent::CreateClassMethodMimeEvent(const QString& className, const QString& methodName, bool isOverload, ScriptCanvas::PropertyStatus propertyStatus)
|
||||
: m_className(className.toUtf8().data())
|
||||
, m_methodName(methodName.toUtf8().data())
|
||||
, m_isOverload(isOverload)
|
||||
, m_propertyStatus(propertyStatus)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -70,7 +72,7 @@ namespace ScriptCanvasEditor
|
||||
}
|
||||
else
|
||||
{
|
||||
return Nodes::CreateObjectMethodNode(m_className, m_methodName, scriptCanvasId);
|
||||
return Nodes::CreateObjectMethodNode(m_className, m_methodName, scriptCanvasId, m_propertyStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,11 +80,12 @@ namespace ScriptCanvasEditor
|
||||
// ClassMethodEventPaletteTreeItem
|
||||
////////////////////////////////////
|
||||
|
||||
ClassMethodEventPaletteTreeItem::ClassMethodEventPaletteTreeItem(AZStd::string_view className, AZStd::string_view methodName, bool isOverload)
|
||||
ClassMethodEventPaletteTreeItem::ClassMethodEventPaletteTreeItem(AZStd::string_view className, AZStd::string_view methodName, bool isOverload, ScriptCanvas::PropertyStatus propertyStatus)
|
||||
: DraggableNodePaletteTreeItem(methodName, ScriptCanvasEditor::AssetEditorId)
|
||||
, m_className(className.data())
|
||||
, m_methodName(methodName.data())
|
||||
, m_isOverload(isOverload)
|
||||
, m_propertyStatus(propertyStatus)
|
||||
{
|
||||
AZStd::string displayMethodName = TranslationHelper::GetKeyTranslation(TranslationContextGroup::ClassMethod, m_className.toUtf8().data(), m_methodName.toUtf8().data(), TranslationItemType::Node, TranslationKeyId::Name);
|
||||
|
||||
@@ -95,6 +98,15 @@ namespace ScriptCanvasEditor
|
||||
SetName(displayMethodName.c_str());
|
||||
}
|
||||
|
||||
if (propertyStatus == ScriptCanvas::PropertyStatus::Getter)
|
||||
{
|
||||
SetName(AZStd::string::format("Get %s", GetName().toUtf8().data()).data());
|
||||
}
|
||||
else if (propertyStatus == ScriptCanvas::PropertyStatus::Setter)
|
||||
{
|
||||
SetName(AZStd::string::format("Set %s", GetName().toUtf8().data()).data());
|
||||
}
|
||||
|
||||
AZStd::string displayEventTooltip = TranslationHelper::GetKeyTranslation(TranslationContextGroup::ClassMethod, m_className.toUtf8().data(), m_methodName.toUtf8().data(), TranslationItemType::Node, TranslationKeyId::Tooltip);
|
||||
|
||||
if (!displayEventTooltip.empty())
|
||||
@@ -107,7 +119,7 @@ namespace ScriptCanvasEditor
|
||||
|
||||
GraphCanvas::GraphCanvasMimeEvent* ClassMethodEventPaletteTreeItem::CreateMimeEvent() const
|
||||
{
|
||||
return aznew CreateClassMethodMimeEvent(m_className, m_methodName, m_isOverload);
|
||||
return aznew CreateClassMethodMimeEvent(m_className, m_methodName, m_isOverload, m_propertyStatus);
|
||||
}
|
||||
|
||||
AZStd::string ClassMethodEventPaletteTreeItem::GetClassMethodName() const
|
||||
@@ -125,6 +137,11 @@ namespace ScriptCanvasEditor
|
||||
return m_isOverload;
|
||||
}
|
||||
|
||||
ScriptCanvas::PropertyStatus ClassMethodEventPaletteTreeItem::GetPropertyStatus() const
|
||||
{
|
||||
return m_propertyStatus;
|
||||
}
|
||||
|
||||
//! Implementation of the CreateGlobalMethod Mime Event
|
||||
void CreateGlobalMethodMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
|
||||
{
|
||||
|
||||
+5
-2
@@ -30,7 +30,7 @@ namespace ScriptCanvasEditor
|
||||
static void Reflect(AZ::ReflectContext* reflectContext);
|
||||
|
||||
CreateClassMethodMimeEvent() = default;
|
||||
CreateClassMethodMimeEvent(const QString& className, const QString& methodName, bool isOverload);
|
||||
CreateClassMethodMimeEvent(const QString& className, const QString& methodName, bool isOverload, ScriptCanvas::PropertyStatus);
|
||||
~CreateClassMethodMimeEvent() = default;
|
||||
|
||||
protected:
|
||||
@@ -40,6 +40,7 @@ namespace ScriptCanvasEditor
|
||||
bool m_isOverload = false;
|
||||
AZStd::string m_className;
|
||||
AZStd::string m_methodName;
|
||||
ScriptCanvas::PropertyStatus m_propertyStatus = ScriptCanvas::PropertyStatus::None;
|
||||
};
|
||||
|
||||
class ClassMethodEventPaletteTreeItem
|
||||
@@ -49,7 +50,7 @@ namespace ScriptCanvasEditor
|
||||
AZ_CLASS_ALLOCATOR(ClassMethodEventPaletteTreeItem, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(ClassMethodEventPaletteTreeItem, "{96F93970-F38A-4F08-8DC5-D52FCCE34E25}", GraphCanvas::DraggableNodePaletteTreeItem);
|
||||
|
||||
ClassMethodEventPaletteTreeItem(AZStd::string_view className, AZStd::string_view methodName, bool isOverload);
|
||||
ClassMethodEventPaletteTreeItem(AZStd::string_view className, AZStd::string_view methodName, bool isOverload, ScriptCanvas::PropertyStatus propertyStatus);
|
||||
~ClassMethodEventPaletteTreeItem() = default;
|
||||
|
||||
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override;
|
||||
@@ -57,11 +58,13 @@ namespace ScriptCanvasEditor
|
||||
AZStd::string GetClassMethodName() const;
|
||||
AZStd::string GetMethodName() const;
|
||||
bool IsOverload() const;
|
||||
ScriptCanvas::PropertyStatus GetPropertyStatus() const;
|
||||
|
||||
private:
|
||||
bool m_isOverload = false;
|
||||
QString m_className;
|
||||
QString m_methodName;
|
||||
ScriptCanvas::PropertyStatus m_propertyStatus = ScriptCanvas::PropertyStatus::None;
|
||||
};
|
||||
// </ClassMethod>
|
||||
|
||||
|
||||
@@ -129,6 +129,7 @@ namespace
|
||||
, const AZ::BehaviorClass* behaviorClass
|
||||
, const AZStd::string& name
|
||||
, const AZ::BehaviorMethod& method
|
||||
, ScriptCanvas::PropertyStatus propertyStatus
|
||||
, bool isOverloaded)
|
||||
{
|
||||
if (IsDeprecated(method.m_attributes))
|
||||
@@ -170,7 +171,7 @@ namespace
|
||||
serializeContext->RegisterType(resultParameter->m_typeId, AZStd::move(classData), EventPlaceholderAnyCreator);
|
||||
|
||||
}
|
||||
nodePaletteModel.RegisterClassNode(categoryPath, behaviorClass ? behaviorClass->m_name : "", name, &method, &behaviorContext, isOverloaded);
|
||||
nodePaletteModel.RegisterClassNode(categoryPath, behaviorClass ? behaviorClass->m_name : "", name, &method, &behaviorContext, propertyStatus, isOverloaded);
|
||||
}
|
||||
|
||||
void RegisterGlobalMethod(ScriptCanvasEditor::NodePaletteModel& nodePaletteModel, const AZ::BehaviorContext& behaviorContext,
|
||||
@@ -556,6 +557,19 @@ namespace
|
||||
categoryPath.append(displayName.c_str());
|
||||
}
|
||||
|
||||
for (auto property : behaviorClass->m_properties)
|
||||
{
|
||||
if (property.second->m_getter)
|
||||
{
|
||||
RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, property.first, *property.second->m_getter, ScriptCanvas::PropertyStatus::Getter, behaviorClass->IsMethodOverloaded(property.first));
|
||||
}
|
||||
|
||||
if (property.second->m_setter)
|
||||
{
|
||||
RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, property.first, *property.second->m_setter, ScriptCanvas::PropertyStatus::Setter, behaviorClass->IsMethodOverloaded(property.first));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto methodIter : behaviorClass->m_methods)
|
||||
{
|
||||
if (!IsExplicitOverload(*methodIter.second))
|
||||
@@ -567,7 +581,7 @@ namespace
|
||||
continue;
|
||||
}
|
||||
|
||||
RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, methodIter.first, *methodIter.second, behaviorClass->IsMethodOverloaded(methodIter.first));
|
||||
RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, methodIter.first, *methodIter.second, ScriptCanvas::PropertyStatus::None, behaviorClass->IsMethodOverloaded(methodIter.first));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -579,7 +593,7 @@ namespace
|
||||
{
|
||||
for (const AZ::ExplicitOverloadInfo& explicitOverload : behaviorContext.m_explicitOverloads)
|
||||
{
|
||||
RegisterMethod(nodePaletteModel, behaviorContext, explicitOverload.m_categoryPath, nullptr, explicitOverload.m_name, *explicitOverload.m_overloads.begin()->first, true);
|
||||
RegisterMethod(nodePaletteModel, behaviorContext, explicitOverload.m_categoryPath, nullptr, explicitOverload.m_name, *explicitOverload.m_overloads.begin()->first, ScriptCanvas::PropertyStatus::None, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -717,7 +731,7 @@ namespace
|
||||
}
|
||||
|
||||
const bool isOverload{ false }; // overloaded events are not trivially supported
|
||||
nodePaletteModel.RegisterEBusSenderNodeModelInformation(categoryPath, behaviorEbus.m_name, event.first, ScriptCanvas::EBusBusId(behaviorEbus.m_name.c_str()), ScriptCanvas::EBusEventId(event.first.c_str()), event.second, isOverload);
|
||||
nodePaletteModel.RegisterEBusSenderNodeModelInformation(categoryPath, behaviorEbus.m_name, event.first, ScriptCanvas::EBusBusId(behaviorEbus.m_name.c_str()), ScriptCanvas::EBusEventId(event.first.c_str()), event.second, ScriptCanvas::PropertyStatus::None, isOverload);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1024,11 +1038,16 @@ namespace ScriptCanvasEditor
|
||||
}
|
||||
}
|
||||
|
||||
void NodePaletteModel::RegisterClassNode(const AZStd::string& categoryPath, const AZStd::string& methodClass,
|
||||
const AZStd::string& methodName, const AZ::BehaviorMethod* behaviorMethod, const AZ::BehaviorContext* behaviorContext,
|
||||
bool isOverload)
|
||||
void NodePaletteModel::RegisterClassNode
|
||||
( const AZStd::string& categoryPath
|
||||
, const AZStd::string& methodClass
|
||||
, const AZStd::string& methodName
|
||||
, const AZ::BehaviorMethod* behaviorMethod
|
||||
, const AZ::BehaviorContext* behaviorContext
|
||||
, ScriptCanvas::PropertyStatus propertyStatus
|
||||
, bool isOverload)
|
||||
{
|
||||
ScriptCanvas::NodeTypeIdentifier nodeIdentifier = isOverload ? ScriptCanvas::NodeUtils::ConstructMethodOverloadedNodeIdentifier(methodName) : ScriptCanvas::NodeUtils::ConstructMethodNodeIdentifier(methodClass, methodName);
|
||||
ScriptCanvas::NodeTypeIdentifier nodeIdentifier = isOverload ? ScriptCanvas::NodeUtils::ConstructMethodOverloadedNodeIdentifier(methodName) : ScriptCanvas::NodeUtils::ConstructMethodNodeIdentifier(methodClass, methodName, propertyStatus);
|
||||
|
||||
auto registerIter = m_registeredNodes.find(nodeIdentifier);
|
||||
|
||||
@@ -1039,7 +1058,7 @@ namespace ScriptCanvasEditor
|
||||
methodModelInformation->m_nodeIdentifier = nodeIdentifier;
|
||||
methodModelInformation->m_classMethod = methodClass;
|
||||
methodModelInformation->m_methodName = methodName;
|
||||
|
||||
methodModelInformation->m_propertyStatus = propertyStatus;
|
||||
methodModelInformation->m_titlePaletteOverride = "MethodNodeTitlePalette";
|
||||
|
||||
methodModelInformation->m_displayName = TranslationHelper::GetKeyTranslation(TranslationContextGroup::ClassMethod, methodClass.c_str(), methodName.c_str(), TranslationItemType::Node, TranslationKeyId::Name);
|
||||
@@ -1198,7 +1217,15 @@ namespace ScriptCanvasEditor
|
||||
}
|
||||
}
|
||||
|
||||
void NodePaletteModel::RegisterEBusSenderNodeModelInformation(AZStd::string_view categoryPath, AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const ScriptCanvas::EBusEventId& eventId, const AZ::BehaviorEBusEventSender&, bool isOverload)
|
||||
void NodePaletteModel::RegisterEBusSenderNodeModelInformation
|
||||
( AZStd::string_view categoryPath
|
||||
, AZStd::string_view busName
|
||||
, AZStd::string_view eventName
|
||||
, const ScriptCanvas::EBusBusId& busId
|
||||
, const ScriptCanvas::EBusEventId& eventId
|
||||
, const AZ::BehaviorEBusEventSender&
|
||||
, ScriptCanvas::PropertyStatus propertyStatus
|
||||
, bool isOverload)
|
||||
{
|
||||
ScriptCanvas::NodeTypeIdentifier nodeIdentifier = isOverload ? ScriptCanvas::NodeUtils::ConstructEBusEventSenderOverloadedIdentifier(busId, eventId) : ScriptCanvas::NodeUtils::ConstructEBusEventSenderIdentifier(busId, eventId);
|
||||
|
||||
@@ -1212,6 +1239,7 @@ namespace ScriptCanvasEditor
|
||||
senderInformation->m_titlePaletteOverride = "MethodNodeTitlePalette";
|
||||
senderInformation->m_categoryPath = categoryPath;
|
||||
senderInformation->m_nodeIdentifier = nodeIdentifier;
|
||||
senderInformation->m_propertyStatus = propertyStatus;
|
||||
|
||||
senderInformation->m_busName = busName;
|
||||
senderInformation->m_eventName = eventName;
|
||||
|
||||
@@ -83,12 +83,12 @@ namespace ScriptCanvasEditor
|
||||
void RepopulateModel();
|
||||
|
||||
void RegisterCustomNode(AZStd::string_view categoryPath, const AZ::Uuid& uuid, AZStd::string_view name, const AZ::SerializeContext::ClassData* classData);
|
||||
void RegisterClassNode(const AZStd::string& categoryPath, const AZStd::string& methodClass, const AZStd::string& methodName, const AZ::BehaviorMethod* behaviorMethod, const AZ::BehaviorContext* behaviorContext, bool isOverload);
|
||||
void RegisterClassNode(const AZStd::string& categoryPath, const AZStd::string& methodClass, const AZStd::string& methodName, const AZ::BehaviorMethod* behaviorMethod, const AZ::BehaviorContext* behaviorContext, ScriptCanvas::PropertyStatus propertyStatus, bool isOverload);
|
||||
void RegisterMethodNode(const AZ::BehaviorContext& behaviorContext, const AZ::BehaviorMethod& behaviorMethod);
|
||||
void RegisterGlobalConstant(const AZ::BehaviorContext& behaviorContext, const AZ::BehaviorMethod& behaviorMethod);
|
||||
|
||||
void RegisterEBusHandlerNodeModelInformation(AZStd::string_view categoryPath, AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const AZ::BehaviorEBusHandler::BusForwarderEvent& forwardEvent);
|
||||
void RegisterEBusSenderNodeModelInformation(AZStd::string_view categoryPath, AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const ScriptCanvas::EBusEventId& eventId, const AZ::BehaviorEBusEventSender& eventSender, bool isOverload);
|
||||
void RegisterEBusSenderNodeModelInformation(AZStd::string_view categoryPath, AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const ScriptCanvas::EBusEventId& eventId, const AZ::BehaviorEBusEventSender& eventSender, ScriptCanvas::PropertyStatus propertyStatus, bool isOverload);
|
||||
|
||||
// Asset Based Registrations
|
||||
AZStd::vector<ScriptCanvas::NodeTypeIdentifier> RegisterScriptEvent(ScriptEvents::ScriptEventsAsset* scriptEventAsset);
|
||||
@@ -164,6 +164,7 @@ namespace ScriptCanvasEditor
|
||||
bool m_isOverload{};
|
||||
AZStd::string m_classMethod;
|
||||
AZStd::string m_methodName;
|
||||
ScriptCanvas::PropertyStatus m_propertyStatus = ScriptCanvas::PropertyStatus::None;
|
||||
};
|
||||
|
||||
struct GlobalMethodNodeModelInformation
|
||||
@@ -202,6 +203,7 @@ namespace ScriptCanvasEditor
|
||||
|
||||
ScriptCanvas::EBusBusId m_busId;
|
||||
ScriptCanvas::EBusEventId m_eventId;
|
||||
ScriptCanvas::PropertyStatus m_propertyStatus = ScriptCanvas::PropertyStatus::None;
|
||||
};
|
||||
|
||||
struct ScriptEventHandlerNodeModelInformation
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace ScriptCanvasEditor
|
||||
}
|
||||
else if (auto methodNodeModelInformation = azrtti_cast<const MethodNodeModelInformation*>(modelInformation))
|
||||
{
|
||||
createdItem = parentItem->CreateChildNode<ScriptCanvasEditor::ClassMethodEventPaletteTreeItem>(methodNodeModelInformation->m_classMethod, methodNodeModelInformation->m_methodName, methodNodeModelInformation->m_isOverload);
|
||||
createdItem = parentItem->CreateChildNode<ScriptCanvasEditor::ClassMethodEventPaletteTreeItem>(methodNodeModelInformation->m_classMethod, methodNodeModelInformation->m_methodName, methodNodeModelInformation->m_isOverload, methodNodeModelInformation->m_propertyStatus);
|
||||
}
|
||||
else if (auto globalMethodNodeModelInformation = azrtti_cast<const GlobalMethodNodeModelInformation*>(modelInformation);
|
||||
globalMethodNodeModelInformation != nullptr)
|
||||
@@ -130,7 +130,7 @@ namespace ScriptCanvasEditor
|
||||
{
|
||||
if (!azrtti_istypeof<const ScriptEventSenderNodeModelInformation*>(ebusSenderNodeModelInformation))
|
||||
{
|
||||
createdItem = parentItem->CreateChildNode<ScriptCanvasEditor::EBusSendEventPaletteTreeItem>(ebusSenderNodeModelInformation->m_busName, ebusSenderNodeModelInformation->m_eventName, ebusSenderNodeModelInformation->m_busId, ebusSenderNodeModelInformation->m_eventId, ebusSenderNodeModelInformation->m_isOverload);
|
||||
createdItem = parentItem->CreateChildNode<ScriptCanvasEditor::EBusSendEventPaletteTreeItem>(ebusSenderNodeModelInformation->m_busName, ebusSenderNodeModelInformation->m_eventName, ebusSenderNodeModelInformation->m_busId, ebusSenderNodeModelInformation->m_eventId, ebusSenderNodeModelInformation->m_isOverload, ebusSenderNodeModelInformation->m_propertyStatus);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,13 @@ namespace ScriptCanvas
|
||||
Current,
|
||||
};
|
||||
|
||||
enum class PropertyStatus : AZ::u8
|
||||
{
|
||||
Getter,
|
||||
None,
|
||||
Setter,
|
||||
};
|
||||
|
||||
struct VersionData
|
||||
{
|
||||
AZ_TYPE_INFO(VersionData, "{14C629F6-467B-46FE-8B63-48FDFCA42175}");
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace ScriptCanvas
|
||||
Event,
|
||||
Free,
|
||||
Member,
|
||||
Getter,
|
||||
Setter,
|
||||
Count,
|
||||
};
|
||||
|
||||
|
||||
@@ -394,6 +394,7 @@ namespace ScriptCanvas
|
||||
AZ::Uuid m_type = AZ::Uuid::CreateNull();
|
||||
AZStd::string m_className;
|
||||
AZStd::string m_methodName;
|
||||
PropertyStatus m_propertyStatus = PropertyStatus::None;
|
||||
|
||||
bool IsValid()
|
||||
{
|
||||
|
||||
@@ -104,8 +104,7 @@ namespace ScriptCanvas
|
||||
|
||||
const FunctorOut& Nodeable::GetExecutionOutChecked(size_t index) const
|
||||
{
|
||||
|
||||
if (index >= m_outs.size() && m_outs[index])
|
||||
if (index >= m_outs.size() || !m_outs[index])
|
||||
{
|
||||
return m_noOpFunctor;
|
||||
}
|
||||
|
||||
@@ -537,6 +537,20 @@ namespace ScriptCanvas
|
||||
return azrtti_istypeof<const ScriptCanvas::Nodes::Logic::Break*>(execution->GetId().m_node);
|
||||
}
|
||||
|
||||
bool IsClassPropertyRead(ExecutionTreeConstPtr execution)
|
||||
{
|
||||
return execution->GetSymbol() == Symbol::FunctionCall
|
||||
&& azrtti_istypeof<const ScriptCanvas::Nodes::Core::Method*>(execution->GetId().m_node)
|
||||
&& azrtti_cast<const ScriptCanvas::Nodes::Core::Method*>(execution->GetId().m_node)->GetPropertyStatus() == PropertyStatus::Getter;
|
||||
}
|
||||
|
||||
bool IsClassPropertyWrite(ExecutionTreeConstPtr execution)
|
||||
{
|
||||
return execution->GetSymbol() == Symbol::FunctionCall
|
||||
&& azrtti_istypeof<const ScriptCanvas::Nodes::Core::Method*>(execution->GetId().m_node)
|
||||
&& azrtti_cast<const ScriptCanvas::Nodes::Core::Method*>(execution->GetId().m_node)->GetPropertyStatus() == PropertyStatus::Setter;
|
||||
}
|
||||
|
||||
bool IsCodeConstructable(Grammar::VariableConstPtr value)
|
||||
{
|
||||
return Data::IsValueType(value->m_datum.GetType())
|
||||
@@ -1280,7 +1294,6 @@ namespace ScriptCanvas
|
||||
return identifier;
|
||||
}
|
||||
|
||||
|
||||
ExecutionTraversalResult TraverseExecutionConnectionsRecurse(const EndpointsResolved& nextEndpoints, AZStd::unordered_set<const Slot*>& previousIns, GraphExecutionPathTraversalListener& listener);
|
||||
|
||||
ExecutionTraversalResult TraverseExecutionConnectionsRecurse(const EndpointResolved& in, AZStd::unordered_set<const Slot*>& previousIns, GraphExecutionPathTraversalListener& listener);
|
||||
|
||||
@@ -79,6 +79,10 @@ namespace ScriptCanvas
|
||||
|
||||
bool IsBreak(const ExecutionTreeConstPtr& execution);
|
||||
|
||||
bool IsClassPropertyRead(ExecutionTreeConstPtr execution);
|
||||
|
||||
bool IsClassPropertyWrite(ExecutionTreeConstPtr execution);
|
||||
|
||||
bool IsCodeConstructable(VariableConstPtr value);
|
||||
|
||||
bool IsCycle(const Node& node);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user