Merge branch 'main' into LYN-3449

This commit is contained in:
srikappa
2021-05-03 16:46:49 -07:00
8 changed files with 155 additions and 46 deletions
+3 -1
View File
@@ -75,7 +75,9 @@ foreach(restricted_platform ${PAL_RESTRICTED_PLATFORMS})
endif()
endforeach()
add_subdirectory(scripts)
if(NOT INSTALLED_ENGINE)
add_subdirectory(scripts)
endif()
# SPEC-1417 will investigate and fix this
if(NOT PAL_PLATFORM_NAME STREQUAL "Mac")
@@ -75,6 +75,16 @@ namespace AWSCore
class AWSScriptBehaviorS3
: public AWSScriptBehaviorBase
{
static constexpr const char AWSScriptBehaviorS3Name[] = "AWSScriptBehaviorS3";
static constexpr const char OutputFileIsEmptyErrorMessage[] = "Request validation failed, output file is empty.";
static constexpr const char OutputFileMissFullPathErrorMessage[] = "Request validation failed, output file miss full path.";
static constexpr const char OutputFileIsDirectoryErrorMessage[] = "Request validation failed, output file is a directory.";
static constexpr const char OutputFileDirectoryNotExistErrorMessage[] = "Request validation failed, output file directory doesn't exist.";
static constexpr const char OutputFileIsReadOnlyErrorMessage[] = "Request validation failed, output file is read-only.";
static constexpr const char BucketNameIsEmptyErrorMessage[] = "Request validation failed, bucket name is empty";
static constexpr const char ObjectKeyNameIsEmptyErrorMessage[] = "Request validation failed, object key name is empty.";
static constexpr const char RegionNameIsEmptyErrorMessage[] = "Request validation failed, region name is empty.";
public:
AWS_SCRIPT_BEHAVIOR_DEFINITION(AWSScriptBehaviorS3, "{7F4E956C-7463-4236-B320-C992D36A9C6E}");
@@ -87,7 +97,7 @@ namespace AWSCore
private:
using S3NotificationFunctionType = void(AWSScriptBehaviorS3Notifications::*)(const AZStd::string&);
static bool ValidateGetObjectRequest(S3NotificationFunctionType notificationFunc,
const AZStd::string& bucket, const AZStd::string& objectKey, const AZStd::string& region, const AZStd::string& outFile);
const AZStd::string& bucket, const AZStd::string& objectKey, const AZStd::string& region, AZStd::string& outFile);
static bool ValidateHeadObjectRequest(S3NotificationFunctionType notificationFunc,
const AZStd::string& bucket, const AZStd::string& key, const AZStd::string& region);
@@ -43,7 +43,7 @@ namespace AWSCore
void AWSScriptBehaviorS3::ReflectBehaviors(AZ::BehaviorContext* behaviorContext)
{
behaviorContext->Class<AWSScriptBehaviorS3>("AWSScriptBehaviorS3")
behaviorContext->Class<AWSScriptBehaviorS3>(AWSScriptBehaviorS3Name)
->Attribute(AZ::Script::Attributes::Category, "AWSCore")
->Method("GetObject", &AWSScriptBehaviorS3::GetObject,
{{{"Bucket Resource KeyName", "The resource key name of the bucket in resource mapping config file."},
@@ -86,7 +86,9 @@ namespace AWSCore
void AWSScriptBehaviorS3::GetObjectRaw(
const AZStd::string& bucket, const AZStd::string& objectKey, const AZStd::string& region, const AZStd::string& outFile)
{
if (!ValidateGetObjectRequest(&AWSScriptBehaviorS3NotificationBus::Events::OnGetObjectError, bucket, objectKey, region, outFile))
AZStd::string normalizedOutFile = outFile;
if (!ValidateGetObjectRequest(
&AWSScriptBehaviorS3NotificationBus::Events::OnGetObjectError, bucket, objectKey, region, normalizedOutFile))
{
return;
}
@@ -112,10 +114,10 @@ namespace AWSCore
job->request.SetBucket(Aws::String(bucket.c_str()));
job->request.SetKey(Aws::String(objectKey.c_str()));
Aws::String outFileName(outFile.c_str());
Aws::String outFileName(normalizedOutFile.c_str());
job->request.SetResponseStreamFactory([outFileName]() {
return Aws::New<Aws::FStream>(
"AWSScriptBehaviorS3", outFileName.c_str(),
AWSScriptBehaviorS3Name, outFileName.c_str(),
std::ios_base::out | std::ios_base::in | std::ios_base::binary | std::ios_base::trunc);
});
job->Start();
@@ -163,20 +165,44 @@ namespace AWSCore
}
bool AWSScriptBehaviorS3::ValidateGetObjectRequest(S3NotificationFunctionType notificationFunc,
const AZStd::string& bucket, const AZStd::string& objectKey, const AZStd::string& region, const AZStd::string& outFile)
const AZStd::string& bucket, const AZStd::string& objectKey, const AZStd::string& region, AZStd::string& outFile)
{
if (ValidateHeadObjectRequest(notificationFunc, bucket, objectKey, region))
{
if (!AzFramework::StringFunc::Path::IsValid(outFile.c_str()))
AzFramework::StringFunc::Path::Normalize(outFile);
if (outFile.empty())
{
AZ_Warning("AWSScriptBehaviorS3", false, "Request validation failed, outfile is not valid.");
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, "Request validation failed, outfile is not valid.");
AZ_Warning(AWSScriptBehaviorS3Name, false, OutputFileIsEmptyErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, OutputFileIsEmptyErrorMessage);
return false;
}
if (!AzFramework::StringFunc::Path::HasDrive(outFile.c_str()))
{
AZ_Warning(AWSScriptBehaviorS3Name, false, OutputFileMissFullPathErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, OutputFileMissFullPathErrorMessage);
return false;
}
if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(outFile.c_str()))
{
AZ_Warning(AWSScriptBehaviorS3Name, false, OutputFileIsDirectoryErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, OutputFileIsDirectoryErrorMessage);
return false;
}
auto lastSeparator = outFile.find_last_of(AZ_CORRECT_FILESYSTEM_SEPARATOR);
if (lastSeparator != AZStd::string::npos)
{
auto parentPath = outFile.substr(0, lastSeparator);
if (!AZ::IO::FileIOBase::GetInstance()->Exists(parentPath.c_str()))
{
AZ_Warning(AWSScriptBehaviorS3Name, false, OutputFileDirectoryNotExistErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, OutputFileDirectoryNotExistErrorMessage);
return false;
}
}
if (AZ::IO::FileIOBase::GetInstance()->IsReadOnly(outFile.c_str()))
{
AZ_Warning("AWSScriptBehaviorS3", false, "Request validation failed, outfile is read-only.");
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, "Request validation failed, outfile is read-only.");
AZ_Warning(AWSScriptBehaviorS3Name, false, OutputFileIsReadOnlyErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, OutputFileIsReadOnlyErrorMessage);
return false;
}
return true;
@@ -189,20 +215,20 @@ namespace AWSCore
{
if (bucket.empty())
{
AZ_Warning("AWSScriptBehaviorS3", false, "Request validation failed, bucket name is required.");
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, "Request validation failed, bucket name is required.");
AZ_Warning(AWSScriptBehaviorS3Name, false, BucketNameIsEmptyErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, BucketNameIsEmptyErrorMessage);
return false;
}
if (objectKey.empty())
{
AZ_Warning("AWSScriptBehaviorS3", false, "Request validation failed, object key name is required.");
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, "Request validation failed, object key name is required.");
AZ_Warning(AWSScriptBehaviorS3Name, false, ObjectKeyNameIsEmptyErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, ObjectKeyNameIsEmptyErrorMessage);
return false;
}
if (region.empty())
{
AZ_Warning("AWSScriptBehaviorS3", false, "Request validation failed, region name is required.");
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, "Request validation failed, region name is required.");
AZ_Warning(AWSScriptBehaviorS3Name, false, RegionNameIsEmptyErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, RegionNameIsEmptyErrorMessage);
return false;
}
return true;
@@ -11,6 +11,7 @@
*/
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzTest/AzTest.h>
#include <ScriptCanvas/AWSScriptBehaviorS3.h>
@@ -38,7 +39,37 @@ public:
MOCK_METHOD1(OnGetObjectError, void(const AZStd::string&));
};
using AWSScriptBehaviorS3Test = UnitTest::ScopedAllocatorSetupFixture;
class AWSScriptBehaviorS3Test
: public AWSCoreFixture
{
public:
void CreateReadOnlyTestFile(const AZStd::string& filePath)
{
AZ::IO::SystemFile file;
if (!file.Open(
filePath.c_str(),
AZ::IO::SystemFile::OpenMode::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
{
AZ_Assert(false, "Failed to open test file at %s", filePath.c_str());
}
AZStd::string testContent = "It is a test file";
if (file.Write(testContent.c_str(), testContent.size()) != testContent.size())
{
AZ_Assert(false, "Failed to write test file with content %s", testContent.c_str());
}
file.Close();
AZ_Assert(AZ::IO::SystemFile::SetWritable(filePath.c_str(), false), "Failed to mark test file as read-only");
}
void RemoveReadOnlyTestFile(const AZStd::string& filePath)
{
if (!filePath.empty())
{
AZ_Assert(AZ::IO::SystemFile::SetWritable(filePath.c_str(), true), "Failed to mark test file as writeable");
AZ_Assert(AZ::IO::SystemFile::Delete(filePath.c_str()), "Failed to delete test config file at %s", filePath.c_str());
}
}
};
TEST_F(AWSScriptBehaviorS3Test, HeadObjectRaw_CallWithEmptyBucketName_InvokeOnError)
{
@@ -96,6 +127,40 @@ TEST_F(AWSScriptBehaviorS3Test, GetObjectRaw_CallWithEmptyOutfileName_InvokeOnEr
AWSScriptBehaviorS3::GetObjectRaw("dummyBucket", "dummyObject", "dummyRegion", "");
}
TEST_F(AWSScriptBehaviorS3Test, GetObjectRaw_CallWithOutfileNameMissFullPath_InvokeOnError)
{
AWSScriptBehaviorS3NotificationBusHandlerMock s3HandlerMock;
EXPECT_CALL(s3HandlerMock, OnGetObjectError(::testing::_)).Times(1);
AWSScriptBehaviorS3::GetObjectRaw("dummyBucket", "dummyObject", "dummyRegion", "dummyOut.txt");
}
TEST_F(AWSScriptBehaviorS3Test, GetObjectRaw_CallWithOutfileNameIsDirectory_InvokeOnError)
{
AWSScriptBehaviorS3NotificationBusHandlerMock s3HandlerMock;
EXPECT_CALL(s3HandlerMock, OnGetObjectError(::testing::_)).Times(1);
AWSScriptBehaviorS3::GetObjectRaw("dummyBucket", "dummyObject", "dummyRegion", AZ::Test::GetCurrentExecutablePath());
}
TEST_F(AWSScriptBehaviorS3Test, GetObjectRaw_CallWithOutfileDirectoryNoExist_InvokeOnError)
{
AWSScriptBehaviorS3NotificationBusHandlerMock s3HandlerMock;
EXPECT_CALL(s3HandlerMock, OnGetObjectError(::testing::_)).Times(1);
AZStd::string dummyDirectory = AZStd::string::format("%s/dummyDirectory/dummyOut.txt", AZ::Test::GetCurrentExecutablePath().c_str());
AWSScriptBehaviorS3::GetObjectRaw("dummyBucket", "dummyObject", "dummyRegion", dummyDirectory);
}
TEST_F(AWSScriptBehaviorS3Test, GetObjectRaw_CallWithOutfileIsReadOnly_InvokeOnError)
{
AWSScriptBehaviorS3NotificationBusHandlerMock s3HandlerMock;
EXPECT_CALL(s3HandlerMock, OnGetObjectError(::testing::_)).Times(1);
AZStd::string randomTestFile = AZStd::string::format("%s/test%s.txt",
AZ::Test::GetCurrentExecutablePath().c_str(), AZ::Uuid::CreateRandom().ToString<AZStd::string>(false, false).c_str());
AzFramework::StringFunc::Path::Normalize(randomTestFile);
CreateReadOnlyTestFile(randomTestFile);
AWSScriptBehaviorS3::GetObjectRaw("dummyBucket", "dummyObject", "dummyRegion", randomTestFile);
RemoveReadOnlyTestFile(randomTestFile);
}
TEST_F(AWSScriptBehaviorS3Test, GetObject_NoBucketNameInResourceMappingFound_InvokeOnError)
{
AWSScriptBehaviorS3NotificationBusHandlerMock s3HandlerMock;
@@ -220,7 +220,7 @@ namespace EMotionFX
//mPool->mFreeList.Reserve( numInstances * 2 );
if (mPool->mFreeList.GetMaxLength() < mPool->mNumInstances)
{
mPool->mFreeList.Reserve(mPool->mNumInstances);
mPool->mFreeList.Reserve(mPool->mNumInstances + mPool->mFreeList.GetMaxLength() / 2);
}
mPool->mFreeList.ResizeFast(startIndex + numInstances);
@@ -290,7 +290,7 @@ namespace AZ::SceneGenerationComponents
const bool hasBlendShapes = HasAnyBlendShapeChild(graph, nodeIndex);
auto [optimizedMesh, optimizedUVs, optimizedTangents, optimizedBitangents, optimizedVertexColors, optimizedSkinWeights] = OptimizeMesh(mesh, uvDatas, tangentDatas, bitangentDatas, colorDatas, skinWeightDatas, meshGroup, hasBlendShapes);
auto [optimizedMesh, optimizedUVs, optimizedTangents, optimizedBitangents, optimizedVertexColors, optimizedSkinWeights] = OptimizeMesh(mesh, mesh, uvDatas, tangentDatas, bitangentDatas, colorDatas, skinWeightDatas, meshGroup, hasBlendShapes);
const NodeIndex optimizedMeshNodeIndex = graph.AddChild(graph.GetNodeParent(nodeIndex), name.c_str(), AZStd::move(optimizedMesh));
@@ -322,7 +322,7 @@ namespace AZ::SceneGenerationComponents
for (const NodeIndex& blendShapeNodeIndex : nodeIndexes(Containers::MakeDerivedFilterView<IBlendShapeData>(childNodes(nodeIndex))))
{
const IBlendShapeData* blendShapeNode = static_cast<IBlendShapeData*>(graph.GetNodeContent(blendShapeNodeIndex).get());
auto [optimizedBlendShape, _1, _2, _3 , _4, _5] = OptimizeMesh(blendShapeNode, {}, {}, {}, {}, {}, meshGroup, hasBlendShapes);
auto [optimizedBlendShape, _1, _2, _3 , _4, _5] = OptimizeMesh(blendShapeNode, mesh, {}, {}, {}, {}, {}, meshGroup, hasBlendShapes);
const AZStd::string optimizedName {graph.GetNodeName(blendShapeNodeIndex).GetName(), graph.GetNodeName(blendShapeNodeIndex).GetNameLength()};
const NodeIndex optimizedNodeIndex = graph.AddChild(optimizedMeshNodeIndex, optimizedName.c_str(), AZStd::move(optimizedBlendShape));
@@ -383,6 +383,7 @@ namespace AZ::SceneGenerationComponents
AZStd::unique_ptr<AZ::SceneAPI::DataTypes::ISkinWeightData>
> MeshOptimizerComponent::OptimizeMesh(
const MeshDataType* meshData,
const IMeshData* baseMesh,
const AZStd::vector<AZStd::reference_wrapper<const IMeshVertexUVData>>& uvs,
const AZStd::vector<AZStd::reference_wrapper<const IMeshVertexTangentData>>& tangents,
const AZStd::vector<AZStd::reference_wrapper<const IMeshVertexBitangentData>>& bitangents,
@@ -441,7 +442,7 @@ namespace AZ::SceneGenerationComponents
const AZ::u32 faceCount = meshData->GetFaceCount();
for (AZ::u32 faceIndex = 0; faceIndex < faceCount; ++faceIndex)
{
meshBuilder.BeginPolygon(GetFaceMaterialId(meshData, faceIndex));
meshBuilder.BeginPolygon(baseMesh->GetFaceMaterialId(faceIndex));
for (const AZ::u32 vertexIndex : meshData->GetFaceInfo(faceIndex).vertexIndex)
{
const int orgVertexNumber = meshData->GetUsedPointIndexForControlPoint(meshData->GetControlPointIndex(vertexIndex));
@@ -584,15 +585,6 @@ namespace AZ::SceneGenerationComponents
);
}
unsigned int MeshOptimizerComponent::GetFaceMaterialId([[maybe_unused]] const AZ::SceneAPI::DataTypes::IBlendShapeData* meshData, [[maybe_unused]] unsigned int index)
{
return 0;
}
unsigned int MeshOptimizerComponent::GetFaceMaterialId(const AZ::SceneAPI::DataTypes::IMeshData* meshData, unsigned int index)
{
return meshData->GetFaceMaterialId(index);
}
void MeshOptimizerComponent::AddFace(AZ::SceneData::GraphData::BlendShapeData* blendShape, unsigned int index1, unsigned int index2, unsigned int index3, [[maybe_unused]] unsigned int faceMaterialId)
{
blendShape->AddFace({index1, index2, index3});
@@ -66,6 +66,7 @@ namespace AZ::SceneGenerationComponents
AZStd::unique_ptr<AZ::SceneAPI::DataTypes::ISkinWeightData>
> OptimizeMesh(
const MeshDataType* meshData,
const SceneAPI::DataTypes::IMeshData* baseMesh,
const AZStd::vector<AZStd::reference_wrapper<const AZ::SceneAPI::DataTypes::IMeshVertexUVData>>& uvs,
const AZStd::vector<AZStd::reference_wrapper<const AZ::SceneAPI::DataTypes::IMeshVertexTangentData>>& tangents,
const AZStd::vector<AZStd::reference_wrapper<const AZ::SceneAPI::DataTypes::IMeshVertexBitangentData>>& bitangents,
@@ -74,9 +75,6 @@ namespace AZ::SceneGenerationComponents
const AZ::SceneAPI::DataTypes::IMeshGroup& meshGroup,
bool hasBlendShapes);
static unsigned int GetFaceMaterialId(const AZ::SceneAPI::DataTypes::IBlendShapeData* meshData, unsigned int index);
static unsigned int GetFaceMaterialId(const AZ::SceneAPI::DataTypes::IMeshData* meshData, unsigned int index);
static void AddFace(AZ::SceneData::GraphData::BlendShapeData* blendShape, unsigned int index1, unsigned int index2, unsigned int index3, unsigned int faceMaterialId);
static void AddFace(AZ::SceneData::GraphData::MeshData* mesh, unsigned int index1, unsigned int index2, unsigned int index3, unsigned int faceMaterialId);
};
+27 -11
View File
@@ -286,7 +286,7 @@ endfunction()
function(ly_setup_others)
# List of directories we want to install relative to engine root
set(DIRECTORIES_TO_INSTALL Tools/LyTestTools Tools/RemoteConsole scripts)
set(DIRECTORIES_TO_INSTALL Tools/LyTestTools Tools/RemoteConsole)
foreach(dir ${DIRECTORIES_TO_INSTALL})
get_filename_component(install_path ${dir} DIRECTORY)
@@ -301,6 +301,24 @@ function(ly_setup_others)
endforeach()
# Scripts
file(GLOB o3de_scripts "${CMAKE_SOURCE_DIR}/scripts/o3de.*")
install(FILES
${o3de_scripts}
DESTINATION ./scripts
COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT}
)
install(DIRECTORY
${CMAKE_SOURCE_DIR}/scripts/bundler
${CMAKE_SOURCE_DIR}/scripts/project_manager
DESTINATION ./scripts
COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT}
PATTERN "__pycache__" EXCLUDE
PATTERN "CMakeLists.txt" EXCLUDE
PATTERN "tests" EXCLUDE
)
install(DIRECTORY "${CMAKE_SOURCE_DIR}/python"
DESTINATION .
COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT}
@@ -311,7 +329,7 @@ function(ly_setup_others)
# Registry
install(DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}/bin/$<CONFIG>/Registry
DESTINATION ./bin/$<CONFIG>
DESTINATION ./bin/${PAL_PLATFORM_NAME}/$<CONFIG>
COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT}
)
install(DIRECTORY
@@ -350,16 +368,14 @@ function(ly_setup_others)
endif()
endforeach()
# Qt Binaries
set(QT_BIN_DIRS bearer iconengines imageformats platforms styles translations)
foreach(qt_dir ${QT_BIN_DIRS})
install(DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}/bin/$<CONFIG>/${qt_dir}
DESTINATION ./bin/$<CONFIG>
COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT}
)
endforeach()
set(QT_DIRS bearer iconengines imageformats platforms styles translations)
list(TRANSFORM QT_DIRS PREPEND "${CMAKE_CURRENT_BINARY_DIR}/bin/$<CONFIG>/" OUTPUT_VARIABLE QT_BIN_DIRS)
install(DIRECTORY
${QT_BIN_DIRS}
DESTINATION ./bin/${PAL_PLATFORM_NAME}/$<CONFIG>
COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT}
)
# Templates
install(DIRECTORY