Merge branch 'main' into Atom/dmcdiar/ATOM-15517

main
Doug McDiarmid 5 years ago
commit ef05412063

@ -97,16 +97,19 @@ class Cdk:
env=self._cdk_env,
shell=True)
def deploy(self, context_variable: str = '') -> List[str]:
def deploy(self, context_variable: str = '', additonal_params: List[str] = None) -> List[str]:
"""
Deploys all the CDK stacks.
:param context_variable: Context variable for enabling optional features.
:param additonal_params: Additonal parameters like --all can be passed in this way.
:return List of deployed stack arns.
"""
if not self._cdk_path:
return []
deploy_cdk_application_cmd = ['cdk', 'deploy', '--require-approval', 'never']
if additonal_params:
deploy_cdk_application_cmd.extend(additonal_params)
if context_variable:
deploy_cdk_application_cmd.extend(['-c', f'{context_variable}'])

@ -137,5 +137,6 @@ enum class AnimParamType
Invalid = static_cast<int>(0xFFFFFFFF)
};
static const int OLD_APARAM_USER = 100;
#endif // CRYINCLUDE_CRYCOMMON_MAESTRO_TYPES_ANIMPARAMTYPE_H

@ -561,7 +561,7 @@ void DebugCallStack::LogExceptionInfo(EXCEPTION_POINTERS* pex)
if (pex)
{
MINIDUMP_TYPE mdumpValue;
MINIDUMP_TYPE mdumpValue = MiniDumpNormal;
bool bDump = true;
switch (g_cvars.sys_dump_type)
{

@ -66,7 +66,7 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
}
if (pSystem && !pSystem->IsQuitting())
{
LRESULT result;
LRESULT result = 0;
bool bAny = false;
for (std::vector<IWindowMessageHandler*>::const_iterator it = pSystem->m_windowMessageHandlers.begin(); it != pSystem->m_windowMessageHandlers.end(); ++it)
{

@ -634,7 +634,7 @@ ICVar* CSystem::attachVariable (const char* szVarName, int* pContainer, const ch
IConsole* pConsole = GetIConsole();
ICVar* pOldVar = pConsole->GetCVar (szVarName);
int nDefault;
int nDefault = 0;
if (pOldVar)
{
nDefault = pOldVar->GetIVal();

@ -1914,7 +1914,7 @@ namespace UnitTest
TEST_F(String, StringView_CompareIsConstexpr)
{
using TypeParam = char;
auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
{
return "HelloWorld";
};
@ -1922,7 +1922,7 @@ namespace UnitTest
{
return "HelloPearl";
};
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1();
constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2();
constexpr basic_string_view<TypeParam> lhsView(compileTimeString1);
constexpr basic_string_view<TypeParam> rhsView(compileTimeString2);
@ -1937,11 +1937,11 @@ namespace UnitTest
TEST_F(String, StringView_CompareOperatorsAreConstexpr)
{
using TypeParam = char;
auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
auto TestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
{
return "HelloWorld";
};
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
constexpr const TypeParam* compileTimeString1 = TestMakeCompileTimeString1();
constexpr basic_string_view<TypeParam> compareView(compileTimeString1);
static_assert(compareView == "HelloWorld", "string_view operator== comparison has failed");
static_assert(compareView != "MadWorld", "string_view operator!= comparison has failed");
@ -1955,7 +1955,7 @@ namespace UnitTest
{
auto swap_test_func = []() constexpr -> basic_string_view<TypeParam>
{
constexpr auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
constexpr auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
{
if constexpr (AZStd::is_same_v<TypeParam, char>)
{
@ -1977,7 +1977,7 @@ namespace UnitTest
return L"InuWorld";
}
};
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1();
constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2();
basic_string_view<TypeParam> lhsView(compileTimeString1);
basic_string_view<TypeParam> rhsView(compileTimeString2);
@ -2001,7 +2001,7 @@ namespace UnitTest
TYPED_TEST(BasicStringViewConstexprFixture, HashString_FunctionIsConstexpr)
{
auto MakeCompileTimeString1 = []() constexpr -> const TypeParam*
auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam*
{
if constexpr (AZStd::is_same_v<TypeParam, char>)
{
@ -2012,7 +2012,7 @@ namespace UnitTest
return L"HelloWorld";
}
};
constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1();
constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1();
constexpr basic_string_view<TypeParam> hashView(compileTimeString1);
constexpr size_t compileHash = AZStd::hash<basic_string_view<TypeParam>>{}(hashView);
static_assert(compileHash != 0, "Hash of \"HelloWorld\" should not be 0");

@ -395,7 +395,8 @@ namespace UnitTest
}
else
{
int result1, result2;
int result1 = 0;
int result2 = 0;
Job* job1 = aznew FibonacciJob2(m_n - 1, &result1, m_context);
Job* job2 = aznew FibonacciJob2(m_n - 2, &result2, m_context);
StartAsChild(job1);

@ -59,7 +59,7 @@ namespace UnitTest
TEST(MATH_Matrix4x4, TestCreateFrom)
{
float testFloats[] =
float thisTestFloats[] =
{
1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
@ -67,20 +67,20 @@ namespace UnitTest
13.0f, 14.0f, 15.0f, 16.0f
};
float testFloatMtx[16];
Matrix4x4 m1 = Matrix4x4::CreateFromRowMajorFloat16(testFloats);
Matrix4x4 m1 = Matrix4x4::CreateFromRowMajorFloat16(thisTestFloats);
AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(1.0f, 2.0f, 3.0f, 4.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(5.0f, 6.0f, 7.0f, 8.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(9.0f, 10.0f, 11.0f, 12.0f));
AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(13.0f, 14.0f, 15.0f, 16.0f));
m1.StoreToRowMajorFloat16(testFloatMtx);
AZ_TEST_ASSERT(memcmp(testFloatMtx, testFloats, sizeof(testFloatMtx)) == 0);
m1 = Matrix4x4::CreateFromColumnMajorFloat16(testFloats);
AZ_TEST_ASSERT(memcmp(testFloatMtx, thisTestFloats, sizeof(testFloatMtx)) == 0);
m1 = Matrix4x4::CreateFromColumnMajorFloat16(thisTestFloats);
AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(1.0f, 5.0f, 9.0f, 13.0f));
AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(2.0f, 6.0f, 10.0f, 14.0f));
AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(3.0f, 7.0f, 11.0f, 15.0f));
AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(4.0f, 8.0f, 12.0f, 16.0f));
m1.StoreToColumnMajorFloat16(testFloatMtx);
AZ_TEST_ASSERT(memcmp(testFloatMtx, testFloats, sizeof(testFloatMtx)) == 0);
AZ_TEST_ASSERT(memcmp(testFloatMtx, thisTestFloats, sizeof(testFloatMtx)) == 0);
}
TEST(MATH_Matrix4x4, TestCreateFromMatrix3x4)

@ -119,10 +119,10 @@ namespace UnitTest
TEST(MATH_Obb, Contains)
{
const Vector3 position(1.0f, 2.0f, 3.0f);
const Quaternion rotation = Quaternion::CreateRotationZ(DegToRad(30.0f));
const Vector3 halfLengths(2.0f, 1.0f, 2.5f);
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
const Vector3 testPosition(1.0f, 2.0f, 3.0f);
const Quaternion testRotation = Quaternion::CreateRotationZ(DegToRad(30.0f));
const Vector3 testHalfLengths(2.0f, 1.0f, 2.5f);
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths);
// test some pairs of points which should be just either side of the Obb boundary
EXPECT_TRUE(obb.Contains(Vector3(1.35f, 3.35f, 3.5f)));
EXPECT_FALSE(obb.Contains(Vector3(1.35f, 3.4f, 3.5f)));
@ -134,10 +134,10 @@ namespace UnitTest
TEST(MATH_Obb, GetDistance)
{
const Vector3 position(5.0f, 3.0f, 2.0f);
const Quaternion rotation = Quaternion::CreateRotationX(DegToRad(60.0f));
const Vector3 halfLengths(0.5f, 2.0f, 1.5f);
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
const Vector3 testPosition(5.0f, 3.0f, 2.0f);
const Quaternion testRotation = Quaternion::CreateRotationX(DegToRad(60.0f));
const Vector3 testHalfLengths(0.5f, 2.0f, 1.5f);
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths);
EXPECT_NEAR(obb.GetDistance(Vector3(5.3f, 3.2f, 1.8f)), 0.0f, 1e-3f);
EXPECT_NEAR(obb.GetDistance(Vector3(5.1f, 1.1f, 3.7f)), 0.9955f, 1e-3f);
EXPECT_NEAR(obb.GetDistance(Vector3(4.7f, 4.5f, 4.2f)), 0.6553f, 1e-3f);
@ -146,10 +146,10 @@ namespace UnitTest
TEST(MATH_Obb, GetDistanceSq)
{
const Vector3 position(1.0f, 4.0f, 3.0f);
const Quaternion rotation = Quaternion::CreateRotationY(DegToRad(45.0f));
const Vector3 halfLengths(1.5f, 3.0f, 1.0f);
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths);
const Vector3 testPosition(1.0f, 4.0f, 3.0f);
const Quaternion testRotation = Quaternion::CreateRotationY(DegToRad(45.0f));
const Vector3 testHalfLengths(1.5f, 3.0f, 1.0f);
const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths);
EXPECT_NEAR(obb.GetDistanceSq(Vector3(1.1f, 4.3f, 2.7f)), 0.0f, 1e-3f);
EXPECT_NEAR(obb.GetDistanceSq(Vector3(-0.7f, 3.5f, 2.0f)), 0.8266f, 1e-3f);
EXPECT_NEAR(obb.GetDistanceSq(Vector3(2.4f, 0.5f, 1.5f)), 0.5532f, 1e-3f);

@ -353,6 +353,7 @@ namespace AzFramework
// Get the dimensions of the display device on which the window is currently displayed.
MONITORINFO monitorInfo;
memset(&monitorInfo, 0, sizeof(MONITORINFO)); // C4701 potentially uninitialized local variable 'monitorInfo' used
monitorInfo.cbSize = sizeof(MONITORINFO);
const BOOL success = monitor ? GetMonitorInfo(monitor, &monitorInfo) : FALSE;
if (!success)

@ -46,7 +46,7 @@ namespace AzNetworking
}
else if (m_updateRate < updateTimeMs)
{
AZLOG_INFO("TimedThread bled %d ms", aznumeric_cast<int32_t>(updateTimeMs - m_updateRate));
AZLOG(NET_TimedThread, "TimedThread bled %d ms", aznumeric_cast<int32_t>(updateTimeMs - m_updateRate));
}
}
OnStop();

@ -124,4 +124,15 @@ namespace AzToolsFramework
return cameraState;
}
float GetScreenDisplayScaling(const int viewportId)
{
float scaling = 1.0f;
ViewportInteraction::ViewportInteractionRequestBus::EventResult(
scaling, viewportId,
&ViewportInteraction::ViewportInteractionRequestBus::Events::DeviceScalingFactor);
return scaling;
}
} // namespace AzToolsFramework

@ -60,6 +60,9 @@ namespace AzToolsFramework
/// Wrapper for EBus call to return the CameraState for a given viewport.
AzFramework::CameraState GetCameraState(int viewportId);
/// Wrapper for EBus call to return the DPI scaling for a given viewport.
float GetScreenDisplayScaling(const int viewportId);
/// A utility to return the center of several points.
/// Take several positions and store the min and max of each in
/// turn - when all points have been added return the center/midpoint.

@ -3573,9 +3573,10 @@ namespace AzToolsFramework
debugDisplay.SetLineWidth(1.0f);
const float labelOffset = cl_viewportGizmoAxisLabelOffset;
const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize;
const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize;
const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize;
const float screenScale = GetScreenDisplayScaling(viewportId);
const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize * screenScale;
// draw the label of of each axis for the gizmo
const float labelSize = cl_viewportGizmoAxisLabelSize;

@ -9,6 +9,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Launcher.h>
#include <AzCore/Casting/numeric_cast.h>
@ -22,6 +23,8 @@
#include <AzCore/Utils/Utils.h>
#include <AzFramework/Asset/AssetSystemBus.h>
#include <AzFramework/IO/RemoteStorageDrive.h>
#include <AzFramework/Windowing/NativeWindow.h>
#include <AzFramework/Windowing/WindowBus.h>
#include <AzGameFramework/Application/GameApplication.h>
@ -45,6 +48,19 @@ extern "C" void CreateStaticModules(AZStd::vector<AZ::Module*>& modulesOut);
namespace
{
void OnViewportResize(const AZ::Vector2& value);
AZ_CVAR(AZ::Vector2, r_viewportSize, AZ::Vector2::CreateZero(), OnViewportResize, AZ::ConsoleFunctorFlags::DontReplicate,
"The default size for the launcher viewport, 0 0 means full screen");
void OnViewportResize(const AZ::Vector2& value)
{
AzFramework::NativeWindowHandle windowHandle = nullptr;
AzFramework::WindowSystemRequestBus::BroadcastResult(windowHandle, &AzFramework::WindowSystemRequestBus::Events::GetDefaultWindowHandle);
AzFramework::WindowSize newSize = AzFramework::WindowSize(aznumeric_cast<int32_t>(value.GetX()), aznumeric_cast<int32_t>(value.GetY()));
AzFramework::WindowRequestBus::Broadcast(&AzFramework::WindowRequestBus::Events::ResizeClientArea, newSize);
}
void ExecuteConsoleCommandFile(AzFramework::Application& application)
{
const AZStd::string_view customConCmdKey = "console-command-file";

@ -1233,7 +1233,7 @@ void EditorViewportWidget::SetViewportId(int id)
auto controller = AZStd::make_shared<AtomToolsFramework::ModularViewportCameraController>();
controller->SetCameraListBuilderCallback(
[](AzFramework::Cameras& cameras)
[id](AzFramework::Cameras& cameras)
{
auto firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::CameraFreeLookButton);
auto firstPersonPanCamera =
@ -1243,17 +1243,17 @@ void EditorViewportWidget::SetViewportId(int id)
auto orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>();
orbitCamera->SetLookAtFn(
[](const AZ::Vector3& position, const AZ::Vector3& direction) -> AZStd::optional<AZ::Vector3>
[id](const AZ::Vector3& position, const AZ::Vector3& direction) -> AZStd::optional<AZ::Vector3>
{
AZStd::optional<AZ::Transform> manipulatorTransform;
AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult(
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
AZStd::optional<AZ::Vector3> lookAtAfterInterpolation;
AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult(
lookAtAfterInterpolation, id,
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::LookAtAfterInterpolation);
// initially attempt to use manipulator transform if one exists (there is a selection)
if (manipulatorTransform)
// initially attempt to use the last set look at point after an interpolation has finished
if (lookAtAfterInterpolation.has_value())
{
return manipulatorTransform->GetTranslation();
return *lookAtAfterInterpolation;
}
const float RayDistance = 1000.0f;

@ -553,7 +553,7 @@ void CLogFile::OnWriteToConsole(const char* sText, bool bNewLine)
// remember selection and the top row
int len = m_hWndEditBox->document()->toPlainText().length();
int top;
int top = 0;
int from = m_hWndEditBox->textCursor().selectionStart();
int to = from + m_hWndEditBox->textCursor().selectionEnd();
bool keepPos = false;

@ -121,7 +121,7 @@ protected:
};
#endif
Q_GLOBAL_STATIC(QtViewPaneManager, s_instance)
Q_GLOBAL_STATIC(QtViewPaneManager, s_viewPaneManagerInstance)
QWidget* QtViewPane::CreateWidget()
@ -611,12 +611,12 @@ void QtViewPaneManager::UnregisterPane(const QString& name)
QtViewPaneManager* QtViewPaneManager::instance()
{
return s_instance();
return s_viewPaneManagerInstance();
}
bool QtViewPaneManager::exists()
{
return s_instance.exists();
return s_viewPaneManagerInstance.exists();
}
void QtViewPaneManager::SetMainWindow(AzQtComponents::DockMainWindow* mainWindow, QSettings* settings, const QByteArray& lastMainWindowState)

@ -59,7 +59,7 @@ namespace
{
int fps;
const char* fpsDesc;
} fps[] = {
} fpsOptions[] = {
{24, "Film(24)"}, {25, "PAL(25)"}, {30, "NTSC(30)"},
{48, "Show(48)"}, {50, "PAL Field(50)"}, {60, "NTSC Field(60)"}
};
@ -213,9 +213,9 @@ void CSequenceBatchRenderDialog::OnInitDialog()
m_ui->m_resolutionCombo->setCurrentIndex(0);
// Fill the FPS combo box.
for (int i = 0; i < AZStd::size(fps); ++i)
for (int i = 0; i < AZStd::size(fpsOptions); ++i)
{
m_ui->m_fpsCombo->addItem(fps[i].fpsDesc);
m_ui->m_fpsCombo->addItem(fpsOptions[i].fpsDesc);
}
m_ui->m_fpsCombo->setCurrentIndex(0);
@ -306,9 +306,9 @@ void CSequenceBatchRenderDialog::OnRenderItemSelChange()
m_ui->m_destinationEdit->setText(item.folder);
// fps
bool bFound = false;
for (int i = 0; i < arraysize(fps); ++i)
for (int i = 0; i < arraysize(fpsOptions); ++i)
{
if (item.fps == fps[i].fps)
if (item.fps == fpsOptions[i].fps)
{
m_ui->m_fpsCombo->setCurrentIndex(i);
bFound = true;
@ -621,7 +621,7 @@ void CSequenceBatchRenderDialog::OnFPSEditChange()
void CSequenceBatchRenderDialog::OnFPSChange(int itemIndex)
{
m_customFPS = fps[itemIndex].fps;
m_customFPS = fpsOptions[itemIndex].fps;
CheckForEnableUpdateButton();
}
@ -1543,13 +1543,13 @@ bool CSequenceBatchRenderDialog::SetUpNewRenderItem(SRenderItem& item)
item.frameRange = Range(m_ui->m_startFrame->value() / m_fpsForTimeToFrameConversion,
m_ui->m_endFrame->value() / m_fpsForTimeToFrameConversion);
// fps
if (m_ui->m_fpsCombo->currentIndex() == -1 || m_ui->m_fpsCombo->currentText() != fps[m_ui->m_fpsCombo->currentIndex()].fpsDesc)
if (m_ui->m_fpsCombo->currentIndex() == -1 || m_ui->m_fpsCombo->currentText() != fpsOptions[m_ui->m_fpsCombo->currentIndex()].fpsDesc)
{
item.fps = m_customFPS;
}
else
{
item.fps = fps[m_ui->m_fpsCombo->currentIndex()].fps;
item.fps = fpsOptions[m_ui->m_fpsCombo->currentIndex()].fps;
}
// prefix
item.prefix = m_ui->BATCH_RENDER_FILE_PREFIX->text();

@ -157,7 +157,7 @@ static Quatern Qt_FromMatrix(HMatrix mat)
* |w| is greater than 1/2, which is as small as a largest component can be.
* Otherwise, the largest diagonal entry corresponds to the largest of |x|,
* |y|, or |z|, one of which must be larger than |w|, and at least 1/2. */
Quatern qu;
Quatern qu = { 0.0f, 0.0f, 0.0f, 1.0f };
double tr, s;
tr = mat[X][X] + mat[Y][Y] + mat[Z][Z];
@ -531,7 +531,7 @@ Quatern snuggle(Quatern q, HVect* k)
#define swap(a, i, j) {a[3] = a[i]; a[i] = a[j]; a[j] = a[3]; }
#define cycle(a, p) if (p) {a[3] = a[0]; a[0] = a[1]; a[1] = a[2]; a[2] = a[3]; } \
else {a[3] = a[2]; a[2] = a[1]; a[1] = a[0]; a[0] = a[3]; }
Quatern p;
Quatern p = { 0.0f, 0.0f, 0.0f, 1.0f };
float ka[4];
int i, turn = -1;
ka[X] = k->x;

@ -2239,7 +2239,8 @@ uint32 CFileUtil::GetAttributes(const char* filename, bool bUseSourceControl /*=
bool CFileUtil::CompareFiles(const QString& strFilePath1, const QString& strFilePath2)
{
// Get the size of both files. If either fails we say they are different (most likely one doesn't exist)
uint64 size1, size2;
uint64 size1 = 0;
uint64 size2 = 0;
if (!GetDiskFileSize(strFilePath1.toUtf8().data(), size1) || !GetDiskFileSize(strFilePath2.toUtf8().data(), size2))
{
return false;

@ -116,6 +116,7 @@ bool CImageBT::Load(const QString& fileName, CFloatImage& image)
// Get the BT header data
BtHeader header;
memset(&header, 0, sizeof(BtHeader)); // C4701 potentially uninitialized local variable 'header' used
bool validData = true;
validData = validData && (fread(&header, sizeof(BtHeader), 1, file) != 0);

@ -419,7 +419,7 @@ static inline bool MatchesWildcardsIgnoreCaseExt_Tpl(const TS& str, const TS& wi
const typename TS::value_type* savedStrBegin = 0;
const typename TS::value_type* savedStrEnd = 0;
const typename TS::value_type* savedWild = 0;
size_t savedWildCount;
size_t savedWildCount = 0;
const typename TS::value_type* pStr = str.c_str();
const typename TS::value_type* pWild = wildcards.c_str();

@ -179,11 +179,11 @@ ComponentEntityEditorPlugin::ComponentEntityEditorPlugin([[maybe_unused]] IEdito
LyViewPane::EntityOutliner,
LyViewPane::CategoryTools,
outlinerOptions);
}
AzToolsFramework::ViewPaneOptions options;
options.preferedDockingArea = Qt::NoDockWidgetArea;
RegisterViewPane<SliceRelationshipWidget>(LyViewPane::SliceRelationships, LyViewPane::CategoryTools, options);
AzToolsFramework::ViewPaneOptions options;
options.preferedDockingArea = Qt::NoDockWidgetArea;
RegisterViewPane<SliceRelationshipWidget>(LyViewPane::SliceRelationships, LyViewPane::CategoryTools, options);
}
RegisterModuleResourceSelectors(GetIEditor()->GetResourceSelectorHost());

@ -1732,13 +1732,14 @@ void SandboxIntegrationManager::GoToEntitiesInViewports(const AzToolsFramework::
// compute new camera transform
const float fov = AzFramework::RetrieveFov(viewportContext->GetCameraProjectionMatrix());
const float fovScale = (1.0f / AZStd::tan(fov * 0.5f));
const float distanceToTarget = selectionSize * fovScale * centerScale;
const float distanceToLookAt = selectionSize * fovScale * centerScale;
const AZ::Transform nextCameraTransform =
AZ::Transform::CreateLookAt(aabb.GetCenter() - (forward * distanceToTarget), aabb.GetCenter());
AZ::Transform::CreateLookAt(aabb.GetCenter() - (forward * distanceToLookAt), aabb.GetCenter());
AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event(
viewportContext->GetId(),
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, nextCameraTransform);
&AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, nextCameraTransform,
distanceToLookAt);
}
}
}

@ -48,7 +48,8 @@ class ConfigurationManager(object):
def configuration(self, new_configuration: ConfigurationManager) -> None:
self._configuration = new_configuration
def setup(self, config_path: str) -> None:
def setup(self, config_path: str) -> bool:
result: bool = True
logger.info("Setting up default configuration ...")
try:
normalized_config_path: str = file_utils.normalize_file_path(config_path);
@ -63,5 +64,7 @@ class ConfigurationManager(object):
self._configuration.account_id = aws_utils.get_default_account_id()
self._configuration.region = aws_utils.get_default_region()
except (RuntimeError, FileNotFoundError) as e:
logger.exception(e)
logger.error(e)
result = False
logger.debug(self._configuration)
return result

@ -74,11 +74,18 @@ if __name__ == "__main__":
logger.warning("Failed to load style sheet for resource mapping tool")
logger.info("Initializing boto3 default session ...")
aws_utils.setup_default_session(arguments.profile)
try:
aws_utils.setup_default_session(arguments.profile)
except RuntimeError as error:
logger.error(error)
environment_utils.cleanup_qt_environment()
exit(-1)
logger.info("Initializing configuration manager ...")
configuration_manager: ConfigurationManager = ConfigurationManager()
configuration_manager.setup(arguments.config_path)
if not configuration_manager.setup(arguments.config_path):
environment_utils.cleanup_qt_environment()
exit(-1)
logger.info("Initializing thread manager ...")
thread_manager: ThreadManager = ThreadManager()

@ -12,10 +12,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import boto3
from botocore.paginate import (PageIterator, Paginator)
from botocore.client import BaseClient
from botocore.exceptions import ClientError
from botocore.exceptions import (ClientError, ConfigNotFound, NoCredentialsError, ProfileNotFound)
from typing import Dict, List
from model import (constants, error_messages)
from model import error_messages
from model.basic_resource_attributes import (BasicResourceAttributes, BasicResourceAttributesBuilder)
"""
@ -65,8 +65,11 @@ def _initialize_boto3_aws_client(service: str, region: str = "") -> BaseClient:
def setup_default_session(profile: str) -> None:
global default_session
default_session = boto3.session.Session(profile_name=profile)
try:
global default_session
default_session = boto3.session.Session(profile_name=profile)
except (ConfigNotFound, ProfileNotFound) as error:
raise RuntimeError(error)
def get_default_account_id() -> str:
@ -76,6 +79,8 @@ def get_default_account_id() -> str:
except ClientError as error:
raise RuntimeError(error_messages.AWS_SERVICE_REQUEST_CLIENT_ERROR_MESSAGE.format(
"get_caller_identity", error.response['Error']['Code'], error.response['Error']['Message']))
except NoCredentialsError as error:
raise RuntimeError(error)
def get_default_region() -> str:

@ -88,7 +88,7 @@ namespace ImageProcessingAtom
int dstPosition;
signed short int n;
bool trimZeros = true, stillzero;
int lastnonzero, hWeight, highest;
int lastnonzero = 0, hWeight, highest = 0;
signed int sumiWeights, iWeight;
signed short int* weightsPtr;
signed short int* weightsMem;

@ -1106,7 +1106,7 @@ namespace ImageProcessingAtom
//fractional amount to apply change in tap intensity along edge to taps
// in a perpendicular direction to edge
CP_ITYPE fixupFrac = (CP_ITYPE)(fixupDist - iFixup) / (CP_ITYPE)(fixupDist);
CP_ITYPE fixupWeight;
CP_ITYPE fixupWeight = 0.0f;
switch(a_FixupType )
{

@ -37,7 +37,7 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
}
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
#include <Atom/Features/PBR/TransparentPassSrg.azsli>
#include <Atom/Features/PBR/ForwardPassSrg.azsli>
#include <Atom/Features/Shadow/DirectionalLightShadow.azsli>
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>

@ -124,7 +124,7 @@
"DrawListSortType": "KeyThenReverseDepth",
"PipelineViewTag": "MainCamera",
"PassSrgAsset": {
"FilePath": "shaderlib/atom/features/pbr/transparentpasssrg.azsli:PassSrg"
"FilePath": "shaderlib/atom/features/pbr/forwardpasssrg.azsli:PassSrg"
}
}
}

@ -35,4 +35,5 @@ ShaderResourceGroup PassSrg : SRG_PerPass
Texture2D<uint4> m_tileLightData;
StructuredBuffer<uint> m_lightListRemapped;
Texture2D<float> m_linearDepthTexture;
}

@ -1,39 +0,0 @@
/*
* 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
#include <Atom/Features/SrgSemantics.azsli>
ShaderResourceGroup PassSrg : SRG_PerPass
{
// [GFX TODO][ATOM-2012] adapt to multiple shadowmaps
Texture2DArray<float> m_directionalLightShadowmap;
Texture2DArray<float> m_directionalLightExponentialShadowmap;
Texture2DArray<float> m_projectedShadowmaps;
Texture2DArray<float> m_projectedExponentialShadowmap;
Texture2D m_brdfMap;
Sampler LinearSampler
{
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
AddressU = Clamp;
AddressV = Clamp;
AddressW = Clamp;
};
Texture2D<uint4> m_tileLightData;
StructuredBuffer<uint> m_lightListRemapped;
Texture2D<float> m_linearDepthTexture;
}

@ -246,7 +246,6 @@ set(FILES
ShaderLib/Atom/Features/PBR/Hammersley.azsli
ShaderLib/Atom/Features/PBR/LightingOptions.azsli
ShaderLib/Atom/Features/PBR/LightingUtils.azsli
ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli
ShaderLib/Atom/Features/PBR/Lighting/DualSpecularLighting.azsli
ShaderLib/Atom/Features/PBR/Lighting/EnhancedLighting.azsli
ShaderLib/Atom/Features/PBR/Lighting/LightingData.azsli

@ -46,8 +46,6 @@ namespace AZ
uint16_t m_padding; // Explicit padding.
};
static constexpr size_t size = sizeof(DiskLightData);
//! DiskLightFeatureProcessorInterface provides an interface to acquire, release, and update a disk light. This is necessary for code outside of
//! the Atom features gem to communicate with the DiskLightFeatureProcessor.
class DiskLightFeatureProcessorInterface

@ -484,6 +484,7 @@ namespace AZ
}
D3D12_RESOURCE_TRANSITION_BARRIER transition;
memset(&transition, 0, sizeof(D3D12_RESOURCE_TRANSITION_BARRIER)); // C4701 potentially unitialized local variable 'transition' used
transition.pResource = image.GetMemoryView().GetMemory();
Scope& firstScope = static_cast<Scope&>(scopeAttachment->GetScope());

@ -45,7 +45,7 @@ namespace AZ
Fence* fenceToSignal)
{
AZStd::vector<VkCommandBuffer> vkCommandBuffers;
AZStd::vector<VkSemaphore> vkWaitSemaphores;
AZStd::vector<VkSemaphore> vkWaitSemaphoreVector; // vulkan.h has a #define called vkWaitSemaphores, so we name this differently
AZStd::vector<VkPipelineStageFlags> vkWaitPipelineStages;
AZStd::vector<VkSemaphore> vkSignalSemaphores;
VkSubmitInfo submitInfo;
@ -65,11 +65,11 @@ namespace AZ
return item->GetNativeSemaphore();
});
vkWaitPipelineStages.reserve(waitSemaphoresInfo.size());
vkWaitSemaphores.reserve(waitSemaphoresInfo.size());
vkWaitSemaphoreVector.reserve(waitSemaphoresInfo.size());
AZStd::for_each(waitSemaphoresInfo.begin(), waitSemaphoresInfo.end(), [&](auto& item)
{
vkWaitPipelineStages.push_back(item.first);
vkWaitSemaphores.push_back(item.second->GetNativeSemaphore());
vkWaitSemaphoreVector.push_back(item.second->GetNativeSemaphore());
// Wait until the wait semaphores has been submitted for signaling.
item.second->WaitEvent();
});
@ -77,8 +77,8 @@ namespace AZ
submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.pNext = nullptr;
submitInfo.waitSemaphoreCount = static_cast<uint32_t>(vkWaitSemaphores.size());
submitInfo.pWaitSemaphores = vkWaitSemaphores.empty() ? nullptr : vkWaitSemaphores.data();
submitInfo.waitSemaphoreCount = static_cast<uint32_t>(vkWaitSemaphoreVector.size());
submitInfo.pWaitSemaphores = vkWaitSemaphoreVector.empty() ? nullptr : vkWaitSemaphoreVector.data();
submitInfo.pWaitDstStageMask = vkWaitPipelineStages.empty() ? nullptr : vkWaitPipelineStages.data();
submitInfo.commandBufferCount = static_cast<uint32_t>(vkCommandBuffers.size());
submitInfo.pCommandBuffers = vkCommandBuffers.empty() ? nullptr : vkCommandBuffers.data();

@ -244,16 +244,26 @@ namespace AZ
void CullingScene::RegisterOrUpdateCullable(Cullable& cullable)
{
m_cullDataConcurrencyCheck.soft_lock();
// Multiple threads can call RegisterOrUpdateCullable at the same time
// since the underlying visScene is thread safe, but if you're inserting or
// updating between BeginCulling and EndCulling, you'll get non-deterministic
// results depending on a race condition if you happen to update before or after
// the culling system starts Enumerating, so use soft_lock_shared here
m_cullDataConcurrencyCheck.soft_lock_shared();
m_visScene->InsertOrUpdateEntry(cullable.m_cullData.m_visibilityEntry);
m_cullDataConcurrencyCheck.soft_unlock();
m_cullDataConcurrencyCheck.soft_unlock_shared();
}
void CullingScene::UnregisterCullable(Cullable& cullable)
{
m_cullDataConcurrencyCheck.soft_lock();
// Multiple threads can call RegisterOrUpdateCullable at the same time
// since the underlying visScene is thread safe, but if you're inserting or
// updating between BeginCulling and EndCulling, you'll get non-deterministic
// results depending on a race condition if you happen to update before or after
// the culling system starts Enumerating, so use soft_lock_shared here
m_cullDataConcurrencyCheck.soft_lock_shared();
m_visScene->RemoveEntry(cullable.m_cullData.m_visibilityEntry);
m_cullDataConcurrencyCheck.soft_unlock();
m_cullDataConcurrencyCheck.soft_unlock_shared();
}
uint32_t CullingScene::GetNumCullables() const

@ -10,6 +10,7 @@
*
*/
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
#include <Atom/RPI.Reflect/Shader/ShaderCommonTypes.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Serialization/SerializeContext.h>
@ -34,10 +35,6 @@ namespace AZ
uint32_t ShaderAsset::MakeAssetProductSubId(uint32_t rhiApiUniqueIndex, uint32_t subProductType)
{
static constexpr uint32_t RhiIndexBitPosition = 30;
static constexpr uint32_t RhiIndexNumBits = 32 - RhiIndexBitPosition;
static constexpr uint32_t RhiIndexMaxValue = (1 << RhiIndexNumBits) - 1;
static constexpr uint32_t SubProductTypeBitPosition = 0;
static constexpr uint32_t SubProductTypeNumBits = RhiIndexBitPosition - SubProductTypeBitPosition;
static constexpr uint32_t SubProductTypeMaxValue = (1 << SubProductTypeNumBits) - 1;

@ -10,6 +10,7 @@
*
*/
#include <Atom/RPI.Reflect/Shader/ShaderVariantAsset.h>
#include <Atom/RPI.Reflect/Shader/ShaderCommonTypes.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Serialization/SerializeContext.h>
@ -24,10 +25,6 @@ namespace AZ
uint32_t ShaderVariantAsset::MakeAssetProductSubId(
uint32_t rhiApiUniqueIndex, ShaderVariantStableId variantStableId, uint32_t subProductType)
{
static constexpr uint32_t RhiIndexBitPosition = 30;
static constexpr uint32_t RhiIndexNumBits = 32 - RhiIndexBitPosition;
static constexpr uint32_t RhiIndexMaxValue = (1 << RhiIndexNumBits) - 1;
static constexpr uint32_t SubProductTypeBitPosition = 17;
static constexpr uint32_t SubProductTypeNumBits = RhiIndexBitPosition - SubProductTypeBitPosition;
static constexpr uint32_t SubProductTypeMaxValue = (1 << SubProductTypeNumBits) - 1;

@ -51,7 +51,8 @@ namespace AtomToolsFramework
void UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event) override;
// ModularViewportCameraControllerRequestBus overrides ...
void InterpolateToTransform(const AZ::Transform& worldFromLocal) override;
void InterpolateToTransform(const AZ::Transform& worldFromLocal, float lookAtDistance) override;
AZStd::optional<AZ::Vector3> LookAtAfterInterpolation() const override;
private:
// AzFramework::ViewportDebugDisplayEventBus overrides ...
@ -71,6 +72,8 @@ namespace AtomToolsFramework
AZ::Transform m_transformEnd = AZ::Transform::CreateIdentity();
float m_animationT = 0.0f;
CameraMode m_cameraMode = CameraMode::Control;
AZStd::optional<AZ::Vector3> m_lookAtAfterInterpolation; //!< The look at point after an interpolation has finished.
//!< Will be cleared when the view changes (camera looks away).
bool m_updatingTransform = false;
AZ::RPI::ViewportContext::MatrixChangedEvent::Handler m_cameraViewMatrixChangeHandler;

@ -32,7 +32,12 @@ namespace AtomToolsFramework
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
//! Begin a smooth transition of the camera to the requested transform.
virtual void InterpolateToTransform(const AZ::Transform& worldFromLocal) = 0;
//! @param worldFromLocal The transform of where the camera should end up.
//! @param lookAtDistance The distance between the camera transform and the imagined look at point.
virtual void InterpolateToTransform(const AZ::Transform& worldFromLocal, float lookAtDistance) = 0;
//! Look at point after an interpolation has finished and no translation has occurred.
virtual AZStd::optional<AZ::Vector3> LookAtAfterInterpolation() const = 0;
protected:
~ModularViewportCameraControllerRequests() = default;

@ -140,6 +140,18 @@ namespace AtomToolsFramework
m_targetCamera = m_cameraSystem.StepCamera(m_targetCamera, event.m_deltaTime.count());
m_camera = AzFramework::SmoothCamera(m_camera, m_targetCamera, event.m_deltaTime.count());
// if there has been an interpolation, only clear the look at point if it is no longer
// centered in the view (the camera has looked away from it)
if (m_lookAtAfterInterpolation.has_value())
{
if (const float lookDirection =
(*m_lookAtAfterInterpolation - m_camera.Translation()).GetNormalized().Dot(m_camera.Transform().GetBasisY());
!AZ::IsCloseMag(lookDirection, 1.0f, 0.001f))
{
m_lookAtAfterInterpolation = {};
}
}
viewportContext->SetCameraTransform(m_camera.Transform());
}
else if (m_cameraMode == CameraMode::Animation)
@ -148,8 +160,8 @@ namespace AtomToolsFramework
{
return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
};
const float transitionT = smootherStepFn(m_animationT);
const float transitionT = smootherStepFn(m_animationT);
const AZ::Transform current = AZ::Transform::CreateFromQuaternionAndTranslation(
m_transformStart.GetRotation().Slerp(m_transformEnd.GetRotation(), transitionT),
m_transformStart.GetTranslation().Lerp(m_transformEnd.GetTranslation(), transitionT));
@ -185,11 +197,17 @@ namespace AtomToolsFramework
}
}
void ModernViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal)
void ModernViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal, const float lookAtDistance)
{
m_animationT = 0.0f;
m_cameraMode = CameraMode::Animation;
m_transformStart = m_camera.Transform();
m_transformEnd = worldFromLocal;
m_lookAtAfterInterpolation = m_transformEnd.GetTranslation() + m_transformEnd.GetBasisY() * lookAtDistance;
}
AZStd::optional<AZ::Vector3> ModernViewportCameraControllerInstance::LookAtAfterInterpolation() const
{
return m_lookAtAfterInterpolation;
}
} // namespace AtomToolsFramework

@ -0,0 +1,37 @@
/*
* 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
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
//! ThumbnailFeatureProcessorProviderRequests allows registering custom Feature Processors for thumbnail generation
//! Duplicates will be ignored
//! You can check minimal feature processors that are already registered in CommonThumbnailRenderer.cpp
class ThumbnailFeatureProcessorProviderRequests
: public AZ::EBusTraits
{
public:
//! Get a list of custom feature processors to register with thumbnail renderer
virtual const AZStd::vector<AZStd::string>& GetCustomFeatureProcessors() const = 0;
};
using ThumbnailFeatureProcessorProviderBus = AZ::EBus<ThumbnailFeatureProcessorProviderRequests>;
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ

@ -34,12 +34,34 @@ namespace AZ
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::MaterialAsset::RTTI_Type());
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::ModelAsset::RTTI_Type());
SystemTickBus::Handler::BusConnect();
ThumbnailFeatureProcessorProviderBus::Handler::BusConnect();
m_steps[Step::Initialize] = AZStd::make_shared<InitializeStep>(this);
m_steps[Step::FindThumbnailToRender] = AZStd::make_shared<FindThumbnailToRenderStep>(this);
m_steps[Step::WaitForAssetsToLoad] = AZStd::make_shared<WaitForAssetsToLoadStep>(this);
m_steps[Step::Capture] = AZStd::make_shared<CaptureStep>(this);
m_steps[Step::ReleaseResources] = AZStd::make_shared<ReleaseResourcesStep>(this);
m_minimalFeatureProcessors =
{
"AZ::Render::TransformServiceFeatureProcessor",
"AZ::Render::MeshFeatureProcessor",
"AZ::Render::SimplePointLightFeatureProcessor",
"AZ::Render::SimpleSpotLightFeatureProcessor",
"AZ::Render::PointLightFeatureProcessor",
// There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow
// flickering [ATOM-13568]
// as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now.
// Possibly re-enable with [GFX TODO][ATOM-13639]
// "AZ::Render::DirectionalLightFeatureProcessor",
"AZ::Render::DiskLightFeatureProcessor",
"AZ::Render::CapsuleLightFeatureProcessor",
"AZ::Render::QuadLightFeatureProcessor",
"AZ::Render::DecalTextureArrayFeatureProcessor",
"AZ::Render::ImageBasedLightFeatureProcessor",
"AZ::Render::PostProcessFeatureProcessor",
"AZ::Render::SkyBoxFeatureProcessor"
};
}
CommonThumbnailRenderer::~CommonThumbnailRenderer()
@ -50,6 +72,7 @@ namespace AZ
}
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusDisconnect();
SystemTickBus::Handler::BusDisconnect();
ThumbnailFeatureProcessorProviderBus::Handler::BusDisconnect();
}
void CommonThumbnailRenderer::SetStep(Step step)
@ -77,6 +100,11 @@ namespace AZ
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::ExecuteQueuedEvents();
}
const AZStd::vector<AZStd::string>& CommonThumbnailRenderer::GetCustomFeatureProcessors() const
{
return m_minimalFeatureProcessors;
}
AZStd::shared_ptr<ThumbnailRendererData> CommonThumbnailRenderer::GetData() const
{
return m_data;

@ -17,6 +17,8 @@
#include <Thumbnails/Rendering/ThumbnailRendererContext.h>
#include <Thumbnails/Rendering/ThumbnailRendererData.h>
#include <AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h>
// Disables warning messages triggered by the Qt library
// 4251: class needs to have dll-interface to be used by clients of class
// 4800: forcing value to bool 'true' or 'false' (performance warning)
@ -34,9 +36,10 @@ namespace AZ
//! Provides custom rendering of material and model thumbnails
class CommonThumbnailRenderer
: private AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler
: public ThumbnailRendererContext
, private AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler
, private SystemTickBus::Handler
, public ThumbnailRendererContext
, private ThumbnailFeatureProcessorProviderBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(CommonThumbnailRenderer, AZ::SystemAllocator, 0)
@ -57,9 +60,13 @@ namespace AZ
//! SystemTickBus::Handler interface overrides...
void OnSystemTick() override;
//! Render::ThumbnailFeatureProcessorProviderBus::Handler interface overrides...
const AZStd::vector<AZStd::string>& GetCustomFeatureProcessors() const override;
AZStd::unordered_map<Step, AZStd::shared_ptr<ThumbnailRendererStep>> m_steps;
Step m_currentStep = Step::None;
AZStd::shared_ptr<ThumbnailRendererData> m_data;
AZStd::vector<AZStd::string> m_minimalFeatureProcessors;
};
} // namespace Thumbnails
} // namespace LyIntegration

@ -11,10 +11,16 @@
*/
#include <AzCore/Math/MatrixUtils.h>
#include <AzCore/EBus/Results.h>
#include <AzFramework/Components/TransformComponent.h>
#include <Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h>
#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
#include <Atom/Feature/Utils/LightingPreset.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/View.h>
@ -23,10 +29,11 @@
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Atom/RPI.Reflect/System/RenderPipelineDescriptor.h>
#include <Atom/RPI.Reflect/System/SceneDescriptor.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
#include <AzCore/Math/MatrixUtils.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h>
#include <Thumbnails/Rendering/ThumbnailRendererData.h>
#include <Thumbnails/Rendering/ThumbnailRendererContext.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.h>
@ -37,7 +44,6 @@ namespace AZ
{
namespace Thumbnails
{
InitializeStep::InitializeStep(ThumbnailRendererContext* context)
: ThumbnailRendererStep(context)
{
@ -50,24 +56,23 @@ namespace AZ
data->m_entityContext = AZStd::make_unique<AzFramework::EntityContext>();
data->m_entityContext->InitContext();
// Create and register a scene with minimum required feature processors
// Create and register a scene with all required feature processors
RPI::SceneDescriptor sceneDesc;
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::TransformServiceFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::MeshFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SimplePointLightFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SimpleSpotLightFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::PointLightFeatureProcessor");
// There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow flickering [ATOM-13568]
// as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now.
// Possibly re-enable with [GFX TODO][ATOM-13639]
// sceneDesc.m_featureProcessorNames.push_back("AZ::Render::DirectionalLightFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::DiskLightFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::CapsuleLightFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::QuadLightFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::DecalTextureArrayFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::ImageBasedLightFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::PostProcessFeatureProcessor");
sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SkyBoxFeatureProcessor");
AZ::EBusAggregateResults<AZStd::vector<AZStd::string>> results;
ThumbnailFeatureProcessorProviderBus::BroadcastResult(results, &ThumbnailFeatureProcessorProviderBus::Handler::GetCustomFeatureProcessors);
AZStd::set<AZStd::string> featureProcessorNames;
for (auto& resultCollection : results.values)
{
for (auto& featureProcessorName : resultCollection)
{
if (featureProcessorNames.emplace(featureProcessorName).second)
{
sceneDesc.m_featureProcessorNames.push_back(featureProcessorName);
}
}
}
data->m_scene = RPI::Scene::CreateScene(sceneDesc);

@ -10,11 +10,12 @@
#
set(FILES
Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h
Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h
Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h
Source/Module.cpp
Source/Animation/EditorAttachmentComponent.h
Source/Animation/EditorAttachmentComponent.cpp
Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h
Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h
Source/EditorCommonFeaturesSystemComponent.h
Source/EditorCommonFeaturesSystemComponent.cpp
Source/CoreLights/EditorAreaLightComponent.h

@ -9,6 +9,10 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(NOT PAL_TRAIT_BUILD_HOST_TOOLS)
return()
endif()
ly_add_target(
NAME DccScriptingInterface.Static STATIC
NAMESPACE Gem
@ -38,3 +42,9 @@ ly_add_target(
PRIVATE
Gem::DccScriptingInterface.Static
)
# Any 'tool' type applications should use Gem::DccScriptingInterface.Editor:
ly_create_alias(NAME DccScriptingInterface.Tools NAMESPACE Gem TARGETS Gem::DccScriptingInterface.Editor)
# Add an empty 'builders' alias to allow the DccScriptInterface root gem path to be added to the generated
# cmake_dependencies.<project>.assetprocessor.setreg to allow the asset scan folder for it to be added
ly_create_alias(NAME DccScriptingInterface.Builders NAMESPACE Gem)

@ -165,8 +165,8 @@ namespace EMotionFX
const AZ::Outcome<size_t> boolYParamIndexOutcome = m_animGraphInstance->FindParameterIndex(nameBoolY);
success = boolXParamIndexOutcome.IsSuccess() && boolYParamIndexOutcome.IsSuccess();
uint32 boolXOutputPortIndex;
uint32 boolYOutputPortIndex;
uint32 boolXOutputPortIndex = InvalidIndex32;
uint32 boolYOutputPortIndex = InvalidIndex32;
const int portIndicesTosetCount = 2;
int portIndicesFound = 0;
const AZStd::vector<EMotionFX::AnimGraphNode::Port>& parameterNodeOutputPorts = parameterNode->GetOutputPorts();

@ -612,7 +612,9 @@ FN_DECIMAL FastNoise::SingleValue(unsigned char offset, FN_DECIMAL x, FN_DECIMAL
int y1 = y0 + 1;
int z1 = z0 + 1;
FN_DECIMAL xs, ys, zs;
FN_DECIMAL xs = 0.0f;
FN_DECIMAL ys = 0.0f;
FN_DECIMAL zs = 0.0f;
switch (m_interp)
{
case Linear:
@ -726,7 +728,8 @@ FN_DECIMAL FastNoise::SingleValue(unsigned char offset, FN_DECIMAL x, FN_DECIMAL
int x1 = x0 + 1;
int y1 = y0 + 1;
FN_DECIMAL xs, ys;
FN_DECIMAL xs = 0.0f;
FN_DECIMAL ys = 0.0f;
switch (m_interp)
{
case Linear:
@ -840,7 +843,9 @@ FN_DECIMAL FastNoise::SinglePerlin(unsigned char offset, FN_DECIMAL x, FN_DECIMA
int y1 = y0 + 1;
int z1 = z0 + 1;
FN_DECIMAL xs, ys, zs;
FN_DECIMAL xs = 0.0f;
FN_DECIMAL ys = 0.0f;
FN_DECIMAL zs = 0.0f;
switch (m_interp)
{
case Linear:
@ -962,7 +967,8 @@ FN_DECIMAL FastNoise::SinglePerlin(unsigned char offset, FN_DECIMAL x, FN_DECIMA
int x1 = x0 + 1;
int y1 = y0 + 1;
FN_DECIMAL xs, ys;
FN_DECIMAL xs = 0.0f;
FN_DECIMAL ys = 0.0f;
switch (m_interp)
{
case Linear:
@ -1699,7 +1705,9 @@ FN_DECIMAL FastNoise::SingleCellular(FN_DECIMAL x, FN_DECIMAL y, FN_DECIMAL z) c
int zr = FastRound(z);
FN_DECIMAL distance = 999999;
int xc, yc, zc;
int xc = 0;
int yc = 0;
int zc = 0;
switch (m_cellularDistanceFunction)
{
@ -1923,7 +1931,8 @@ FN_DECIMAL FastNoise::SingleCellular(FN_DECIMAL x, FN_DECIMAL y) const
int yr = FastRound(y);
FN_DECIMAL distance = 999999;
int xc, yc;
int xc = 0;
int yc = 0;
switch (m_cellularDistanceFunction)
{

@ -1072,8 +1072,7 @@ namespace GraphCanvas
if (!id.empty())
{
Selector selector = Selector::Get(id);
result.emplace_back(selector);
result.emplace_back(Selector::Get(id));
continue;
}
@ -1111,8 +1110,7 @@ namespace GraphCanvas
{
bits.emplace_back(stateSelector);
}
Selector selector = aznew CompoundSelector(std::move(bits));
nestedSelectors.emplace_back(selector);
nestedSelectors.emplace_back(aznew CompoundSelector(std::move(bits)));
}
}

@ -1239,7 +1239,7 @@ namespace GraphCanvas
bool GraphUtils::IsValidModelConnection(const GraphId& graphId, const Endpoint& sourceEndpoint, const Endpoint& targetEndpoint)
{
bool validConnection;
bool validConnection = false;
AZStd::unordered_set< Endpoint > finalSourceEndpoints = RemapEndpointForModel(sourceEndpoint);
AZStd::unordered_set< Endpoint > finalTargetEndpoints = RemapEndpointForModel(targetEndpoint);

@ -23,10 +23,6 @@
#include <QPainter>
#include <QStaticText>
static const QColor timeMarkerCol = QColor(255, 0, 255);
static const QColor textCol = QColor(0, 0, 0);
static const QColor ltgrayCol = QColor(110, 110, 110);
QColor InterpolateColor(const QColor& c1, const QColor& c2, float fraction)
{
const int r = (c2.red() - c1.red()) * fraction + c1.red();

@ -48,7 +48,6 @@ static const EUiAnimCurveType DEFAULT_TRACK_TYPE = eUiAnimCurveType_BezierFloat;
// Old serialization values that are no longer
// defined in IUiAnimationSystem.h, but needed for conversion:
static const int OLD_APARAM_USER = 100;
static const int OLD_ACURVE_GOTO = 21;
static const int OLD_APARAM_PARTICLE_COUNT_SCALE = 95;
static const int OLD_APARAM_PARTICLE_PULSE_PERIOD = 96;

@ -691,7 +691,7 @@ IUiAnimTrack* CUiAnimAzEntityNode::CreateTrackForAzField(const UiAnimParamData&
return nullptr;
}
EUiAnimValue valueType;
EUiAnimValue valueType = eUiAnimValue_Unknown;
switch (numElements)
{
case 2:

@ -375,7 +375,7 @@ namespace LyShine
}
///////////////////////////////////////////////////////////////////////////////////////////////
void LyShineSystemComponent::OnCrySystemInitialized(ISystem& system, [[maybe_unused]] const SSystemInitParams& startupParams)
void LyShineSystemComponent::OnCrySystemInitialized([[maybe_unused]] ISystem& system, [[maybe_unused]] const SSystemInitParams& startupParams)
{
#if !defined(AZ_MONOLITHIC_BUILD)
// When module is linked dynamically, we must set our gEnv pointer.

@ -107,7 +107,8 @@ void UiLayoutGridComponent::ApplyLayoutHeight()
AZStd::vector<AZ::EntityId> childEntityIds;
EBUS_EVENT_ID_RESULT(childEntityIds, GetEntityId(), UiElementBus, GetChildEntityIds);
int childIndex = 0;
int columnIndex, rowIndex;
int columnIndex = 0;
int rowIndex = 0;
for (auto child : childEntityIds)
{
// Set the anchors
@ -627,7 +628,8 @@ AZ::Vector2 UiLayoutGridComponent::GetChildrenBoundingRectSize(const AZ::Vector2
UiLayoutHelpers::GetSizeInsidePadding(GetEntityId(), m_padding, layoutRectSize);
// Calculate number of rows and columns
int numColumns, numRows;
int numColumns = 0;
int numRows = 0;
switch (m_startingDirection)
{
case StartingDirection::HorizontalOrder:

@ -151,6 +151,8 @@ namespace UiNavigationHelpers
}
UiTransformInterface::Rect parentRect;
parentRect.Set(0.0f, 0.0f, 0.0f, 0.0f);
AZ::Matrix4x4 parentTransformFromViewport;
if (parentElement.IsValid() && !isCurElementDescendantOfParentElement)
{

@ -60,7 +60,6 @@ static const EAnimCurveType DEFAULT_TRACK_TYPE = eAnimCurveType_BezierFloat;
// Old serialization values that are no longer
// defined in IMovieSystem.h, but needed for conversion:
static const int OLD_APARAM_USER = 100;
static const int OLD_ACURVE_GOTO = 21;
static const int OLD_APARAM_PARTICLE_COUNT_SCALE = 95;
static const int OLD_APARAM_PARTICLE_PULSE_PERIOD = 96;

@ -281,7 +281,6 @@ void CAnimPostFXNode::SerializeAnims(XmlNodeRef& xmlNode, bool bLoading, bool bL
paramType.Serialize(trackNode, true);
// Don't use APARAM_USER because it could change in newer versions
// CAnimNode::SerializeAnims will then take care of that
static const unsigned int OLD_APARAM_USER = 100;
paramType = static_cast<AnimParamType>(static_cast<int>(paramType.GetType()) + OLD_APARAM_USER);
paramType.Serialize(trackNode, false);
}

@ -51,11 +51,11 @@ namespace Audio
// To avoid errors, we initialize COM here with the same model.
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const CLSID CLSID_MMDeviceEnumerator_UUID = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator_UUID = __uuidof(IMMDeviceEnumerator);
HRESULT hresult = CoCreateInstance(
CLSID_MMDeviceEnumerator, nullptr,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
CLSID_MMDeviceEnumerator_UUID, nullptr,
CLSCTX_ALL, IID_IMMDeviceEnumerator_UUID,
reinterpret_cast<void**>(&m_enumerator)
);
@ -133,8 +133,8 @@ namespace Audio
AZ_Assert(m_device != nullptr, "Attempting to start a Microphone session while the device is uninitialized - Windows!\n");
// Get the IAudioClient from the device
const IID IID_IAudioClient = __uuidof(IAudioClient);
HRESULT hresult = m_device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, reinterpret_cast<void**>(&m_audioClient));
const IID IID_IAudioClient_UUID = __uuidof(IAudioClient);
HRESULT hresult = m_device->Activate(IID_IAudioClient_UUID, CLSCTX_ALL, nullptr, reinterpret_cast<void**>(&m_audioClient));
if (FAILED(hresult))
{
@ -182,8 +182,8 @@ namespace Audio
}
// Get the IAudioCaptureClient
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
hresult = m_audioClient->GetService(IID_IAudioCaptureClient, reinterpret_cast<void**>(&m_audioCaptureClient));
const IID IID_IAudioCaptureClient_UUID = __uuidof(IAudioCaptureClient);
hresult = m_audioClient->GetService(IID_IAudioCaptureClient_UUID, reinterpret_cast<void**>(&m_audioCaptureClient));
if (FAILED(hresult))
{

@ -39,6 +39,7 @@ namespace Multiplayer
using EntityMigrationStartEvent = AZ::Event<ClientInputId>;
using EntityMigrationEndEvent = AZ::Event<>;
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, HostId, AzNetworking::ConnectionId>;
using EntityPreRenderEvent = AZ::Event<float, float>;
//! @class NetBindComponent
//! @brief Component that provides net-binding to a networked entity.
@ -97,6 +98,7 @@ namespace Multiplayer
void NotifyMigrationStart(ClientInputId migratedInputId);
void NotifyMigrationEnd();
void NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId);
void NotifyPreRender(float deltaTime, float blendFactor);
void AddEntityStopEventHandler(EntityStopEvent::Handler& eventHandler);
void AddEntityDirtiedEventHandler(EntityDirtiedEvent::Handler& eventHandler);
@ -104,6 +106,7 @@ namespace Multiplayer
void AddEntityMigrationStartEventHandler(EntityMigrationStartEvent::Handler& eventHandler);
void AddEntityMigrationEndEventHandler(EntityMigrationEndEvent::Handler& eventHandler);
void AddEntityServerMigrationEventHandler(EntityServerMigrationEvent::Handler& eventHandler);
void AddEntityPreRenderEventHandler(EntityPreRenderEvent::Handler& eventHandler);
bool SerializeEntityCorrection(AzNetworking::ISerializer& serializer);
@ -152,6 +155,7 @@ namespace Multiplayer
EntityMigrationStartEvent m_entityMigrationStartEvent;
EntityMigrationEndEvent m_entityMigrationEndEvent;
EntityServerMigrationEvent m_entityServerMigrationEvent;
EntityPreRenderEvent m_entityPreRenderEvent;
AZ::Event<> m_onRemove;
RpcSendEvent::Handler m_handleLocalServerRpcMessageEventHandle;
AZ::Event<>::Handler m_handleMarkedDirty;

@ -13,6 +13,7 @@
#pragma once
#include <Source/AutoGen/NetworkTransformComponent.AutoComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <AzCore/Component/TransformBus.h>
namespace Multiplayer
@ -32,13 +33,22 @@ namespace Multiplayer
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
private:
void OnPreRender(float deltaTime, float blendFactor);
void OnRotationChangedEvent(const AZ::Quaternion& rotation);
void OnTranslationChangedEvent(const AZ::Vector3& translation);
void OnScaleChangedEvent(float scale);
void OnResetCountChangedEvent();
AZ::Transform m_previousTransform = AZ::Transform::CreateIdentity();
AZ::Transform m_targetTransform = AZ::Transform::CreateIdentity();
AZ::Event<AZ::Quaternion>::Handler m_rotationEventHandler;
AZ::Event<AZ::Vector3>::Handler m_translationEventHandler;
AZ::Event<float>::Handler m_scaleEventHandler;
AZ::Event<uint8_t>::Handler m_resetCountEventHandler;
EntityPreRenderEvent::Handler m_entityPreRenderEventHandler;
};
class NetworkTransformComponentController

@ -45,10 +45,10 @@ namespace Multiplayer
static constexpr uint32_t MaxRecordBits = 2048;
ReplicationRecord() = default;
ReplicationRecord(NetEntityRole netEntityRole);
ReplicationRecord(NetEntityRole remoteNetEntityRole);
void SetNetworkRole(NetEntityRole netEntityRole);
NetEntityRole GetNetworkRole() const;
void SetRemoteNetworkRole(NetEntityRole remoteNetEntityRole);
NetEntityRole GetRemoteNetworkRole() const;
bool AreAllBitsConsumed() const;
void ResetConsumedBits();
@ -92,6 +92,6 @@ namespace Multiplayer
// Sequence number this ReplicationRecord was sent on
AzNetworking::PacketId m_sentPacketId = AzNetworking::InvalidPacketId;
NetEntityRole m_netEntityRole = NetEntityRole::InvalidRole;;
NetEntityRole m_remoteNetEntityRole = NetEntityRole::InvalidRole;;
};
}

@ -175,7 +175,7 @@ void Handle{{ PropertyName }}(AzNetworking::IConnection* invokingConnection, {{
//! {{ PropertyName }} Handler
//! {{ Property.attrib['Description'] }}
//! HandleOn {{ HandleOn }}
virtual void Handle{{ PropertyName }}(AzNetworking::IConnection* invokingConnection, {{ ', '.join(paramDefines) }}) = 0;
virtual void Handle{{ PropertyName }}([[maybe_unused]] AzNetworking::IConnection* invokingConnection, [[maybe_unused]] {{ ', [[maybe_unused]] '.join(paramDefines) }}) {}
{% endif %}
{% endmacro %}
{#

@ -311,7 +311,7 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const {{ Prop
{{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }}
void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramDefines) }})
{
constexpr RpcIndex rpcId = static_cast<RpcIndex>({{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ UpperFirst(Property.attrib['Name']) }});
constexpr Multiplayer::RpcIndex rpcId = static_cast<Multiplayer::RpcIndex>({{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ UpperFirst(Property.attrib['Name']) }});
{% if Property.attrib['IsReliable']|booleanTrue %}
constexpr AzNetworking::ReliabilityType isReliable = Multiplayer::ReliabilityType::Reliable;
{% else %}
@ -368,6 +368,31 @@ void {{ ClassName }}::Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.jo
->Method("{{ UpperFirst(Property.attrib['Name']) }}", [](const {{ ClassName }}* self, {{ ', '.join(paramDefines) }}) {
self->m_controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }});
})
->Method("{{ UpperFirst(Property.attrib['Name']) }}ByEntity", [](AZ::EntityId id, {{ ', '.join(paramDefines) }}) {
AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(id);
if (!entity)
{
AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} failed. The entity with id %s doesn't exist, please provide a valid entity id.", id.ToString().c_str())
return;
}
{{ ClassName }}* networkComponent = entity->FindComponent<{{ ClassName }}>();
if (!networkComponent)
{
AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} failed. Entity '%s' (id: %s) is missing {{ ClassName }}, be sure to add {{ ClassName }} to this entity.", entity->GetName().c_str(), id.ToString().c_str())
return;
}
{{ ClassName }}Controller* controller = static_cast<{{ ClassName }}Controller*>(networkComponent->GetController());
if (!controller)
{
AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} method failed. Entity '%s' (id: %s) {{ ClassName }} is missing the network controller. This RemoteProcedure can only be invoked from {{InvokeFrom}} network entities, because this entity doesn't have a controller, it must not be a {{InvokeFrom}} entity. Please check your network context before attempting to call {{ UpperFirst(Property.attrib['Name']) }}.", entity->GetName().c_str(), id.ToString().c_str())
return;
}
controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }});
})
{% endif %}
{% endcall %}
{% endmacro %}

@ -25,6 +25,7 @@ namespace Multiplayer
AZ_CVAR(AZ::TimeMs, cl_MaxRewindHistoryMs, AZ::TimeMs{ 2000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum number of milliseconds to keep for server correction rewind and replay");
#ifndef AZ_RELEASE_BUILD
AZ_CVAR(float, cl_DebugHackTimeMultiplier, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Scalar value used to simulate clock hacking cheats for validating bank time system and anticheat");
AZ_CVAR(bool, cl_EnableDesyncDebugging, false, nullptr, AZ::ConsoleFunctorFlags::Null, "If enabled, debug logs will contain verbose information on detected state desyncs");
#endif
AZ_CVAR(bool, sv_EnableCorrections, true, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables server corrections on autonomous proxy desyncs");
@ -214,11 +215,12 @@ namespace Multiplayer
// Send correction
SendClientInputCorrection(GetLastInputId(), correction);
#ifdef _DEBUG
// In debug, show which states caused the correction
#ifndef AZ_RELEASE_BUILD
AZStd::string clientStateString;
AZStd::string serverStateString;
if (cl_EnableDesyncDebugging)
{
// In debug, show which states caused the correction
// Write in client state
AzNetworking::NetworkOutputSerializer clientStateSerializer(clientState.GetBuffer(), clientState.GetSize());
GetNetBindComponent()->SerializeEntityCorrection(clientStateSerializer);
@ -236,11 +238,13 @@ namespace Multiplayer
GetNetBindComponent()->SerializeEntityCorrection(serverValues);
AZStd::map<AZStd::string, AZStd::pair<AZStd::string, AZStd::string>> mapComparison;
// put the server value in the first part of the pair
for (const auto& pair : serverValues.GetValueMap())
{
mapComparison[pair.first].first = pair.second;
}
// put the client value in the second part of the pair
for (const auto& pair : clientValues.GetValueMap())
{
@ -266,12 +270,13 @@ namespace Multiplayer
}
}
}
#else
const AZStd::string clientStateString = "available in debug only";
const AZStd::string serverStateString = "available in debug only";
#endif
else
{
clientStateString = "available in debug only";
serverStateString = "available in debug only";
}
AZLOG_ERROR("** Autonomous proxy desync detected! ** clientState=[%s], serverState=[%s]", clientStateString.c_str(), serverStateString.c_str());
#endif
}
}
}
@ -416,7 +421,7 @@ namespace Multiplayer
ClientInputId LocalPredictionPlayerInputComponentController::GetLastInputId() const
{
return m_clientInputId;
return m_lastClientInputId;
}
HostFrameId LocalPredictionPlayerInputComponentController::GetInputFrameId(const NetworkInput& input) const
@ -520,10 +525,13 @@ namespace Multiplayer
// In debug, send the entire client output state to the server to make it easier to debug desync issues
AzNetworking::PacketEncodingBuffer processInputResult;
#ifdef _DEBUG
AzNetworking::NetworkInputSerializer processInputResultSerializer(processInputResult.GetBuffer(), processInputResult.GetCapacity());
GetNetBindComponent()->SerializeEntityCorrection(processInputResultSerializer);
processInputResult.Resize(processInputResultSerializer.GetSize());
#ifndef AZ_RELEASE_BUILD
if (cl_EnableDesyncDebugging)
{
AzNetworking::NetworkInputSerializer processInputResultSerializer(processInputResult.GetBuffer(), processInputResult.GetCapacity());
GetNetBindComponent()->SerializeEntityCorrection(processInputResultSerializer);
processInputResult.Resize(processInputResultSerializer.GetSize());
}
#endif
// Save this input and discard move history outside our client rewind window

@ -390,6 +390,11 @@ namespace Multiplayer
m_entityServerMigrationEvent.Signal(m_netEntityHandle, hostId, connectionId);
}
void NetBindComponent::NotifyPreRender(float deltaTime, float blendFactor)
{
m_entityPreRenderEvent.Signal(deltaTime, blendFactor);
}
void NetBindComponent::AddEntityStopEventHandler(EntityStopEvent::Handler& eventHandler)
{
eventHandler.Connect(m_entityStopEvent);
@ -420,6 +425,11 @@ namespace Multiplayer
eventHandler.Connect(m_entityServerMigrationEvent);
}
void NetBindComponent::AddEntityPreRenderEventHandler(EntityPreRenderEvent::Handler& eventHandler)
{
eventHandler.Connect(m_entityPreRenderEvent);
}
bool NetBindComponent::SerializeEntityCorrection(AzNetworking::ISerializer& serializer)
{
m_predictableRecord.ResetConsumedBits();

@ -33,6 +33,8 @@ namespace Multiplayer
: m_rotationEventHandler([this](const AZ::Quaternion& rotation) { OnRotationChangedEvent(rotation); })
, m_translationEventHandler([this](const AZ::Vector3& translation) { OnTranslationChangedEvent(translation); })
, m_scaleEventHandler([this](float scale) { OnScaleChangedEvent(scale); })
, m_resetCountEventHandler([this](const uint8_t&) { OnResetCountChangedEvent(); })
, m_entityPreRenderEventHandler([this](float deltaTime, float blendFactor) { OnPreRender(deltaTime, blendFactor); })
{
;
}
@ -47,6 +49,11 @@ namespace Multiplayer
RotationAddEvent(m_rotationEventHandler);
TranslationAddEvent(m_translationEventHandler);
ScaleAddEvent(m_scaleEventHandler);
ResetCountAddEvent(m_resetCountEventHandler);
GetNetBindComponent()->AddEntityPreRenderEventHandler(m_entityPreRenderEventHandler);
// When coming into relevance, reset all blending factors so we don't interpolate to our start position
OnResetCountChangedEvent();
}
void NetworkTransformComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
@ -56,23 +63,40 @@ namespace Multiplayer
void NetworkTransformComponent::OnRotationChangedEvent(const AZ::Quaternion& rotation)
{
AZ::Transform worldTm = GetTransformComponent()->GetWorldTM();
worldTm.SetRotation(rotation);
GetTransformComponent()->SetWorldTM(worldTm);
m_previousTransform.SetRotation(m_targetTransform.GetRotation());
m_targetTransform.SetRotation(rotation);
}
void NetworkTransformComponent::OnTranslationChangedEvent(const AZ::Vector3& translation)
{
AZ::Transform worldTm = GetTransformComponent()->GetWorldTM();
worldTm.SetTranslation(translation);
GetTransformComponent()->SetWorldTM(worldTm);
m_previousTransform.SetTranslation(m_targetTransform.GetTranslation());
m_targetTransform.SetTranslation(translation);
}
void NetworkTransformComponent::OnScaleChangedEvent(float scale)
{
AZ::Transform worldTm = GetTransformComponent()->GetWorldTM();
worldTm.SetUniformScale(scale);
GetTransformComponent()->SetWorldTM(worldTm);
m_previousTransform.SetUniformScale(m_targetTransform.GetUniformScale());
m_targetTransform.SetUniformScale(scale);
}
void NetworkTransformComponent::OnResetCountChangedEvent()
{
m_targetTransform.SetRotation(GetRotation());
m_targetTransform.SetTranslation(GetTranslation());
m_targetTransform.SetUniformScale(GetScale());
m_previousTransform = m_targetTransform;
}
void NetworkTransformComponent::OnPreRender([[maybe_unused]] float deltaTime, float blendFactor)
{
if (!HasController())
{
AZ::Transform blendTransform;
blendTransform.SetRotation(m_previousTransform.GetRotation().Slerp(m_targetTransform.GetRotation(), blendFactor));
blendTransform.SetTranslation(m_previousTransform.GetTranslation().Lerp(m_targetTransform.GetTranslation(), blendFactor));
blendTransform.SetUniformScale(AZ::Lerp(m_previousTransform.GetUniformScale(), m_targetTransform.GetUniformScale(), blendFactor));
GetTransformComponent()->SetWorldTM(blendTransform);
}
}
@ -96,11 +120,8 @@ namespace Multiplayer
void NetworkTransformComponentController::OnTransformChangedEvent(const AZ::Transform& worldTm)
{
if (IsAuthority())
{
SetRotation(worldTm.GetRotation());
SetTranslation(worldTm.GetTranslation());
SetScale(worldTm.GetUniformScale());
}
SetRotation(worldTm.GetRotation());
SetTranslation(worldTm.GetTranslation());
SetScale(worldTm.GetUniformScale());
}
}

@ -140,7 +140,7 @@ namespace Multiplayer
AZ::CVarFixedString remoteAddress;
uint16_t remotePort;
if (console->GetCvarValue("editorsv_serveraddr", remoteAddress) != AZ::GetValueResult::ConsoleVarNotFound &&
console->GetCvarValue("editorsv_port", remotePort) != AZ::GetValueResult::ConsoleVarNotFound)
console->GetCvarValue("sv_port", remotePort) != AZ::GetValueResult::ConsoleVarNotFound)
{
// Connect the Editor to the editor server for Multiplayer simulation
AZ::Interface<IMultiplayer>::Get()->InitializeMultiplayer(MultiplayerAgentType::Client);
@ -149,6 +149,8 @@ namespace Multiplayer
const IpAddress ipAddress(remoteAddress.c_str(), remotePort, networkInterface->GetType());
networkInterface->Connect(ipAddress);
AZ::Interface<IMultiplayer>::Get()->SendReadyForEntityUpdates(true);
}
}
}

@ -165,7 +165,7 @@ namespace Multiplayer
// BeginGameMode and Prefab Processing have completed at this point
IMultiplayerTools* mpTools = AZ::Interface<IMultiplayerTools>::Get();
if (editorsv_enabled && mpTools != nullptr && mpTools->DidProcessNetworkPrefabs())
if (editorsv_enabled && mpTools != nullptr)
{
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& assetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData();

@ -12,7 +12,6 @@
#include <Multiplayer/MultiplayerConstants.h>
#include <Multiplayer/Components/MultiplayerComponent.h>
#include <MultiplayerSystemComponent.h>
#include <ConnectionData/ClientToServerConnectionData.h>
#include <ConnectionData/ServerToClientConnectionData.h>
@ -24,14 +23,23 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/Math/ShapeIntersection.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/Components/CameraBus.h>
#include <AzFramework/Visibility/IVisibilitySystem.h>
#include <AzFramework/Visibility/EntityBoundsUnionBus.h>
#include <AzFramework/Spawnable/Spawnable.h>
#include <AzNetworking/Framework/INetworking.h>
#include <cmath>
namespace AZ::ConsoleTypeHelpers
{
template <>
@ -74,6 +82,7 @@ namespace Multiplayer
AZ_CVAR(ProtocolType, sv_protocol, ProtocolType::Udp, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "This flag controls whether we use TCP or UDP for game networking");
AZ_CVAR(bool, sv_isDedicated, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether the host command creates an independent or client hosted server");
AZ_CVAR(AZ::TimeMs, cl_defaultNetworkEntityActivationTimeSliceMs, AZ::TimeMs{ 0 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Max Ms to use to activate entities coming from the network, 0 means instantiate everything");
AZ_CVAR(AZ::TimeMs, sv_serverSendRateMs, AZ::TimeMs{ 50 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum number of milliseconds between each network update");
AZ_CVAR(AZ::CVarFixedString, sv_defaultPlayerSpawnAsset, "prefabs/player.network.spawnable", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The default spawnable to use when a new player connects");
void MultiplayerSystemComponent::Reflect(AZ::ReflectContext* context)
@ -156,10 +165,26 @@ namespace Multiplayer
AZ::TickBus::Handler::BusDisconnect();
}
void MultiplayerSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
void MultiplayerSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
AZ::TimeMs deltaTimeMs = aznumeric_cast<AZ::TimeMs>(static_cast<int32_t>(deltaTime * 1000.0f));
AZ::TimeMs hostTimeMs = AZ::GetElapsedTimeMs();
const AZ::TimeMs deltaTimeMs = aznumeric_cast<AZ::TimeMs>(static_cast<int32_t>(deltaTime * 1000.0f));
const AZ::TimeMs hostTimeMs = AZ::GetElapsedTimeMs();
const AZ::TimeMs serverRateMs = static_cast<AZ::TimeMs>(sv_serverSendRateMs);
const float serverRateSeconds = static_cast<float>(serverRateMs) / 1000.0f;
TickVisibleNetworkEntities(deltaTime, serverRateSeconds);
if (GetAgentType() == MultiplayerAgentType::ClientServer
|| GetAgentType() == MultiplayerAgentType::DedicatedServer)
{
m_serverSendAccumulator += deltaTime;
if (m_serverSendAccumulator < serverRateSeconds)
{
return;
}
m_serverSendAccumulator -= serverRateSeconds;
m_networkTime.IncrementHostFrameId();
}
// Handle deferred local rpc messages that were generated during the updates
m_networkEntityManager.DispatchLocalDeferredRpcMessages();
@ -365,13 +390,21 @@ namespace Multiplayer
}
EntityReplicationManager& replicationManager = reinterpret_cast<IConnectionData*>(connection->GetUserData())->GetReplicationManager();
// Ignore a_Request.GetServerGameTimePoint(), clients can't affect the server gametime
if ((GetAgentType() == MultiplayerAgentType::Client) && (packet.GetHostFrameId() > m_lastReplicatedHostFrameId))
{
// Update client to latest server time
m_renderBlendFactor = 0.0f;
m_lastReplicatedHostTimeMs = packet.GetHostTimeMs();
m_lastReplicatedHostFrameId = packet.GetHostFrameId();
m_networkTime.AlterTime(m_lastReplicatedHostFrameId, m_lastReplicatedHostTimeMs, AzNetworking::InvalidConnectionId);
}
for (AZStd::size_t i = 0; i < packet.GetEntityMessages().size(); ++i)
{
const NetworkEntityUpdateMessage& updateMessage = packet.GetEntityMessages()[i];
handledAll &= replicationManager.HandleEntityUpdateMessage(connection, packetHeader, updateMessage);
AZ_Assert(handledAll, "GameServerToClientNetworkRequestHandler EntityUpdates Did not handle all updates");
AZ_Assert(handledAll, "EntityUpdates did not handle all update messages");
}
return handledAll;
@ -440,7 +473,7 @@ namespace Multiplayer
// Hosts will spawn a new default player prefab for the user that just connected
if (GetAgentType() == MultiplayerAgentType::ClientServer
|| GetAgentType() == MultiplayerAgentType::DedicatedServer)
|| GetAgentType() == MultiplayerAgentType::DedicatedServer)
{
NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab();
if (controlledEntity.Exists())
@ -602,6 +635,74 @@ namespace Multiplayer
AZLOG_INFO("Total RPCs received bytes: %llu", aznumeric_cast<AZ::u64>(rpcsRecv.m_totalBytes));
}
void MultiplayerSystemComponent::TickVisibleNetworkEntities(float deltaTime, float serverRateSeconds)
{
const float targetAdjustBlend = AZStd::clamp(deltaTime / serverRateSeconds, 0.0f, 1.0f);
m_renderBlendFactor += targetAdjustBlend;
// Linear close to the origin, but asymptote at y = 1
const float adjustedBlendFactor = 1.0f - (std::pow(0.2f, m_renderBlendFactor));
AZLOG(NET_Blending, "Computed blend factor of %f", adjustedBlendFactor);
if (Camera::ActiveCameraRequestBus::HasHandlers())
{
// If there's a camera, update only what's visible
AZ::Transform activeCameraTransform;
Camera::Configuration activeCameraConfiguration;
Camera::ActiveCameraRequestBus::BroadcastResult(activeCameraTransform, &Camera::ActiveCameraRequestBus::Events::GetActiveCameraTransform);
Camera::ActiveCameraRequestBus::BroadcastResult(activeCameraConfiguration, &Camera::ActiveCameraRequestBus::Events::GetActiveCameraConfiguration);
const AZ::ViewFrustumAttributes frustumAttributes
(
activeCameraTransform,
activeCameraConfiguration.m_frustumHeight / activeCameraConfiguration.m_frustumWidth,
activeCameraConfiguration.m_fovRadians,
activeCameraConfiguration.m_nearClipDistance,
activeCameraConfiguration.m_farClipDistance
);
const AZ::Frustum viewFrustum = AZ::Frustum(frustumAttributes);
// Unfortunately necessary, as NotifyPreRender can update transforms and thus cause a deadlock inside the vis system
AZStd::vector<NetBindComponent*> gatheredEntities;
AzFramework::IEntityBoundsUnion* entityBoundsUnion = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get();
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetDefaultVisibilityScene()->Enumerate(viewFrustum,
[&gatheredEntities, entityBoundsUnion](const AzFramework::IVisibilityScene::NodeData& nodeData)
{
gatheredEntities.reserve(gatheredEntities.size() + nodeData.m_entries.size());
for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries)
{
if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity)
{
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
if (netBindComponent != nullptr)
{
gatheredEntities.push_back(netBindComponent);
}
}
}
});
for (NetBindComponent* netBindComponent : gatheredEntities)
{
netBindComponent->NotifyPreRender(deltaTime, adjustedBlendFactor);
}
}
else
{
// If there's no camera, fall back to updating all net entities
for (auto& iter : *(m_networkEntityManager.GetNetworkEntityTracker()))
{
AZ::Entity* entity = iter.second;
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
if (netBindComponent != nullptr)
{
netBindComponent->NotifyPreRender(deltaTime, adjustedBlendFactor);
}
}
}
}
void MultiplayerSystemComponent::OnConsoleCommandInvoked
(
AZStd::string_view command,

@ -102,6 +102,7 @@ namespace Multiplayer
private:
void TickVisibleNetworkEntities(float deltaTime, float serverRateSeconds);
void OnConsoleCommandInvoked(AZStd::string_view command, const AZ::ConsoleCommandContainer& args, AZ::ConsoleFunctorFlags flags, AZ::ConsoleInvokedFrom invokedFrom);
void ExecuteConsoleCommandList(AzNetworking::IConnection* connection, const AZStd::fixed_vector<Multiplayer::LongNetworkString, 32>& commands);
NetworkEntityHandle SpawnDefaultPlayerPrefab();
@ -124,6 +125,9 @@ namespace Multiplayer
AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 };
HostFrameId m_lastReplicatedHostFrameId = InvalidHostFrameId;
double m_serverSendAccumulator = 0.0;
float m_renderBlendFactor = 0.0f;
#if !defined(AZ_RELEASE_BUILD)
MultiplayerEditorConnection m_editorConnectionListener;
#endif

@ -132,7 +132,7 @@ namespace Multiplayer
EntityReplicatorList replicatorUpdatedList;
MultiplayerPackets::EntityUpdates entityUpdatePacket;
entityUpdatePacket.SetHostTimeMs(hostTimeMs);
entityUpdatePacket.SetHostFrameId(InvalidHostFrameId);
entityUpdatePacket.SetHostFrameId(GetNetworkTime()->GetHostFrameId());
// Serialize everything
while (!toSendList.empty())
{

@ -27,7 +27,7 @@ namespace Multiplayer
, m_sentRecords(net_EntityReplicatorRecordsMax)
{
AZ_Assert(m_netBindComponent, "NetBindComponent is nullptr");
m_pendingRecord.SetNetworkRole(remoteNetworkRole);
m_pendingRecord.SetRemoteNetworkRole(remoteNetworkRole);
}
bool PropertyPublisher::IsDeleting() const
@ -67,7 +67,7 @@ namespace Multiplayer
void PropertyPublisher::SetRebasing()
{
AZ_Assert(m_pendingRecord.GetNetworkRole() == NetEntityRole::Autonomous, "Expected to be rebasing on a Autonomous entity");
AZ_Assert(m_pendingRecord.GetRemoteNetworkRole() == NetEntityRole::Autonomous, "Expected to be rebasing on a Autonomous entity");
m_replicatorState = EntityReplicatorState::Rebasing;
}
@ -118,7 +118,7 @@ namespace Multiplayer
m_sentRecords.clear();
m_netBindComponent->FillTotalReplicationRecord(m_pendingRecord);
// Don't send predictable properties back to the Autonomous unless we correct them
if (m_pendingRecord.GetNetworkRole() == NetEntityRole::Autonomous)
if (m_pendingRecord.GetRemoteNetworkRole() == NetEntityRole::Autonomous)
{
m_pendingRecord.Subtract(m_netBindComponent->GetPredictableRecord());
}
@ -137,7 +137,7 @@ namespace Multiplayer
// We need to clear out old records, and build up a list of everything that has changed since the last acked packet
m_sentRecords.push_front(m_pendingRecord);
auto iter = m_sentRecords.begin();
++iter; // consider everything after the record we are going to send
++iter; // Consider everything after the record we are going to send
for (; iter != m_sentRecords.end(); ++iter)
{
// Sequence wasn't acked, so we need to send these bits again
@ -145,7 +145,7 @@ namespace Multiplayer
}
// Don't send predictable properties back to the Autonomous unless we correct them
if (m_pendingRecord.GetNetworkRole() == NetEntityRole::Autonomous)
if (m_pendingRecord.GetRemoteNetworkRole() == NetEntityRole::Autonomous)
{
m_pendingRecord.Subtract(m_netBindComponent->GetPredictableRecord());
}

@ -49,19 +49,19 @@ namespace Multiplayer
}
ReplicationRecord::ReplicationRecord(NetEntityRole netEntityRole)
: m_netEntityRole(netEntityRole)
: m_remoteNetEntityRole(netEntityRole)
{
;
}
void ReplicationRecord::SetNetworkRole(NetEntityRole netEntityRole)
void ReplicationRecord::SetRemoteNetworkRole(NetEntityRole remoteNetEntityRole)
{
m_netEntityRole = netEntityRole;
m_remoteNetEntityRole = remoteNetEntityRole;
}
NetEntityRole ReplicationRecord::GetNetworkRole() const
NetEntityRole ReplicationRecord::GetRemoteNetworkRole() const
{
return m_netEntityRole;
return m_remoteNetEntityRole;
}
bool ReplicationRecord::AreAllBitsConsumed() const
@ -196,26 +196,26 @@ namespace Multiplayer
bool ReplicationRecord::ContainsAuthorityToClientBits() const
{
return (m_netEntityRole != NetEntityRole::Authority)
|| (m_netEntityRole == NetEntityRole::InvalidRole);
return (m_remoteNetEntityRole != NetEntityRole::Authority)
|| (m_remoteNetEntityRole == NetEntityRole::InvalidRole);
}
bool ReplicationRecord::ContainsAuthorityToServerBits() const
{
return (m_netEntityRole == NetEntityRole::Server)
|| (m_netEntityRole == NetEntityRole::InvalidRole);
return (m_remoteNetEntityRole == NetEntityRole::Server)
|| (m_remoteNetEntityRole == NetEntityRole::InvalidRole);
}
bool ReplicationRecord::ContainsAuthorityToAutonomousBits() const
{
return (m_netEntityRole == NetEntityRole::Autonomous || m_netEntityRole == NetEntityRole::Server)
|| (m_netEntityRole == NetEntityRole::InvalidRole);
return (m_remoteNetEntityRole == NetEntityRole::Autonomous || m_remoteNetEntityRole == NetEntityRole::Server)
|| (m_remoteNetEntityRole == NetEntityRole::InvalidRole);
}
bool ReplicationRecord::ContainsAutonomousToAuthorityBits() const
{
return (m_netEntityRole == NetEntityRole::Authority)
|| (m_netEntityRole == NetEntityRole::InvalidRole);
return (m_remoteNetEntityRole == NetEntityRole::Authority)
|| (m_remoteNetEntityRole == NetEntityRole::InvalidRole);
}
uint32_t ReplicationRecord::GetRemainingAuthorityToClientBits() const

@ -27,6 +27,6 @@ namespace NumericalMethods::Optimization
const double epsilon = 1e-7;
// values recommended in Nocedal and Wright for constants in the Wolfe conditions for satisfactory solution improvement
const double c1 = 1e-4;
const double c2 = 0.9;
const double WolfeConditionsC1 = 1e-4;
const double WolfeConditionsC2 = 0.9;
} // namespace NumericalMethods::Optimization

@ -72,7 +72,7 @@ namespace NumericalMethods::Optimization
for (AZ::u32 iteration = 0; iteration < lineSearchIterations; iteration++)
{
ScalarVariable alphaNew;
ScalarVariable alphaNew = 0.0;
if (iteration > 0)
{
// first try selecting a new alpha value based on cubic interpolation through the most recent points
@ -162,16 +162,16 @@ namespace NumericalMethods::Optimization
{
// if the value of f corresponding to alpha1 isn't sufficiently small compared to f at x0,
// then the interval [alpha0 ... alpha1] must bracket a suitable point.
if ((f_alpha1 > f_x0 + c1 * alpha1 * df_x0) || (iteration > 0 && f_alpha1 > f_alpha0))
if ((f_alpha1 > f_x0 + WolfeConditionsC1 * alpha1 * df_x0) || (iteration > 0 && f_alpha1 > f_alpha0))
{
return SelectStepSizeFromInterval(alpha0, alpha1, f_alpha0, f_alpha1, df_alpha0,
f, x0, searchDirection, f_x0, df_x0, c1, c2);
f, x0, searchDirection, f_x0, df_x0, WolfeConditionsC1, WolfeConditionsC2);
}
// otherwise, if the derivative corresponding to alpha1 is large enough, alpha1 already
// satisfies the Wolfe conditions and so return alpha1.
double df_alpha1 = DirectionalDerivative(f, x0 + alpha1 * searchDirection, searchDirection);
if (fabs(df_alpha1) <= -c2 * df_x0)
if (fabs(df_alpha1) <= -WolfeConditionsC2 * df_x0)
{
LineSearchResult result;
result.m_outcome = LineSearchOutcome::Success;
@ -184,7 +184,7 @@ namespace NumericalMethods::Optimization
if (df_alpha1 >= 0.0)
{
return SelectStepSizeFromInterval(alpha1, alpha0, f_alpha1, f_alpha0, df_alpha1,
f, x0, searchDirection, f_x0, df_x0, c1, c2);
f, x0, searchDirection, f_x0, df_x0, WolfeConditionsC1, WolfeConditionsC2);
}
// haven't found an interval which is guaranteed to bracket a suitable point,

@ -158,12 +158,12 @@ namespace NumericalMethods::Optimization
double f_x0 = f_alpha0;
double df_x0 = df_alpha0;
LineSearchResult lineSearchResult = SelectStepSizeFromInterval(alpha0, alpha1, f_alpha0, f_alpha1, df_alpha0,
testFunctionRosenbrock, x0, searchDirection, f_x0, df_x0, c1, c2);
testFunctionRosenbrock, x0, searchDirection, f_x0, df_x0, WolfeConditionsC1, WolfeConditionsC2);
EXPECT_TRUE(lineSearchResult.m_outcome == LineSearchOutcome::Success);
// check that the Wolfe conditions are satisfied by the returned step size
EXPECT_TRUE(lineSearchResult.m_functionValue < f_x0 + c1 * df_x0 * lineSearchResult.m_stepSize);
EXPECT_TRUE(fabs(lineSearchResult.m_derivativeValue) <= -c2 * df_x0);
EXPECT_TRUE(lineSearchResult.m_functionValue < f_x0 + WolfeConditionsC1 * df_x0 * lineSearchResult.m_stepSize);
EXPECT_TRUE(fabs(lineSearchResult.m_derivativeValue) <= -WolfeConditionsC2 * df_x0);
}
TEST(OptimizationTest, LineSearch_VariousSearchDirections_SatisfiesWolfeCondition)
@ -180,8 +180,8 @@ namespace NumericalMethods::Optimization
EXPECT_TRUE(lineSearchResult.m_outcome == LineSearchOutcome::Success);
// check that the Wolfe conditions are satisfied by the returned step size
EXPECT_TRUE(lineSearchResult.m_functionValue < f_x0 + c1 * df_x0 * lineSearchResult.m_stepSize);
EXPECT_TRUE(fabs(lineSearchResult.m_derivativeValue) <= -c2 * df_x0);
EXPECT_TRUE(lineSearchResult.m_functionValue < f_x0 + WolfeConditionsC1 * df_x0 * lineSearchResult.m_stepSize);
EXPECT_TRUE(fabs(lineSearchResult.m_derivativeValue) <= -WolfeConditionsC2 * df_x0);
}
}

@ -112,9 +112,9 @@ namespace PhysX::Benchmarks
for (double percentile : percentiles)
{
//ensure the percentile is between 0.0 and 1.0
const double epsilon = 0.001;
AZ::ClampIfCloseMag(percentile, 0.0, epsilon);
AZ::ClampIfCloseMag(percentile, 1.0, epsilon);
const double testEpsilon = 0.001;
AZ::ClampIfCloseMag(percentile, 0.0, testEpsilon);
AZ::ClampIfCloseMag(percentile, 1.0, testEpsilon);
size_t idx = aznumeric_cast<size_t>(std::round(percentile * (values.size() - 1)));
std::nth_element(values.begin(), values.begin() + idx, values.end());

@ -15,6 +15,7 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Console/IConsole.h>
#include <PhysX/SystemComponentBus.h>
#include <PhysX/MathConversion.h>
@ -183,9 +184,7 @@ namespace PhysXDebug
void SystemComponent::OnCrySystemInitialized([[maybe_unused]] ISystem& system, const SSystemInitParams&)
{
InitPhysXColorMappings();
RegisterCommands();
ConfigurePhysXVisualizationParameters();
}
void SystemComponent::Reflect(AZ::ReflectContext* context)
@ -537,12 +536,13 @@ namespace PhysXDebug
}
}
static void CmdEnableWireFrame([[maybe_unused]] IConsoleCmdArgs* args)
static void physx_CullingBox([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
PhysXDebug::PhysXDebugRequestBus::Broadcast(&PhysXDebug::PhysXDebugRequestBus::Events::ToggleCullingWireFrame);
}
AZ_CONSOLEFREEFUNC(physx_CullingBox, AZ::ConsoleFunctorFlags::DontReplicate, "Enables physx wireframe view");
static void CmdConnectToPvd([[maybe_unused]] IConsoleCmdArgs* args)
static void physx_PvdConnect([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
auto* debug = AZ::Interface<PhysX::Debug::PhysXDebugInterface>::Get();
if (debug)
@ -550,8 +550,9 @@ namespace PhysXDebug
debug->ConnectToPvd();
}
}
AZ_CONSOLEFREEFUNC(physx_PvdConnect, AZ::ConsoleFunctorFlags::DontReplicate, "Connects to the physx visual debugger");
static void CmdDisconnectFromPvd([[maybe_unused]] IConsoleCmdArgs* args)
static void physx_PvdDisconnect([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
auto* debug = AZ::Interface<PhysX::Debug::PhysXDebugInterface>::Get();
if (debug)
@ -559,13 +560,14 @@ namespace PhysXDebug
debug->DisconnectFromPvd();
}
}
AZ_CONSOLEFREEFUNC(physx_PvdDisconnect, AZ::ConsoleFunctorFlags::DontReplicate, "Disconnects from the physx visual debugger");
static void CmdSetPhysXDebugCullingBoxSize(IConsoleCmdArgs* args)
static void physx_CullingBoxSize([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
const int argumentCount = args->GetArgCount();
const int argumentCount = arguments.size();
if (argumentCount == 2)
{
float newCullingBoxSize = (float)strtol(args->GetArg(1), nullptr, 10);
float newCullingBoxSize = (float)strtol(AZ::CVarFixedString(arguments[1]).c_str(), nullptr, 10);
PhysXDebug::PhysXDebugRequestBus::Broadcast(&PhysXDebug::PhysXDebugRequestBus::Events::SetCullingBoxSize, newCullingBoxSize);
}
else
@ -574,16 +576,17 @@ namespace PhysXDebug
"Please use physx_SetDebugCullingBoxSize <boxSize> e.g. physx_SetDebugCullingBoxSize 100.");
}
}
AZ_CONSOLEFREEFUNC(physx_CullingBoxSize, AZ::ConsoleFunctorFlags::DontReplicate, "Sets physx debug culling box size");
static void CmdTogglePhysXDebugVisualization(IConsoleCmdArgs* args)
static void physx_Debug([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
using namespace CryStringUtils;
const int argumentCount = args->GetArgCount();
const int argumentCount = arguments.size();
if (argumentCount == 2)
{
const auto userPreference = static_cast<DebugCVarValues>(strtol(args->GetArg(1), nullptr, 10));
const auto userPreference = static_cast<DebugCVarValues>(strtol(AZ::CVarFixedString(arguments[1]).c_str(), nullptr, 10));
switch (userPreference)
{
@ -609,29 +612,7 @@ namespace PhysXDebug
AZ_Warning("PhysXDebug", false, "Invalid physx_Debug Arguments. Please use physx_Debug 1 to enable, physx_Debug 0 to disable or physx_Debug 2 to enable all configuration settings.");
}
}
void SystemComponent::RegisterCommands()
{
if (m_registered)
{
return;
}
if (gEnv)
{
IConsole* console = gEnv->pSystem->GetIConsole();
if (console)
{
console->AddCommand("physx_Debug", CmdTogglePhysXDebugVisualization);
console->AddCommand("physx_CullingBox", CmdEnableWireFrame);
console->AddCommand("physx_CullingBoxSize", CmdSetPhysXDebugCullingBoxSize);
console->AddCommand("physx_PvdConnect", CmdConnectToPvd);
console->AddCommand("physx_PvdDisconnect", CmdDisconnectFromPvd);
}
m_registered = true;
}
}
AZ_CONSOLEFREEFUNC(physx_Debug, AZ::ConsoleFunctorFlags::DontReplicate, "Toggles physx debug visualization");
void SystemComponent::ConfigurePhysXVisualizationParameters()
{

@ -161,9 +161,6 @@ namespace PhysXDebug
/// Initialise the PhysX debug draw colors based on defaults.
void InitPhysXColorMappings();
/// Register debug drawing PhysX commands with Open 3D Engine console during game mode.
void RegisterCommands();
/// Draw the culling box being used by the viewport.
/// @param cullingBoxAabb culling box Aabb to debug draw.
void DrawDebugCullingBox(const AZ::Aabb& cullingBoxAabb);

@ -10,7 +10,7 @@
# {END_LICENSE}
set(ENABLED_GEMS
Project::${Name}
${Name}
Atom_AtomBridge
Camera
CameraFramework

@ -29,9 +29,6 @@ function(ly_create_alias)
message(FATAL_ERROR "Provide the namespace of the alias to create using the NAMESPACE keyword")
endif()
if (NOT ly_create_alias_TARGETS)
message(FATAL_ERROR "Provide the name of the targets the alias be associated with, using the TARGETS keyword")
endif()
if(TARGET ${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME})
message(FATAL_ERROR "Target already exists, cannot create an alias for it: ${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME}\n"
@ -78,8 +75,11 @@ function(ly_create_alias)
list(APPEND final_targets ${de_aliased_target_name})
endforeach()
ly_parse_third_party_dependencies("${final_targets}")
ly_add_dependencies(${ly_create_alias_NAME} ${final_targets})
# add_dependencies must be called with at least one dependent target
if(final_targets)
ly_parse_third_party_dependencies("${final_targets}")
ly_add_dependencies(${ly_create_alias_NAME} ${final_targets})
endif()
# now add the final alias:
add_library(${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME} ALIAS ${ly_create_alias_NAME})

@ -443,7 +443,6 @@ function(ly_setup_others)
install(DIRECTORY
${LY_ROOT_FOLDER}/scripts/bundler
${LY_ROOT_FOLDER}/scripts/project_manager
${LY_ROOT_FOLDER}/scripts/o3de
DESTINATION ./scripts
COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT}

@ -74,8 +74,6 @@ ly_append_configurations_options(
/wd4436 # the result of unary operator may be unaligned
/wd4450 # declaration hides global declaration
/wd4457 # declaration hides function parameter
/wd4459 # declaration hides global declaration
/wd4701 # potentially unintialized local variable
# Enabling warnings that are disabled by default from /W4
# https://docs.microsoft.com/en-us/cpp/preprocessor/compiler-warnings-that-are-off-by-default?view=vs-2019

@ -25,14 +25,15 @@ set(gems_json_template [[
@target_gem_dependencies_names@
}
}
}]]
}
]]
)
set(gem_module_template [[
"@stripped_gem_target@":
{
"Modules":["$<TARGET_FILE_NAME:@gem_target@>"],
"SourcePaths":["@gem_module_root_relative_to_engine_root@"]
}]]
string(APPEND gem_module_template
[=[ "@stripped_gem_target@":]=] "\n"
[=[ {]=] "\n"
[=[$<$<NOT:$<IN_LIST:$<TARGET_PROPERTY:@gem_target@,TYPE>,INTERFACE_LIBRARY>>: "Modules":["$<TARGET_FILE_NAME:@gem_target@>"]]=] "$<COMMA>\n>"
[=[ "SourcePaths":["@gem_module_root_relative_to_engine_root@"]]=] "\n"
[=[ }]=]
)
#!ly_get_gem_load_dependencies: Retrieves the list of "load" dependencies for a target
@ -161,10 +162,12 @@ function(ly_delayed_generate_settings_registry)
message(FATAL_ERROR "Dependency ${gem_target} from ${target} does not exist")
endif()
get_property(has_manually_added_dependencies TARGET ${gem_target} PROPERTY MANUALLY_ADDED_DEPENDENCIES SET)
get_target_property(target_type ${gem_target} TYPE)
if (target_type STREQUAL "INTERFACE_LIBRARY")
# don't use interface libraries here, we only want ones which produce actual binaries.
# we have still already recursed into their dependencies - they'll show up later.
if (target_type STREQUAL "INTERFACE_LIBRARY" AND has_manually_added_dependencies)
# don't use interface libraries here, we only want ones which produce actual binaries unless the target
# is empty. We have still already recursed into their dependencies - they'll show up later.
# When the target has no dependencies however we want to add the gem root path to the generated setreg
continue()
endif()

@ -12,5 +12,4 @@
add_subdirectory(detect_file_changes)
add_subdirectory(commit_validation)
add_subdirectory(o3de)
add_subdirectory(project_manager)
add_subdirectory(ctest)

@ -32,7 +32,7 @@ def add_args(parser, subparsers) -> None:
# add the scripts/o3de directory to the front of the sys.path
sys.path.insert(0, str(o3de_package_dir))
from o3de import engine_template, global_project, register, print_registration, get_registration, \
enable_gem, disable_gem, sha256
enable_gem, disable_gem, project_properties, sha256
# Remove the temporarily added path
sys.path = sys.path[1:]
@ -55,7 +55,10 @@ def add_args(parser, subparsers) -> None:
# remove a gem from a project
disable_gem.add_args(subparsers)
# modify project properties
project_properties.add_args(subparsers)
# sha256
sha256.add_args(subparsers)

@ -573,9 +573,7 @@ def get_registered(engine_name: str = None,
return engine_path
elif isinstance(project_name, str):
enging_projects = get_engine_projects()
projects = json_data['projects'].copy()
projects.extend(engine_object['projects'])
projects = get_all_projects()
for project_path in projects:
project_path = pathlib.Path(project_path).resolve()
project_json = project_path / 'project.json'
@ -605,9 +603,7 @@ def get_registered(engine_name: str = None,
return gem_path
elif isinstance(template_name, str):
engine_templates = get_engine_templates()
templates = json_data['templates'].copy()
templates.extend(engine_templates)
templates = get_all_templates()
for template_path in templates:
template_path = pathlib.Path(template_path).resolve()
template_json = template_path / 'template.json'
@ -622,9 +618,7 @@ def get_registered(engine_name: str = None,
return template_path
elif isinstance(restricted_name, str):
engine_restricted = get_engine_restricted()
restricted = json_data['restricted'].copy()
restricted.extend(engine_restricted)
restricted = get_all_restricted()
for restricted_path in restricted:
restricted_path = pathlib.Path(restricted_path).resolve()
restricted_json = restricted_path / 'restricted.json'

@ -0,0 +1,104 @@
#
# 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.
#
import argparse
import json
import os
import pathlib
import sys
import logging
from o3de import manifest
logger = logging.getLogger()
logging.basicConfig()
def get_project_props(name: str = None, path: pathlib.Path = None) -> dict:
proj_json = manifest.get_project_json_data(project_name=name, project_path=path)
if not proj_json:
param = name if name else path
logger.error(f'Could not retrieve project.json file for {param}')
return None
return proj_json
def edit_project_props(proj_path, proj_name, new_origin, new_display,
new_summary, new_icon, new_tag, remove_tag) -> int:
proj_json = get_project_props(proj_name, proj_path)
if not proj_json:
return 1
if new_origin:
proj_json['origin'] = new_origin
if new_display:
proj_json['display_name'] = new_display
if new_summary:
proj_json['summary'] = new_summary
if new_icon:
proj_json['icon_path'] = new_icon
if new_tag:
proj_json.setdefault('user_tags', []).append(new_tag)
if remove_tag:
if 'user_tags' in proj_json:
if remove_tag in proj_json['user_tags']:
proj_json['user_tags'].remove(remove_tag)
else:
logger.warn(f'{remove_tag} not found in user_tags for removal.')
else:
logger.warn(f'user_tags property not found for removal of tag {remove_tag}.')
manifest.save_o3de_manifest(proj_json, pathlib.Path(proj_path) / 'project.json')
return 0
def _edit_project_props(args: argparse) -> int:
return edit_project_props(args.project_path,
args.project_name,
args.project_origin,
args.project_display,
args.project_summary,
args.project_icon,
args.project_tag,
args.remove_tag)
def add_parser_args(parser):
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-pp', '--project-path', type=pathlib.Path, required=False,
help='The path to the project.')
group.add_argument('-pn', '--project-name', type=str, required=False,
help='The name of the project.')
group = parser.add_argument_group('properties', 'arguments for modifying individual project properties.')
group.add_argument('-po', '--project-origin', type=str, required=False,
help='Sets description or url for project origin (such as project host, repository, owner...etc).')
group.add_argument('-pd', '--project-display', type=str, required=False,
help='Sets the project display name.')
group.add_argument('-ps', '--project-summary', type=str, required=False,
help='Sets the summary description of the project.')
group.add_argument('-pi', '--project-icon', type=str, required=False,
help='Sets the path to the projects icon resource.')
group.add_argument('-pt', '--project-tag', type=str, required=False,
help='Adds a tag to user_tags property. These tags are intended for documentation and filtering.')
group.add_argument('-rt', '--remove-tag', type=str, required=False,
help='Removes a tag from the user_tags property.')
parser.set_defaults(func=_edit_project_props)
def add_args(subparsers) -> None:
enable_project_props_subparser = subparsers.add_parser('edit-project-properties')
add_parser_args(enable_project_props_subparser)
def main():
the_parser = argparse.ArgumentParser()
add_parser_args(the_parser)
the_args = the_parser.parse_args()
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
sys.exit(ret)
if __name__ == "__main__":
main()

@ -1,22 +0,0 @@
#
# 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.
#
ly_download_associated_package(pyside2)
ly_add_pytest(
NAME test_pyside
PATH ${CMAKE_CURRENT_LIST_DIR}/tests/test_pyside.py
)
ly_add_pytest(
NAME test_projects
PATH ${CMAKE_CURRENT_LIST_DIR}/tests/test_projects.py
)

@ -1,10 +0,0 @@
#
# 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.
#

@ -1,804 +0,0 @@
#
# 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.
#
# PySide project and gem selector GUI
import os
import pathlib
import sys
import argparse
import json
import logging
import subprocess
from logging.handlers import RotatingFileHandler
from typing import List
from pyside import add_pyside_environment, is_pyside_ready, uninstall_env
engine_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..'))
sys.path.append(engine_path)
executable_path = ''
logger = logging.getLogger()
logger.setLevel(logging.INFO)
from o3de import disable_gem, enable_gem, cmake, engine_template, manifest, register
o3de_folder = manifest.get_o3de_folder()
o3de_logs_folder = manifest.get_o3de_logs_folder()
project_manager_log_file_path = o3de_logs_folder / "project_manager.log"
log_file_handler = RotatingFileHandler(filename=project_manager_log_file_path, maxBytes=1024 * 1024, backupCount=1)
formatter = logging.Formatter('%(asctime)s | %(levelname)s : %(message)s')
log_file_handler.setFormatter(formatter)
logger.addHandler(log_file_handler)
logger.info("Starting Project Manager")
def initialize_pyside_from_parser():
# Parse arguments up top. We need to know the path to our binaries and QT libs in particular to load up
# PySide
parser = argparse.ArgumentParser()
parser.add_argument('--executable-path', required=True, help='Path to Executable to launch with project')
parser.add_argument('--binaries-path', default=None, help='Path to QT Binaries necessary for PySide. If not'
' provided executable_path folder is assumed')
parser.add_argument('--parent-pid', default=0, help='Process ID of launching process')
args = parser.parse_args()
logger.info(f"parent_pid is {args.parent_pid}")
global executable_path
executable_path = args.executable_path
binaries_path = args.binaries_path or os.path.dirname(executable_path)
# Initialize PySide before imports below. This adds both PySide python modules to the python system interpreter
# path and adds the necessary paths to binaries for the DLLs to be found and load their dependencies
add_pyside_environment(binaries_path)
if not is_pyside_ready():
initialize_pyside_from_parser()
try:
from PySide2.QtWidgets import QApplication, QDialogButtonBox, QPushButton, QComboBox, QMessageBox, QFileDialog
from PySide2.QtWidgets import QListView, QLabel
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import QFile, QObject, Qt, Signal, Slot
from PySide2.QtGui import QIcon, QStandardItemModel, QStandardItem
except ImportError as e:
logger.error(f"Failed to import PySide2 with error {e}")
exit(-1)
logger.error(f"PySide2 imports successful")
class DialogLoggerSignaller(QObject):
send_to_dialog = Signal(str)
def __init__(self, dialog_logger):
super(DialogLoggerSignaller, self).__init__()
self.dialog_logger = dialog_logger
# Independent class to handle log forwarding. Logger and qt signals both use emit method.
# This class's job is to receive the logger record and then emit the formatted message through
# DialogLoggerSignaller which is what the ProjectDialog handler listens for
class DialogLogger(logging.Handler):
def __init__(self, log_dialog, log_level=logging.INFO, forward_log_level=logging.WARNING,
message_box_log_level=logging.ERROR):
super(DialogLogger, self).__init__()
self.log_dialog = log_dialog
self.log_level = log_level
self.forward_log_level = forward_log_level
self.message_box_log_level = message_box_log_level
self.log_records = []
self.formatter = logging.Formatter('%(levelname)s : %(message)s')
self.setFormatter(self.formatter)
self.signaller = DialogLoggerSignaller(self)
def emit(self, record):
self.log_records.append(record)
if record.levelno >= self.message_box_log_level:
QMessageBox.warning(None, record.levelname, record.message)
elif record.levelno >= self.forward_log_level:
self.signaller.send_to_dialog.emit(self.format(record))
class ProjectManagerDialog(QObject):
"""
Main project manager dialog is responsible for displaying the project selection list and output pane
"""
def __init__(self, parent=None):
super(ProjectManagerDialog, self).__init__(parent)
self.ui_path = (pathlib.Path(__file__).parent / 'ui').resolve()
self.home_folder = manifest.get_home_folder()
self.log_display = None
self.dialog_logger = DialogLogger(self)
logger.addHandler(self.dialog_logger)
logger.setLevel(logging.INFO)
self.dialog_logger.signaller.send_to_dialog.connect(self.handle_log_message)
self.mru_file_path = o3de_folder / 'mru.json'
self.create_from_template_ui_file_path = self.ui_path / 'create_from_template.ui'
self.create_gem_ui_file_path = self.ui_path / 'create_gem.ui'
self.create_project_ui_file_path = self.ui_path / 'create_project.ui'
self.manage_project_gem_targets_ui_file_path = self.ui_path / 'manage_gem_targets.ui'
self.project_manager_icon_file_path = self.ui_path / 'project_manager.ico'
self.project_manager_ui_file_path = self.ui_path / 'project_manager.ui'
self.project_manager_ui_file = QFile(self.project_manager_ui_file_path.as_posix())
self.project_manager_ui_file.open(QFile.ReadOnly)
loader = QUiLoader()
self.dialog = loader.load(self.project_manager_ui_file)
self.dialog.setWindowIcon(QIcon(self.project_manager_icon_file_path.as_posix()))
self.dialog.setFixedSize(self.dialog.size())
self.project_list_box = self.dialog.findChild(QComboBox, 'projectListBox')
self.refresh_project_list()
mru = self.get_mru_list()
if len(mru):
last_mru = pathlib.Path(mru[0]).resolve()
for this_slot in range(self.project_list_box.count()):
item_text = self.project_list_box.itemText(this_slot)
if last_mru.as_posix() in item_text:
self.project_list_box.setCurrentIndex(this_slot)
break
self.create_project_button = self.dialog.findChild(QPushButton, 'createProjectButton')
self.create_project_button.clicked.connect(self.create_project_handler)
self.create_gem_button = self.dialog.findChild(QPushButton, 'createGemButton')
self.create_gem_button.clicked.connect(self.create_gem_handler)
self.create_template_button = self.dialog.findChild(QPushButton, 'createTemplateButton')
self.create_template_button.clicked.connect(self.create_template_handler)
self.create_from_template_button = self.dialog.findChild(QPushButton, 'createFromTemplateButton')
self.create_from_template_button.clicked.connect(self.create_from_template_handler)
self.add_project_button = self.dialog.findChild(QPushButton, 'addProjectButton')
self.add_project_button.clicked.connect(self.add_project_handler)
self.add_gem_button = self.dialog.findChild(QPushButton, 'addGemButton')
self.add_gem_button.clicked.connect(self.add_gem_handler)
self.add_template_button = self.dialog.findChild(QPushButton, 'addTemplateButton')
self.add_template_button.clicked.connect(self.add_template_handler)
self.add_restricted_button = self.dialog.findChild(QPushButton, 'addRestrictedButton')
self.add_restricted_button.clicked.connect(self.add_restricted_handler)
self.remove_project_button = self.dialog.findChild(QPushButton, 'removeProjectButton')
self.remove_project_button.clicked.connect(self.remove_project_handler)
self.remove_gem_button = self.dialog.findChild(QPushButton, 'removeGemButton')
self.remove_gem_button.clicked.connect(self.remove_gem_handler)
self.remove_template_button = self.dialog.findChild(QPushButton, 'removeTemplateButton')
self.remove_template_button.clicked.connect(self.remove_template_handler)
self.remove_restricted_button = self.dialog.findChild(QPushButton, 'removeRestrictedButton')
self.remove_restricted_button.clicked.connect(self.remove_restricted_handler)
self.manage_project_gem_targets_button = self.dialog.findChild(QPushButton, 'manageRuntimeGemTargetsButton')
self.manage_project_gem_targets_button.clicked.connect(self.manage_project_gem_targets_handler)
self.log_display = self.dialog.findChild(QLabel, 'logDisplay')
self.ok_cancel_button = self.dialog.findChild(QDialogButtonBox, 'okCancel')
self.ok_cancel_button.accepted.connect(self.accepted_handler)
self.dialog.show()
def refresh_project_list(self) -> None:
projects = manifest.get_all_projects()
self.project_list_box.clear()
for this_slot in range(len(projects)):
display_name = f'{os.path.basename(os.path.normpath(projects[this_slot]))} ({projects[this_slot]})'
self.project_list_box.addItem(display_name)
self.project_list_box.setItemData(self.project_list_box.count() - 1, projects[this_slot],
Qt.ToolTipRole)
def accepted_handler(self) -> None:
"""
Override for handling "Ok" on main project dialog to first check whether the user has selected a project and
prompt them to if not. If a project is selected will attempt to open it.
:return: None
"""
if not self.project_list_box.currentText():
msg_box = QMessageBox(parent=self.dialog)
msg_box.setWindowTitle("O3DE")
msg_box.setText("Please select a project")
msg_box.exec()
return
self.launch_with_project_path(self.get_selected_project_path())
def get_launch_project(self) -> str:
return os.path.normpath(self.get_selected_project_path())
def get_executable_launch_params(self) -> list:
"""
Retrieve the necessary launch parameters to make the subprocess launch call with - this is the path
to the executable such as the Editor and the path to the selected project
:return: list of params
"""
launch_params = [executable_path,
f'-regset="/Amazon/AzCore/Bootstrap/project_path={self.get_launch_project()}"']
return launch_params
def launch_with_project_path(self, project_path: str) -> None:
"""
Launch the desired application given the selected project
:param project_path: Path to currently selected project
:return: None
"""
logger.info(f'Attempting to open {project_path}')
self.update_mru_list(project_path)
launch_params = self.get_executable_launch_params()
logger.info(f'Launching with params {launch_params}')
subprocess.run(launch_params, env=uninstall_env())
def get_selected_project_path(self) -> str:
if self.project_list_box.currentIndex() == -1:
logger.warning("No project selected")
return ""
return self.project_list_box.itemData(self.project_list_box.currentIndex(), Qt.ToolTipRole)
def get_selected_project_name(self) -> str:
project_data = manifest.get_project_json_data(project_path=self.get_selected_project_path())
return project_data['project_name']
def create_project_handler(self):
"""
Opens the Create Project pane. Retrieves a list of available templates for display
:return: None
"""
loader = QUiLoader()
self.create_project_file = QFile(self.create_project_ui_file_path.as_posix())
if not self.create_project_file:
logger.error(f'Failed to create project UI file at {self.create_project_file}')
return
self.create_project_dialog = loader.load(self.create_project_file)
if not self.create_project_dialog:
logger.error(f'Failed to load create project dialog file at {self.create_project_file}')
return
self.create_project_ok_button = self.create_project_dialog.findChild(QDialogButtonBox, 'okCancel')
self.create_project_ok_button.accepted.connect(self.create_project_accepted_handler)
self.create_project_template_list = self.create_project_dialog.findChild(QListView, 'projectTemplates')
self.refresh_create_project_template_list()
self.create_project_dialog.exec()
def create_project_accepted_handler(self) -> None:
"""
Searches the available gems list for selected gems and attempts to add each one to the current project.
Updates UI after completion.
:return: None
"""
selected_item = self.create_project_template_list.selectionModel().currentIndex()
project_template_path = self.create_project_template_list.model().data(selected_item)
if not project_template_path:
return
folder_dialog = QFileDialog(self.dialog, "Select a Folder and Enter a New Project Name",
manifest.get_o3de_projects_folder().as_posix())
folder_dialog.setFileMode(QFileDialog.AnyFile)
folder_dialog.setOptions(QFileDialog.ShowDirsOnly)
project_count = 0
project_name = "MyNewProject"
while os.path.exists(os.path.join(engine_path, project_name)):
project_name = f"MyNewProject{project_count}"
project_count += 1
folder_dialog.selectFile(project_name)
project_folder = None
if folder_dialog.exec():
project_folder = folder_dialog.selectedFiles()
if project_folder:
if engine_template.create_project(project_path=project_folder[0],
template_path=project_template_path) == 0:
# Success
register.register(project_path=project_folder[0])
self.refresh_project_list()
msg_box = QMessageBox(parent=self.dialog)
msg_box.setWindowTitle("O3DE")
msg_box.setText(f"Project {project_folder[0]} created.")
msg_box.exec()
return
def create_gem_handler(self):
"""
Opens the Create Gem pane. Retrieves a list of available templates for display
:return: None
"""
loader = QUiLoader()
self.create_gem_file = QFile(self.create_gem_ui_file_path.as_posix())
if not self.create_gem_file:
logger.error(f'Failed to create gem UI file at {self.create_gem_file}')
return
self.create_gem_dialog = loader.load(self.create_gem_file)
if not self.create_gem_dialog:
logger.error(f'Failed to load create gem dialog file at {self.create_gem_file}')
return
self.create_gem_ok_button = self.create_gem_dialog.findChild(QDialogButtonBox, 'okCancel')
self.create_gem_ok_button.accepted.connect(self.create_gem_accepted_handler)
self.create_gem_template_list = self.create_gem_dialog.findChild(QListView, 'gemTemplates')
self.refresh_create_gem_template_list()
self.create_gem_dialog.exec()
def create_gem_accepted_handler(self) -> None:
"""
Searches the available gems list for selected gems and attempts to add each one to the current gem.
Updates UI after completion.
:return: None
"""
selected_item = self.create_gem_template_list.selectionModel().currentIndex()
gem_template_path = self.create_gem_template_list.model().data(selected_item)
if not gem_template_path:
return
folder_dialog = QFileDialog(self.dialog, "Select a Folder and Enter a New Gem Name",
manifest.get_o3de_gems_folder().as_posix())
folder_dialog.setFileMode(QFileDialog.AnyFile)
folder_dialog.setOptions(QFileDialog.ShowDirsOnly)
gem_count = 0
gem_name = "MyNewGem"
while os.path.exists(os.path.join(engine_path, gem_name)):
gem_name = f"MyNewGem{gem_count}"
gem_count += 1
folder_dialog.selectFile(gem_name)
gem_folder = None
if folder_dialog.exec():
gem_folder = folder_dialog.selectedFiles()
if gem_folder:
if engine_template.create_gem(gem_path=gem_folder[0],
template_path=gem_template_path) == 0:
# Success
register.register(gem_path=gem_folder[0])
msg_box = QMessageBox(parent=self.dialog)
msg_box.setWindowTitle("O3DE")
msg_box.setText(f"Gem {gem_folder[0]} created.")
msg_box.exec()
return
def create_template_handler(self):
"""
Opens a foldr select dialog and lets the user select the source folder they want to make a template
out of, then opens a second folder select dialog to get where they want to put the template and it name
:return: None
"""
source_folder = QFileDialog.getExistingDirectory(self.dialog,
"Select a Folder to make a template out of.",
manifest.get_o3de_folder().as_posix())
if not source_folder:
return
destination_template_folder_dialog = QFileDialog(self.dialog,
"Select where the template is to be created and named.",
manifest.get_o3de_templates_folder().as_posix())
destination_template_folder_dialog.setFileMode(QFileDialog.AnyFile)
destination_template_folder_dialog.setOptions(QFileDialog.ShowDirsOnly)
destination_folder = None
if destination_template_folder_dialog.exec():
destination_folder = destination_template_folder_dialog.selectedFiles()
if not destination_folder:
return
if engine_template.create_template(source_path=source_folder,
template_path=destination_folder[0]) == 0:
# Success
register.register(template_path=destination_folder[0])
msg_box = QMessageBox(parent=self.dialog)
msg_box.setWindowTitle("O3DE")
msg_box.setText(f"Template {destination_folder[0]} created.")
msg_box.exec()
return
def create_from_template_handler(self):
"""
Opens the Create from_template pane. Retrieves a list of available from_templates for display
:return: None
"""
loader = QUiLoader()
self.create_from_template_file = QFile(self.create_from_template_ui_file_path.as_posix())
if not self.create_from_template_file:
logger.error(f'Failed to create from_template UI file at {self.create_from_template_file}')
return
self.create_from_template_dialog = loader.load(self.create_from_template_file)
if not self.create_from_template_dialog:
logger.error(f'Failed to load create from_template dialog file at {self.create_from_template_file}')
return
self.create_from_template_ok_button = self.create_from_template_dialog.findChild(QDialogButtonBox, 'okCancel')
self.create_from_template_ok_button.accepted.connect(self.create_from_template_accepted_handler)
self.create_from_template_list = self.create_from_template_dialog.findChild(QListView, 'genericTemplates')
self.refresh_create_from_template_list()
self.create_from_template_dialog.exec()
def create_from_template_accepted_handler(self) -> None:
"""
Searches the available gems list for selected gems and attempts to add each one to the current gem.
Updates UI after completion.
:return: None
"""
create_gem_item = self.get_selected_gem_template()
if not create_gem_item:
return
folder_dialog = QFileDialog(self.dialog, "Select a Folder and Enter a New Gem Name",
manifest.get_o3de_gems_folder().as_posix())
folder_dialog.setFileMode(QFileDialog.AnyFile)
folder_dialog.setOptions(QFileDialog.ShowDirsOnly)
gem_count = 0
gem_name = "MyNewGem"
while os.path.exists(os.path.join(engine_path, gem_name)):
gem_name = f"MyNewGem{gem_count}"
gem_count += 1
folder_dialog.selectFile(gem_name)
gem_folder = None
if folder_dialog.exec():
gem_folder = folder_dialog.selectedFiles()
if gem_folder:
if engine_template.create_gem(gem_folder[0], create_gem_item[1]) == 0:
# Success
msg_box = QMessageBox(parent=self.dialog)
msg_box.setWindowTitle("O3DE")
msg_box.setText(f"gem {os.path.basename(os.path.normpath(gem_folder[0]))} created."
" Build your\nnew gem before hitting OK to launch.")
msg_box.exec()
return
def add_project_handler(self):
"""
Open a file search dialog looking for a folder which contains a valid project. If valid
will update the mru list with the new entry, if invalid will warn the user.
:return: None
"""
project_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Project Folder",
manifest.get_o3de_projects_folder().as_posix())
if project_folder:
if register.register(project_path=project_folder) == 0:
# Success
self.refresh_project_list()
msg_box = QMessageBox(parent=self.dialog)
msg_box.setWindowTitle("O3DE")
msg_box.setText(f"Added Project {project_folder}.")
msg_box.exec()
return
def add_gem_handler(self):
"""
Open a file search dialog looking for a folder which contains a gem. If valid
will update the mru list with the new entry, if invalid will warn the user.
:return: None
"""
gem_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Gem Folder",
manifest.get_o3de_gems_folder().as_posix())
if gem_folder:
if register.register(gem_path=gem_folder) == 0:
# Success
msg_box = QMessageBox(parent=self.dialog)
msg_box.setWindowTitle("O3DE")
msg_box.setText(f"Added Gem {gem_folder}.")
msg_box.exec()
return
def add_template_handler(self):
"""
Open a file search dialog looking for a folder which contains a valid template. If valid
will update the mru list with the new entry, if invalid will warn the user.
:return: None
"""
template_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Template Folder",
manifest.get_o3de_templates_folder().as_posix())
if template_folder:
if register.register(template_path=template_folder) == 0:
# Success
msg_box = QMessageBox(parent=self.dialog)
msg_box.setWindowTitle("O3DE")
msg_box.setText(f"Added Template {template_folder}.")
msg_box.exec()
return
def add_restricted_handler(self):
"""
Open a file search dialog looking for a folder which contains a valid template. If valid
will update the mru list with the new entry, if invalid will warn the user.
:return: None
"""
restricted_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Restricted Folder",
manifest.get_o3de_restricted_folder().as_posix())
if restricted_folder:
if register.register(restricted_path=restricted_folder) == 0:
# Success
msg_box = QMessageBox(parent=self.dialog)
msg_box.setWindowTitle("O3DE")
msg_box.setText(f"Added Restricted {restricted_folder}.")
msg_box.exec()
return
def remove_project_handler(self):
"""
Open a file search dialog looking for a folder which contains a valid project. If valid
will update the mru list with the new entry, if invalid will warn the user.
:return: None
"""
project_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Project Folder",
manifest.get_o3de_projects_folder().as_posix())
if project_folder:
if register.register(project_path=project_folder, remove=True) == 0:
# Success
self.refresh_project_list()
msg_box = QMessageBox(parent=self.dialog)
msg_box.setWindowTitle("O3DE")
msg_box.setText(f"Removed Project {project_folder}.")
msg_box.exec()
return
def remove_gem_handler(self):
"""
Open a file search dialog looking for a folder which contains a gem. If valid
will update the mru list with the new entry, if invalid will warn the user.
:return: None
"""
gem_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Gem Folder",
manifest.get_o3de_gems_folder().as_posix())
if gem_folder:
if register.register(gem_path=gem_folder, remove=True) == 0:
# Success
msg_box = QMessageBox(parent=self.dialog)
msg_box.setWindowTitle("O3DE")
msg_box.setText(f"Removed Gem {gem_folder}.")
msg_box.exec()
return
def remove_template_handler(self):
"""
Open a file search dialog looking for a folder which contains a valid template. If valid
will update the mru list with the new entry, if invalid will warn the user.
:return: None
"""
template_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Template Folder",
manifest.get_o3de_templates_folder().as_posix())
if template_folder:
if register.register(template_path=template_folder, remove=True) == 0:
# Success
msg_box = QMessageBox(parent=self.dialog)
msg_box.setWindowTitle("O3DE")
msg_box.setText(f"Removed Template {template_folder}.")
msg_box.exec()
return
def remove_restricted_handler(self):
"""
Open a file search dialog looking for a folder which contains a valid template. If valid
will update the mru list with the new entry, if invalid will warn the user.
:return: None
"""
restricted_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Restricted Folder",
manifest.get_o3de_restricted_folder().as_posix())
if restricted_folder:
if register.register(restricted_path=restricted_folder, remove=True) == 0:
# Success
msg_box = QMessageBox(parent=self.dialog)
msg_box.setWindowTitle("O3DE")
msg_box.setText(f"Removed Restricted {restricted_folder}.")
msg_box.exec()
return
def manage_project_gem_targets_handler(self):
"""
Opens the Gem management pane. Waits for the load thread to complete if still running and displays all
active gems for the current project as well as all available gems which aren't currently active.
:return: None
"""
if not self.get_selected_project_path():
msg_box = QMessageBox(parent=self.dialog)
msg_box.setWindowTitle("O3DE")
msg_box.setText("Please select a project")
msg_box.exec()
return
loader = QUiLoader()
self.manage_project_gem_targets_file = QFile(self.manage_project_gem_targets_ui_file_path.as_posix())
if not self.manage_project_gem_targets_file:
logger.error(f'Failed to load manage gem targets UI file at {self.manage_project_gem_targets_ui_file_path}')
return
self.manage_project_gem_targets_dialog = loader.load(self.manage_project_gem_targets_file)
if not self.manage_project_gem_targets_dialog:
logger.error(f'Failed to load gems dialog file at {self.manage_project_gem_targets_ui_file_path.as_posix()}')
return
self.manage_project_gem_targets_dialog.setWindowTitle(f"Manage Gems for Project:"
f" {self.get_selected_project_name()}")
self.add_gem_button = self.manage_project_gem_targets_dialog.findChild(QPushButton, 'addGemTargetsButton')
self.add_gem_button.clicked.connect(self.add_project_gem_targets_handler)
self.available_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView,
'availableGemTargetsList')
self.refresh_project_gem_targets_available_list()
self.remove_project_gem_targets_button = self.manage_project_gem_targets_dialog.findChild(QPushButton,
'removeGemTargetsButton')
self.remove_project_gem_targets_button.clicked.connect(self.remove_project_gem_targets_handler)
self.enabled_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView,
'enabledGemTargetsList')
self.refresh_project_gem_targets_enabled_list()
self.manage_project_gem_targets_dialog.exec()
def manage_project_gem_targets_get_selected_available_gems(self) -> list:
selected_items = self.available_gem_targets_list.selectionModel().selectedRows()
return [(self.available_gem_targets_list.model().data(item)) for item in selected_items]
def manage_project_gem_targets_get_selected_enabled_gems(self) -> list:
selected_items = self.enabled_gem_targets_list.selectionModel().selectedRows()
return [(self.enabled_gem_targets_list.model().data(item)) for item in selected_items]
def add_project_gem_targets_handler(self) -> None:
gem_paths = manifest.get_all_gems()
for gem_target in self.manage_project_gem_targets_get_selected_available_gems():
for gem_path in gem_paths:
enable_gem.enable_gem_in_project(gem_path=gem_path,
project_path=self.get_selected_project_path())
self.refresh_project_gem_targets_available_list()
self.refresh_project_gem_targets_enabled_list()
return
self.refresh_project_gem_targets_available_list()
self.refresh_project_gem_targets_enabled_list()
def remove_project_gem_targets_handler(self):
gem_paths = manifest.get_all_gems()
for gem_target in self.manage_project_gem_targets_get_selected_enabled_gems():
for gem_path in gem_paths:
disable_gem.disable_gem_in_project(gem_path=gem_path,
project_path=self.get_selected_project_path())
self.refresh_project_gem_targets_available_list()
self.refresh_project_gem_targets_enabled_list()
return
self.refresh_project_gem_targets_available_list()
self.refresh_project_gem_targets_enabled_list()
def refresh_project_gem_targets_enabled_list(self) -> None:
enabled_project_gem_targets_model = QStandardItemModel()
enabled_project_gems = cmake.get_project_gems(project_path=self.get_selected_project_path())
for gem_target in sorted(enabled_project_gems):
model_item = QStandardItem(gem_target)
enabled_project_gem_targets_model.appendRow(model_item)
self.enabled_gem_targets_list.setModel(enabled_project_gem_targets_model)
def refresh_project_gem_targets_available_list(self) -> None:
available_project_gem_targets_model = QStandardItemModel()
enabled_project_gem_targets = cmake.get_project_gems(project_path=self.get_selected_project_path())
all_gem_targets = manifest.get_all_gems()
for gem_target in sorted(all_gem_targets):
if gem_target not in enabled_project_gem_targets:
model_item = QStandardItem(gem_target)
available_project_gem_targets_model.appendRow(model_item)
self.available_gem_targets_list.setModel(available_project_gem_targets_model)
def refresh_create_project_template_list(self) -> None:
self.create_project_template_model = QStandardItemModel()
for project_template_path in manifest.get_project_templates():
model_item = QStandardItem(project_template_path)
self.create_project_template_model.appendRow(model_item)
self.create_project_template_list.setModel(self.create_project_template_model)
def refresh_create_gem_template_list(self) -> None:
self.create_gem_template_model = QStandardItemModel()
for gem_template_path in manifest.get_gem_templates():
model_item = QStandardItem(gem_template_path)
self.create_gem_template_model.appendRow(model_item)
self.create_gem_template_list.setModel(self.create_gem_template_model)
def refresh_create_from_template_list(self) -> None:
self.create_from_template_model = QStandardItemModel()
for generic_template_path in manifest.get_generic_templates():
model_item = QStandardItem(generic_template_path)
self.create_from_template_model.appendRow(model_item)
self.create_from_template_list.setModel(self.create_from_template_model)
def update_mru_list(self, used_project: str) -> None:
"""
Promote a supplied project name to the "most recent" in a given MRU list.
:param used_project: path to project to promote
:param file_path: path to mru list file
:return: None
"""
used_project = os.path.normpath(used_project)
if not os.path.exists(os.path.dirname(self.mru_file_path)):
os.makedirs(os.path.dirname(self.mru_file_path), exist_ok=True)
mru_data = {}
try:
with open(self.mru_file_path, 'r') as mru_file:
mru_data = json.loads(mru_file.read())
except FileNotFoundError:
pass
except json.JSONDecodeError:
pass
recent_list = mru_data.get('Projects', [])
recent_list = [item for item in recent_list if item.get('Path') != used_project and
self.is_project_folder(item.get('Path'))]
new_list = [{'Path': used_project}]
new_list.extend(recent_list)
mru_data['Projects'] = new_list
try:
with open(self.mru_file_path, 'w') as mru_file:
mru_file.write(json.dumps(mru_data, indent=1))
except PermissionError as e:
logger.warning(f"Failed to write {self.mru_file_path} with error {e}")
def get_mru_list(self) -> List[str]:
"""
Retrieve the current MRU list. Does not perform validation that the projects still appear valid
:return: list of full path strings to project folders
"""
if not os.path.exists(os.path.dirname(self.mru_file_path)):
return []
try:
with open(self.mru_file_path, 'r') as mru_file:
mru_data = json.loads(mru_file.read())
except FileNotFoundError:
return []
except json.JSONDecodeError:
logger.error(f'MRU list at {self.mru_file_path} is not valid JSON')
return []
recent_list = mru_data.get('Projects', [])
return [item.get('Path') for item in recent_list if item.get('Path') is not None]
@Slot(str)
def handle_log_message(self, message: str) -> None:
"""
Signal handler for messages from the logger. Displays the most recent warning/error
:param message: formatted log message from DialogLoggerSignaller
:return:
"""
if not self.log_display:
return
self.log_display.setText(message)
self.log_display.setToolTip(message)
if __name__ == "__main__":
dialog_app = QApplication(sys.argv)
my_dialog = ProjectManagerDialog()
dialog_app.exec_()
sys.exit(0)

@ -1,61 +0,0 @@
#
# 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.
#
import os
import logging
from pathlib import Path
logger = logging.getLogger()
pyside_initialized = False
old_env = os.environ.copy()
# Helper to extend OS PATH for pyside to locate our QT binaries based on our build folder
def add_pyside_environment(bin_path):
if is_pyside_ready():
# No need to reinitialize currently
logger.info("Pyside environment already initialized")
return
global old_env
old_env = os.environ.copy()
binaries_path = Path(os.path.normpath(bin_path))
platforms_path = binaries_path.joinpath("platforms")
logger.info(f'Adding binaries path {binaries_path}')
os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = str(platforms_path)
path = os.environ['PATH']
new_path = os.pathsep.join([str(binaries_path), str(platforms_path), path])
os.environ['PATH'] = new_path
global pyside_initialized
pyside_initialized = True
def is_pyside_ready():
return pyside_initialized
def is_configuration_valid(workspace):
return os.path.basename(workspace.paths.build_directory()) != "debug"
def uninstall_env():
if not is_pyside_ready():
logger.warning("Pyside not initialized")
return os.environ
global old_env
if old_env.get("QT_QPA_PLATFORM_PLUGIN_PATH"):
old_env.pop("QT_QPA_PLATFORM_PLUGIN_PATH")
os.environ = old_env
return old_env

@ -1,10 +0,0 @@
#
# 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.
#

@ -1,255 +0,0 @@
#
# 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.
#
import pytest
'''
import os
import sys
import tempfile
import logging
import pathlib
from unittest.mock import MagicMock
logger = logging.getLogger()
# Code lives one folder above
project_manager_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.append(project_manager_path)
from pyside import add_pyside_environment, is_configuration_valid
from ly_test_tools import WINDOWS
sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..', '..')))
executable_path = ''
from cmake.Tools import registration
from cmake.Tools import engine_template
class ProjectHelper:
def __init__(self):
self._temp_directory = pathlib.Path(tempfile.TemporaryDirectory().name).resolve()
self._temp_directory.mkdir(parents=True, exist_ok=True)
self.home_path = self._temp_directory
registration.override_home_folder = self.home_path
self.engine_path = registration.get_this_engine_path()
if registration.register(engine_path=self.engine_path):
assert True, f"Failed to register the engine."
if registration.register_shipped_engine_o3de_objects():
assert True, f"Failed to register shipped engine objects."
self.projects_folder = registration.get_o3de_projects_folder()
if not self.projects_folder.is_dir():
assert True
self.application = None
self.dialog = None
def create_empty_projects(self):
self.project_1_dir = self.projects_folder / "Project1"
if engine_template.create_project(project_manager_path=self.project_1_dir):
assert True, f"Failed to create Project1."
self.project_2_dir = self.projects_folder / "Project2"
if engine_template.create_project(project_manager_path=self.project_2_dir):
assert True, f"Failed to create Project2."
self.project_3_dir = self.projects_folder / "Project3"
if engine_template.create_project(project_manager_path=self.project_3_dir):
assert True, f"Failed to create Project3."
self.invalid_project_dir = self.projects_folder / "InvalidProject"
self.invalid_project_dir.mkdir(parents=True, exist_ok=True)
def setup_dialog_test(self, workspace):
add_pyside_environment(workspace.paths.build_directory())
if not is_configuration_valid(workspace):
# This is essentially skipif debug. Our debug tests use our profile version of python, but that means we'd
# need to use the profile version of PySide which works with the profile QT libs which aren't in the debug
# folder we've built.
return None
from PySide2.QtWidgets import QApplication, QMessageBox
if QApplication.instance():
self.application = QApplication.instance()
else:
self.application = QApplication(sys.argv)
assert self.application
from projects import ProjectManagerDialog
try:
self.dialog = ProjectManagerDialog(settings_folder=self.home_path)
return self.dialog
except Exception as e:
logger.error(f'Failed to create ProjectManagerDialog with error {e}')
return None
def create_project_from_template(self, project_name) -> bool:
"""
Uses the dialog to create a temporary project based on the first template found
:param project_name: Name of project to create. Will be created under temp_project_root
:return: True for Success, False for failure
"""
from PySide2.QtWidgets import QWidget, QFileDialog
from projects import ProjectManagerDialog
QWidget.exec = MagicMock()
self.dialog.create_project_handler()
QWidget.exec.assert_called_once()
assert len(self.dialog.project_templates), 'Failed to find any project templates'
ProjectManagerDialog.get_selected_project_template = MagicMock(return_value=self.dialog.project_templates[0])
QFileDialog.exec = MagicMock()
create_project_path = self.projects_folder / project_name
QFileDialog.selectedFiles = MagicMock(return_value=[create_project_path])
self.dialog.create_project_accepted_handler()
if create_project_path.is_dir():
assert True, f"Expected project creation folder not found at {create_project_path}"
if QWidget.exec.call_count == 2:
assert True, "Message box confirming project creation failed to show"
@pytest.fixture
def project_helper():
return ProjectHelper()
@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently")
@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent
def test_logger_handler(workspace, project_helper):
my_dialog = project_helper.setup_dialog_test(workspace)
if not my_dialog:
return
from PySide2.QtWidgets import QMessageBox
QMessageBox.warning = MagicMock()
logger.error(f'Testing logger')
QMessageBox.warning.assert_called_once()
@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently")
@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent
def test_mru_list(workspace, project_helper):
my_dialog = project_helper.setup_dialog_test(workspace)
if not my_dialog:
return
project_helper.create_empty_projects()
from PySide2.QtWidgets import QMessageBox
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 0, f'MRU list unexpectedly had entries: {mru_list}'
QMessageBox.warning = MagicMock()
my_dialog.add_project(project_helper.invalid_project_dir)
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 0, f'MRU list unexpectedly added an invalid project : {mru_list}'
QMessageBox.warning.assert_called_once()
my_dialog.add_project(project_helper.project_1_dir)
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 1, f'MRU list failed to add project at {project_helper.project_1_dir}'
my_dialog.add_project(project_helper.project_1_dir)
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 1, f'MRU list added project at {project_helper.project_1_dir} a second time : {mru_list}'
my_dialog.update_mru_list(project_helper.project_1_dir)
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 1, f'MRU list added project at {project_helper.project_1_dir} a second time : {mru_list}'
my_dialog.add_project(project_helper.project_2_dir)
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 2, f'MRU list failed to add project at {project_helper.project_2_dir}'
assert mru_list[0] == project_helper.project_2_dir, f"{project_helper.project_2_dir} wasn't first item"
assert mru_list[1] == project_helper.project_1_dir, f"{project_helper.project_1_dir} wasn't second item"
my_dialog.update_mru_list(project_helper.project_1_dir)
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 2, f'MRU list added wrong items {mru_list}'
assert mru_list[0] == project_helper.project_1_dir, f"{project_helper.project_1_dir} wasn't first item"
assert mru_list[1] == project_helper.project_2_dir, f"{project_helper.project_2_dir} wasn't second item"
my_dialog.add_project(project_helper.invalid_project_dir)
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 2, f'MRU list added invalid item {mru_list}'
assert mru_list[0] == project_helper.project_1_dir, f"{project_helper.project_1_dir} wasn't first item"
assert mru_list[1] == project_helper.project_2_dir, f"{project_helper.project_2_dir} wasn't second item"
my_dialog.add_project(project_helper.project_3_dir)
mru_list = my_dialog.get_mru_list()
assert len(mru_list) == 3, f'MRU list failed to add {project_helper.project_3_dir} : {mru_list}'
assert mru_list[0] == project_helper.project_3_dir, f"{project_helper.project_3_dir} wasn't first item"
assert mru_list[1] == project_helper.project_1_dir, f"{project_helper.project_1_dir} wasn't second item"
assert mru_list[2] == project_helper.project_2_dir, f"{project_helper.project_2_dir} wasn't third item"
@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently")
@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent
def test_create_project(workspace, project_helper):
my_dialog = project_helper.setup_dialog_test(workspace)
if not my_dialog:
return
project_helper.create_project_from_template("TestCreateProject")
@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently")
@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent
def test_add_remove_gems(workspace, project_helper):
my_dialog = project_helper.setup_dialog_test(workspace)
if not my_dialog:
return
my_project_name = "TestAddRemoveGems"
project_helper.create_project_from_template(project_manager_path=my_project_name)
my_project_path = project_helper.projects_folder / my_project_name
from PySide2.QtWidgets import QWidget, QFileDialog
from projects import ProjectManagerDialog
assert my_dialog.get_selected_project_path() == my_project_path, "TestAddRemoveGems project not selected"
QWidget.exec = MagicMock()
my_dialog.manage_gems_handler()
assert my_dialog.manage_gem_targets_dialog, "No gem management dialog created"
QWidget.exec.assert_called_once()
if not len(my_dialog.all_gems_list):
assert True, 'Failed to find any gems'
my_test_gem_path = my_dialog.all_gems_list[0]
gem_data = registration.get_gem_data(my_test_gem_path)
my_test_gem_selection = (my_test_gem_name, my_test_gem_path)
ProjectManagerDialog.get_selected_add_gems = MagicMock(return_value=[my_test_gem_selection])
assert my_test_gem_name, "No Name set in test gem"
assert my_test_gem_name not in my_dialog.project_gem_list, f'Gem {my_test_gem_name} already in project gem list'
my_dialog.add_gems_handler()
assert my_test_gem_name in my_dialog.project_gem_list, f'Gem {my_test_gem_name} failed to add to gem list'
ProjectManagerDialog.get_selected_project_gems = MagicMock(return_value=[my_test_gem_name])
my_dialog.remove_gems_handler()
assert my_test_gem_name not in my_dialog.project_gem_list, f'Gem {my_test_gem_name} still in project gem list'
'''
def test_project_place_holder():
pass

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save