merge stabilization/2110 to development - 2021/11/09
Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com>
This commit is contained in:
+11
-8
@@ -50,6 +50,7 @@ def NvCloth_AddClothSimulationToActor():
|
||||
|
||||
# Constants
|
||||
FRAMES_IN_GAME_MODE = 200
|
||||
CLOTH_GEM_ERROR_WARNING_LIST = ["Cloth", "NvCloth", "ClothComponentMesh", "ActorClothSkinning", "ActorClothSkinning", "TangentSpaceHelper", "MeshAssetHelper", "ActorAssetHelper", "ClothDebugDisplay"]
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load the level
|
||||
@@ -64,14 +65,16 @@ def NvCloth_AddClothSimulationToActor():
|
||||
general.idle_wait_frames(FRAMES_IN_GAME_MODE)
|
||||
|
||||
# 5) Verify there are no errors and warnings in the logs
|
||||
success_condition = not (section_tracer.has_errors or section_tracer.has_warnings)
|
||||
Report.result(Tests.no_errors_and_warnings_found, success_condition)
|
||||
if not success_condition:
|
||||
if section_tracer.has_warnings:
|
||||
Report.info(f"Warnings found: {section_tracer.warnings}")
|
||||
if section_tracer.has_errors:
|
||||
Report.info(f"Errors found: {section_tracer.errors}")
|
||||
Report.failure(Tests.no_errors_and_warnings_found)
|
||||
has_errors_or_warnings = False
|
||||
for error_msg in section_tracer.errors:
|
||||
if error_msg.window in CLOTH_GEM_ERROR_WARNING_LIST:
|
||||
has_errors_or_warnings = True
|
||||
Report.info(f"Cloth error found: {error_msg}")
|
||||
for warning_msg in section_tracer.warnings:
|
||||
if warning_msg.window in CLOTH_GEM_ERROR_WARNING_LIST:
|
||||
has_errors_or_warnings = True
|
||||
Report.info(f"Cloth warning found: {warning_msg}")
|
||||
Report.result(Tests.no_errors_and_warnings_found, not has_errors_or_warnings)
|
||||
|
||||
# 6) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
@@ -50,6 +50,7 @@ def NvCloth_AddClothSimulationToMesh():
|
||||
|
||||
# Constants
|
||||
FRAMES_IN_GAME_MODE = 200
|
||||
CLOTH_GEM_ERROR_WARNING_LIST = ["Cloth", "NvCloth", "ClothComponentMesh", "ActorClothSkinning", "ActorClothSkinning", "TangentSpaceHelper", "MeshAssetHelper", "ActorAssetHelper", "ClothDebugDisplay"]
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load the level
|
||||
@@ -64,14 +65,16 @@ def NvCloth_AddClothSimulationToMesh():
|
||||
general.idle_wait_frames(FRAMES_IN_GAME_MODE)
|
||||
|
||||
# 5) Verify there are no errors and warnings in the logs
|
||||
success_condition = not (section_tracer.has_errors or section_tracer.has_warnings)
|
||||
Report.result(Tests.no_errors_and_warnings_found, success_condition)
|
||||
if not success_condition:
|
||||
if section_tracer.has_warnings:
|
||||
Report.info(f"Warnings found: {section_tracer.warnings}")
|
||||
if section_tracer.has_errors:
|
||||
Report.info(f"Errors found: {section_tracer.errors}")
|
||||
Report.failure(Tests.no_errors_and_warnings_found)
|
||||
has_errors_or_warnings = False
|
||||
for error_msg in section_tracer.errors:
|
||||
if error_msg.window in CLOTH_GEM_ERROR_WARNING_LIST:
|
||||
has_errors_or_warnings = True
|
||||
Report.info(f"Cloth error found: {error_msg}")
|
||||
for warning_msg in section_tracer.warnings:
|
||||
if warning_msg.window in CLOTH_GEM_ERROR_WARNING_LIST:
|
||||
has_errors_or_warnings = True
|
||||
Report.info(f"Cloth warning found: {warning_msg}")
|
||||
Report.result(Tests.no_errors_and_warnings_found, not has_errors_or_warnings)
|
||||
|
||||
# 6) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
@@ -13,9 +13,32 @@
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzFramework/Render/IntersectorInterface.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
|
||||
#include <EditorViewportSettings.h>
|
||||
|
||||
AZ_CVAR(
|
||||
bool,
|
||||
ed_cameraPinDefaultOrbit,
|
||||
true,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"Sets whether the default orbit point moves with the camera or not");
|
||||
AZ_CVAR(
|
||||
bool,
|
||||
ed_cameraDefaultOrbitAxesOrtho,
|
||||
true,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"Sets whether to draw the default orbit point as orthographic or not");
|
||||
AZ_CVAR(
|
||||
float,
|
||||
ed_cameraDefaultOrbitFadeDuration,
|
||||
0.5f,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"Sets how long the default orbit point should take to appear and disappear");
|
||||
|
||||
namespace SandboxEditor
|
||||
{
|
||||
static AzFramework::TranslateCameraInputChannelIds BuildTranslateCameraInputChannelIds()
|
||||
@@ -174,7 +197,7 @@ namespace SandboxEditor
|
||||
return SandboxEditor::CameraScrollSpeed();
|
||||
};
|
||||
|
||||
const auto pivotFn = []
|
||||
const auto pivotFn = []() -> AZStd::optional<AZ::Vector3>
|
||||
{
|
||||
// use the manipulator transform as the pivot point
|
||||
AZStd::optional<AZ::Transform> entityPivot;
|
||||
@@ -187,8 +210,7 @@ namespace SandboxEditor
|
||||
return entityPivot->GetTranslation();
|
||||
}
|
||||
|
||||
// otherwise just use the identity
|
||||
return AZ::Vector3::CreateZero();
|
||||
return AZStd::nullopt;
|
||||
};
|
||||
|
||||
m_firstPersonFocusCamera =
|
||||
@@ -199,9 +221,26 @@ namespace SandboxEditor
|
||||
m_orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>(SandboxEditor::CameraOrbitChannelId());
|
||||
|
||||
m_orbitCamera->SetPivotFn(
|
||||
[pivotFn]([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
|
||||
[this, pivotFn](const AZ::Vector3& position, const AZ::Vector3& direction)
|
||||
{
|
||||
return pivotFn();
|
||||
// return the pivot
|
||||
if (auto pivot = pivotFn())
|
||||
{
|
||||
return pivot.value();
|
||||
}
|
||||
|
||||
// start ticking and drawing (for the default pivot)
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
AzFramework::ViewportDebugDisplayEventBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId());
|
||||
|
||||
m_defaultOrbiting = true;
|
||||
// calculate the default orbit point
|
||||
if (!ed_cameraPinDefaultOrbit || m_orbitCamera->Beginning())
|
||||
{
|
||||
m_defaultOrbitPoint = position + direction * SandboxEditor::CameraDefaultOrbitDistance();
|
||||
}
|
||||
|
||||
return m_defaultOrbitPoint;
|
||||
});
|
||||
|
||||
m_orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(SandboxEditor::CameraOrbitLookChannelId());
|
||||
@@ -306,4 +345,67 @@ namespace SandboxEditor
|
||||
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorModularViewportCameraComposer::OnTick(const float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
const float delta = [duration = &ed_cameraDefaultOrbitFadeDuration, deltaTime] {
|
||||
if (*duration == 0.0f) {
|
||||
return 1.0f;
|
||||
}
|
||||
return deltaTime / *duration;
|
||||
}();
|
||||
|
||||
if (m_defaultOrbiting)
|
||||
{
|
||||
m_defaultOrbitOpacity = AZStd::min(m_defaultOrbitOpacity + delta, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_defaultOrbitOpacity = AZStd::max(m_defaultOrbitOpacity - delta, 0.0f);
|
||||
if (m_defaultOrbitOpacity == 0.0f)
|
||||
{
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
AzFramework::ViewportDebugDisplayEventBus::Handler::BusDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
m_defaultOrbiting = false;
|
||||
}
|
||||
|
||||
static void DrawTransformAxis(
|
||||
AzFramework::DebugDisplayRequests& display,
|
||||
const AzFramework::CameraState& cameraState,
|
||||
const AZ::Vector3& pivot,
|
||||
const float axisLength,
|
||||
const float alpha)
|
||||
{
|
||||
const int prevState = display.GetState();
|
||||
|
||||
display.DepthWriteOff();
|
||||
display.DepthTestOff();
|
||||
display.CullOff();
|
||||
|
||||
const float orthoScale =
|
||||
ed_cameraDefaultOrbitAxesOrtho ? AzToolsFramework::CalculateScreenToWorldMultiplier(pivot, cameraState) : 1.0f;
|
||||
|
||||
display.SetColor(AZ::Color::CreateFromVector3AndFloat(AZ::Colors::Red.GetAsVector3(), alpha));
|
||||
display.DrawLine(pivot, pivot + AZ::Vector3::CreateAxisX() * axisLength * orthoScale);
|
||||
display.SetColor(AZ::Color::CreateFromVector3AndFloat(AZ::Colors::LawnGreen.GetAsVector3(), alpha));
|
||||
display.DrawLine(pivot, pivot + AZ::Vector3::CreateAxisY() * axisLength * orthoScale);
|
||||
display.SetColor(AZ::Color::CreateFromVector3AndFloat(AZ::Colors::Blue.GetAsVector3(), alpha));
|
||||
display.DrawLine(pivot, pivot + AZ::Vector3::CreateAxisZ() * axisLength * orthoScale);
|
||||
|
||||
display.DepthWriteOn();
|
||||
display.DepthTestOn();
|
||||
display.CullOn();
|
||||
|
||||
display.SetState(prevState);
|
||||
}
|
||||
|
||||
void EditorModularViewportCameraComposer::DisplayViewport(
|
||||
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
|
||||
{
|
||||
DrawTransformAxis(
|
||||
debugDisplay, AzToolsFramework::GetCameraState(viewportInfo.m_viewportId), m_defaultOrbitPoint, 1.0f, m_defaultOrbitOpacity);
|
||||
}
|
||||
} // namespace SandboxEditor
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
#include <AzFramework/Viewport/CameraInput.h>
|
||||
#include <AzToolsFramework/API/EditorCameraBus.h>
|
||||
#include <EditorModularViewportCameraComposerBus.h>
|
||||
@@ -20,6 +22,8 @@ namespace SandboxEditor
|
||||
class EditorModularViewportCameraComposer
|
||||
: private EditorModularViewportCameraComposerNotificationBus::Handler
|
||||
, private Camera::EditorCameraNotificationBus::Handler
|
||||
, private AzFramework::ViewportDebugDisplayEventBus::Handler
|
||||
, private AZ::TickBus::Handler
|
||||
{
|
||||
public:
|
||||
SANDBOX_API explicit EditorModularViewportCameraComposer(AzFramework::ViewportId viewportId);
|
||||
@@ -29,6 +33,12 @@ namespace SandboxEditor
|
||||
SANDBOX_API AZStd::shared_ptr<AtomToolsFramework::ModularViewportCameraController> CreateModularViewportCameraController();
|
||||
|
||||
private:
|
||||
// AzFramework::ViewportDebugDisplayEventBus overrides ...
|
||||
void DisplayViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
|
||||
|
||||
// AZ::TickBus overrides ...
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
|
||||
//! Setup all internal camera inputs.
|
||||
void SetupCameras();
|
||||
|
||||
@@ -52,5 +62,9 @@ namespace SandboxEditor
|
||||
AZStd::shared_ptr<AzFramework::FocusCameraInput> m_orbitFocusCamera;
|
||||
|
||||
AzFramework::ViewportId m_viewportId;
|
||||
|
||||
float m_defaultOrbitOpacity = 0.0f; //!< The default orbit axes opacity (to fade in and out).
|
||||
AZ::Vector3 m_defaultOrbitPoint = AZ::Vector3::CreateZero(); //!< The orbit point to use when no entity is selected.
|
||||
bool m_defaultOrbiting = false; //!< Is the camera default orbiting (orbiting when there's no selected entity).
|
||||
};
|
||||
} // namespace SandboxEditor
|
||||
|
||||
@@ -61,7 +61,7 @@ static AZStd::vector<AZStd::string> GetEditorInputNames()
|
||||
void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serialize)
|
||||
{
|
||||
serialize.Class<CameraMovementSettings>()
|
||||
->Version(3)
|
||||
->Version(4)
|
||||
->Field("TranslateSpeed", &CameraMovementSettings::m_translateSpeed)
|
||||
->Field("RotateSpeed", &CameraMovementSettings::m_rotateSpeed)
|
||||
->Field("BoostMultiplier", &CameraMovementSettings::m_boostMultiplier)
|
||||
@@ -76,9 +76,8 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
|
||||
->Field("OrbitYawRotationInverted", &CameraMovementSettings::m_orbitYawRotationInverted)
|
||||
->Field("PanInvertedX", &CameraMovementSettings::m_panInvertedX)
|
||||
->Field("PanInvertedY", &CameraMovementSettings::m_panInvertedY)
|
||||
->Field("DefaultPositionX", &CameraMovementSettings::m_defaultCameraPositionX)
|
||||
->Field("DefaultPositionY", &CameraMovementSettings::m_defaultCameraPositionY)
|
||||
->Field("DefaultPositionZ", &CameraMovementSettings::m_defaultCameraPositionZ);
|
||||
->Field("DefaultPosition", &CameraMovementSettings::m_defaultPosition)
|
||||
->Field("DefaultOrbitDistance", &CameraMovementSettings::m_defaultOrbitDistance);
|
||||
|
||||
serialize.Class<CameraInputSettings>()
|
||||
->Version(2)
|
||||
@@ -159,14 +158,12 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
|
||||
AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_captureCursorLook, "Camera Capture Look Cursor",
|
||||
"Should the cursor be captured (hidden) while performing free look")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionX, "Default X Camera Position",
|
||||
"Default X Camera Position when a level is opened")
|
||||
AZ::Edit::UIHandlers::Vector3, &CameraMovementSettings::m_defaultPosition, "Default Camera Position",
|
||||
"Default Camera Position when a level is first opened")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionY, "Default Y Camera Position",
|
||||
"Default Y Camera Position when a level is opened")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionZ, "Default Z Camera Position",
|
||||
"Default Z Camera Position when a level is opened");
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultOrbitDistance, "Default Orbit Distance",
|
||||
"The default distance to orbit about when there is no entity selected")
|
||||
->Attribute(AZ::Edit::Attributes::Min, minValue);
|
||||
|
||||
editContext->Class<CameraInputSettings>("Camera Input Settings", "")
|
||||
->DataElement(
|
||||
@@ -283,12 +280,8 @@ void CEditorPreferencesPage_ViewportCamera::OnApply()
|
||||
SandboxEditor::SetCameraOrbitYawRotationInverted(m_cameraMovementSettings.m_orbitYawRotationInverted);
|
||||
SandboxEditor::SetCameraPanInvertedX(m_cameraMovementSettings.m_panInvertedX);
|
||||
SandboxEditor::SetCameraPanInvertedY(m_cameraMovementSettings.m_panInvertedY);
|
||||
SandboxEditor::SetDefaultCameraEditorPosition(
|
||||
AZ::Vector3(
|
||||
m_cameraMovementSettings.m_defaultCameraPositionX,
|
||||
m_cameraMovementSettings.m_defaultCameraPositionY,
|
||||
m_cameraMovementSettings.m_defaultCameraPositionZ
|
||||
));
|
||||
SandboxEditor::SetCameraDefaultEditorPosition(m_cameraMovementSettings.m_defaultPosition);
|
||||
SandboxEditor::SetCameraDefaultOrbitDistance(m_cameraMovementSettings.m_defaultOrbitDistance);
|
||||
|
||||
SandboxEditor::SetCameraTranslateForwardChannelId(m_cameraInputSettings.m_translateForwardChannelId);
|
||||
SandboxEditor::SetCameraTranslateBackwardChannelId(m_cameraInputSettings.m_translateBackwardChannelId);
|
||||
@@ -325,11 +318,8 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings()
|
||||
m_cameraMovementSettings.m_orbitYawRotationInverted = SandboxEditor::CameraOrbitYawRotationInverted();
|
||||
m_cameraMovementSettings.m_panInvertedX = SandboxEditor::CameraPanInvertedX();
|
||||
m_cameraMovementSettings.m_panInvertedY = SandboxEditor::CameraPanInvertedY();
|
||||
|
||||
AZ::Vector3 defaultCameraPosition = SandboxEditor::DefaultEditorCameraPosition();
|
||||
m_cameraMovementSettings.m_defaultCameraPositionX = defaultCameraPosition.GetX();
|
||||
m_cameraMovementSettings.m_defaultCameraPositionY = defaultCameraPosition.GetY();
|
||||
m_cameraMovementSettings.m_defaultCameraPositionZ = defaultCameraPosition.GetZ();
|
||||
m_cameraMovementSettings.m_defaultPosition = SandboxEditor::CameraDefaultEditorPosition();
|
||||
m_cameraMovementSettings.m_defaultOrbitDistance = SandboxEditor::CameraDefaultOrbitDistance();
|
||||
|
||||
m_cameraInputSettings.m_translateForwardChannelId = SandboxEditor::CameraTranslateForwardChannelId().GetName();
|
||||
m_cameraInputSettings.m_translateBackwardChannelId = SandboxEditor::CameraTranslateBackwardChannelId().GetName();
|
||||
|
||||
@@ -9,9 +9,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "Include/IPreferencesPage.h"
|
||||
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <QIcon>
|
||||
|
||||
inline AZ::Crc32 EditorPropertyVisibility(const bool enabled)
|
||||
@@ -43,6 +46,7 @@ private:
|
||||
{
|
||||
AZ_TYPE_INFO(CameraMovementSettings, "{60B8C07E-5F48-4171-A50B-F45558B5CCA1}")
|
||||
|
||||
AZ::Vector3 m_defaultPosition;
|
||||
float m_translateSpeed;
|
||||
float m_rotateSpeed;
|
||||
float m_scrollSpeed;
|
||||
@@ -50,16 +54,14 @@ private:
|
||||
float m_panSpeed;
|
||||
float m_boostMultiplier;
|
||||
float m_rotateSmoothness;
|
||||
bool m_rotateSmoothing;
|
||||
float m_translateSmoothness;
|
||||
bool m_translateSmoothing;
|
||||
float m_defaultOrbitDistance;
|
||||
bool m_captureCursorLook;
|
||||
bool m_orbitYawRotationInverted;
|
||||
bool m_panInvertedX;
|
||||
bool m_panInvertedY;
|
||||
float m_defaultCameraPositionX;
|
||||
float m_defaultCameraPositionY;
|
||||
float m_defaultCameraPositionZ;
|
||||
bool m_rotateSmoothing;
|
||||
bool m_translateSmoothing;
|
||||
|
||||
AZ::Crc32 RotateSmoothingVisibility() const
|
||||
{
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace SandboxEditor
|
||||
constexpr AZStd::string_view CameraTranslateSmoothingSetting = "/Amazon/Preferences/Editor/Camera/TranslateSmoothing";
|
||||
constexpr AZStd::string_view CameraRotateSmoothingSetting = "/Amazon/Preferences/Editor/Camera/RotateSmoothing";
|
||||
constexpr AZStd::string_view CameraCaptureCursorLookSetting = "/Amazon/Preferences/Editor/Camera/CaptureCursorLook";
|
||||
constexpr AZStd::string_view CameraDefaultOrbitDistanceSetting = "/Amazon/Preferences/Editor/Camera/DefaultOrbitDistance";
|
||||
constexpr AZStd::string_view CameraTranslateForwardIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateForwardId";
|
||||
constexpr AZStd::string_view CameraTranslateBackwardIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateBackwardId";
|
||||
constexpr AZStd::string_view CameraTranslateLeftIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateLeftId";
|
||||
@@ -114,15 +115,15 @@ namespace SandboxEditor
|
||||
return AZStd::make_unique<EditorViewportSettingsCallbacksImpl>();
|
||||
}
|
||||
|
||||
AZ::Vector3 DefaultEditorCameraPosition()
|
||||
AZ::Vector3 CameraDefaultEditorPosition()
|
||||
{
|
||||
float xPosition = aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionX, 0.0));
|
||||
float yPosition = aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionY, -10.0));
|
||||
float zPosition = aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionZ, 4.0));
|
||||
return AZ::Vector3(xPosition, yPosition, zPosition);
|
||||
return AZ::Vector3(
|
||||
aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionX, 0.0)),
|
||||
aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionY, -10.0)),
|
||||
aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionZ, 4.0)));
|
||||
}
|
||||
|
||||
void SetDefaultCameraEditorPosition(const AZ::Vector3 defaultCameraPosition)
|
||||
void SetCameraDefaultEditorPosition(const AZ::Vector3& defaultCameraPosition)
|
||||
{
|
||||
SetRegistry(CameraDefaultStartingPositionX, defaultCameraPosition.GetX());
|
||||
SetRegistry(CameraDefaultStartingPositionY, defaultCameraPosition.GetY());
|
||||
@@ -359,6 +360,16 @@ namespace SandboxEditor
|
||||
SetRegistry(CameraCaptureCursorLookSetting, capture);
|
||||
}
|
||||
|
||||
float CameraDefaultOrbitDistance()
|
||||
{
|
||||
return aznumeric_cast<float>(GetRegistry(CameraDefaultOrbitDistanceSetting, 20.0));
|
||||
}
|
||||
|
||||
void SetCameraDefaultOrbitDistance(const float distance)
|
||||
{
|
||||
SetRegistry(CameraDefaultOrbitDistanceSetting, distance);
|
||||
}
|
||||
|
||||
AzFramework::InputChannelId CameraTranslateForwardChannelId()
|
||||
{
|
||||
return AzFramework::InputChannelId(
|
||||
|
||||
@@ -33,9 +33,6 @@ namespace SandboxEditor
|
||||
//! event will fire when a value in the settings registry (editorpreferences.setreg) is modified.
|
||||
SANDBOX_API AZStd::unique_ptr<EditorViewportSettingsCallbacks> CreateEditorViewportSettingsCallbacks();
|
||||
|
||||
SANDBOX_API AZ::Vector3 DefaultEditorCameraPosition();
|
||||
SANDBOX_API void SetDefaultCameraEditorPosition(AZ::Vector3 defaultCameraPosition);
|
||||
|
||||
SANDBOX_API AZ::u64 MaxItemsShownInAssetBrowserSearch();
|
||||
SANDBOX_API void SetMaxItemsShownInAssetBrowserSearch(AZ::u64 numberOfItemsShown);
|
||||
|
||||
@@ -105,6 +102,12 @@ namespace SandboxEditor
|
||||
SANDBOX_API bool CameraCaptureCursorForLook();
|
||||
SANDBOX_API void SetCameraCaptureCursorForLook(bool capture);
|
||||
|
||||
SANDBOX_API AZ::Vector3 CameraDefaultEditorPosition();
|
||||
SANDBOX_API void SetCameraDefaultEditorPosition(const AZ::Vector3& position);
|
||||
|
||||
SANDBOX_API float CameraDefaultOrbitDistance();
|
||||
SANDBOX_API void SetCameraDefaultOrbitDistance(float distance);
|
||||
|
||||
SANDBOX_API AzFramework::InputChannelId CameraTranslateForwardChannelId();
|
||||
SANDBOX_API void SetCameraTranslateForwardChannelId(AZStd::string_view cameraTranslateForwardId);
|
||||
|
||||
|
||||
@@ -620,9 +620,9 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
break;
|
||||
|
||||
case eNotify_OnEndNewScene:
|
||||
PopDisableRendering();
|
||||
|
||||
{
|
||||
PopDisableRendering();
|
||||
|
||||
Matrix34 viewTM;
|
||||
viewTM.SetIdentity();
|
||||
|
||||
@@ -638,9 +638,9 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
break;
|
||||
|
||||
case eNotify_OnEndTerrainCreate:
|
||||
PopDisableRendering();
|
||||
|
||||
{
|
||||
PopDisableRendering();
|
||||
|
||||
Matrix34 viewTM;
|
||||
viewTM.SetIdentity();
|
||||
|
||||
@@ -2021,10 +2021,6 @@ void EditorViewportWidget::SetDefaultCamera()
|
||||
GetViewManager()->SetCameraObjectId(GUID_NULL);
|
||||
SetName(m_defaultViewName);
|
||||
|
||||
// Set the default Editor Camera position.
|
||||
m_defaultViewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition()));
|
||||
SetViewTM(m_defaultViewTM);
|
||||
|
||||
// Synchronize the configured editor viewport FOV to the default camera
|
||||
if (m_viewPane)
|
||||
{
|
||||
@@ -2041,6 +2037,10 @@ void EditorViewportWidget::SetDefaultCamera()
|
||||
atomViewportRequests->PushView(contextName, m_defaultView);
|
||||
}
|
||||
|
||||
// Set the default Editor Camera position.
|
||||
m_defaultViewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition()));
|
||||
SetViewTM(m_defaultViewTM);
|
||||
|
||||
PostCameraSet();
|
||||
}
|
||||
|
||||
@@ -2527,7 +2527,7 @@ bool EditorViewportSettings::StickySelectEnabled() const
|
||||
|
||||
AZ::Vector3 EditorViewportSettings::DefaultEditorCameraPosition() const
|
||||
{
|
||||
return SandboxEditor::DefaultEditorCameraPosition();
|
||||
return SandboxEditor::CameraDefaultEditorPosition();
|
||||
}
|
||||
|
||||
AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once);
|
||||
|
||||
@@ -806,12 +806,20 @@ namespace AzFramework
|
||||
[[maybe_unused]] float scrollDelta,
|
||||
[[maybe_unused]] float deltaTime)
|
||||
{
|
||||
const auto pivot = m_pivotFn();
|
||||
|
||||
if (!pivot.has_value())
|
||||
{
|
||||
EndActivation();
|
||||
return targetCamera;
|
||||
}
|
||||
|
||||
if (Beginning())
|
||||
{
|
||||
// as the camera starts, record the camera we would like to end up as
|
||||
m_nextCamera.m_offset = m_offsetFn(m_pivotFn().GetDistance(targetCamera.Translation()));
|
||||
m_nextCamera.m_offset = m_offsetFn(pivot.value().GetDistance(targetCamera.Translation()));
|
||||
const auto angles =
|
||||
EulerAngles(AZ::Matrix3x3::CreateFromMatrix3x4(AZ::Matrix3x4::CreateLookAt(targetCamera.Translation(), m_pivotFn())));
|
||||
EulerAngles(AZ::Matrix3x3::CreateFromMatrix3x4(AZ::Matrix3x4::CreateLookAt(targetCamera.Translation(), pivot.value())));
|
||||
m_nextCamera.m_pitch = angles.GetX();
|
||||
m_nextCamera.m_yaw = angles.GetZ();
|
||||
m_nextCamera.m_pivot = targetCamera.m_pivot;
|
||||
|
||||
@@ -651,7 +651,7 @@ namespace AzFramework
|
||||
class FocusCameraInput : public CameraInput
|
||||
{
|
||||
public:
|
||||
using PivotFn = AZStd::function<AZ::Vector3()>;
|
||||
using PivotFn = AZStd::function<AZStd::optional<AZ::Vector3>()>;
|
||||
|
||||
FocusCameraInput(const InputChannelId& focusChannelId, FocusOffsetFn offsetFn);
|
||||
|
||||
|
||||
+2
-2
@@ -40,9 +40,9 @@ namespace AzToolsFramework
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
QModelIndex parent(const QModelIndex& child) const override;
|
||||
QModelIndex sibling(int row, int column, const QModelIndex& idx) const override;
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
|
||||
protected:
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override;
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace AzToolsFramework
|
||||
private slots:
|
||||
void SourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
private:
|
||||
AZ::u64 m_numberOfItemsDisplayed = 0;
|
||||
AZ::u64 m_numberOfItemsDisplayed = 50;
|
||||
int m_displayedItemsCounter = 0;
|
||||
QPointer<AssetBrowserFilterModel> m_filterModel;
|
||||
QMap<int, QModelIndex> m_indexMap;
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserComponent.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Search/SearchWidget.h>
|
||||
#include <AzToolsFramework/AssetDatabase/AssetDatabaseConnection.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextComponent.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
#include <QAbstractItemModelTester>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
// Test fixture for the AssetBrowser model that uses a QAbstractItemModelTester to validate the state of the model
|
||||
// when QAbstractItemModel signals fire. Tests will exit with a fatal error if an invalid state is detected.
|
||||
class AssetBrowserTest
|
||||
: public ToolsApplicationFixture
|
||||
, public testing::WithParamInterface<const char*>
|
||||
{
|
||||
protected:
|
||||
enum class AssetEntryType
|
||||
{
|
||||
Root,
|
||||
Folder,
|
||||
Source,
|
||||
Product
|
||||
};
|
||||
|
||||
enum class FolderType
|
||||
{
|
||||
Root,
|
||||
File
|
||||
};
|
||||
|
||||
void SetUpEditorFixtureImpl() override;
|
||||
void TearDownEditorFixtureImpl() override;
|
||||
|
||||
//! Creates a Mock Scan Folder
|
||||
void AddScanFolder(AZ::s64 folderID, AZStd::string folderPath, AZStd::string displayName, FolderType folderType = FolderType::File);
|
||||
|
||||
//! Creates a Source entry from a mock file
|
||||
AZ::Uuid CreateSourceEntry(
|
||||
AZ::s64 fileID, AZ::s64 parentFolderID, AZStd::string filename, AssetEntryType sourceType = AssetEntryType::Source);
|
||||
|
||||
//! Creates a product from a given sourceEntry
|
||||
void CreateProduct(AZ::s64 productID, AZ::Uuid sourceUuid, AZStd::string productName);
|
||||
|
||||
void SetupAssetBrowser();
|
||||
void PrintModel(const QAbstractItemModel* model, AZStd::function<void(const QString&)> printer);
|
||||
QModelIndex GetModelIndex(const QAbstractItemModel* model, int targetDepth, int row = 0);
|
||||
AZStd::shared_ptr<AzToolsFramework::AssetBrowser::RootAssetBrowserEntry> GetRootEntry();
|
||||
AZStd::vector<QString> GetVectorFromFormattedString(const QString& formattedString);
|
||||
|
||||
protected:
|
||||
QString m_assetBrowserHierarchy = QString();
|
||||
|
||||
AZStd::unique_ptr<AzToolsFramework::AssetBrowser::SearchWidget> m_searchWidget;
|
||||
AZStd::unique_ptr<AzToolsFramework::AssetBrowser::AssetBrowserComponent> m_assetBrowserComponent;
|
||||
|
||||
AZStd::unique_ptr<AzToolsFramework::AssetBrowser::AssetBrowserFilterModel> m_filterModel;
|
||||
AZStd::unique_ptr<AzToolsFramework::AssetBrowser::AssetBrowserTableModel> m_tableModel;
|
||||
|
||||
AZStd::unique_ptr<QAbstractItemModelTester> m_modelTesterAssetBrowser;
|
||||
AZStd::unique_ptr<QAbstractItemModelTester> m_modelTesterFilterModel;
|
||||
AZStd::unique_ptr<QAbstractItemModelTester> m_modelTesterTableModel;
|
||||
|
||||
QVector<int> m_folderIds = { 13, 14, 15 };
|
||||
QVector<int> m_sourceIDs = { 1, 2, 3, 4, 5 };
|
||||
QVector<int> m_productIDs = { 1, 2, 3, 4, 5 };
|
||||
};
|
||||
|
||||
void AssetBrowserTest::SetUpEditorFixtureImpl()
|
||||
{
|
||||
GetApplication()->RegisterComponentDescriptor(AzToolsFramework::EditorEntityContextComponent::CreateDescriptor());
|
||||
|
||||
m_assetBrowserComponent = AZStd::make_unique<AzToolsFramework::AssetBrowser::AssetBrowserComponent>();
|
||||
m_assetBrowserComponent->Activate();
|
||||
|
||||
m_filterModel = AZStd::make_unique<AzToolsFramework::AssetBrowser::AssetBrowserFilterModel>();
|
||||
m_tableModel = AZStd::make_unique<AzToolsFramework::AssetBrowser::AssetBrowserTableModel>();
|
||||
|
||||
m_filterModel->setSourceModel(m_assetBrowserComponent->GetAssetBrowserModel());
|
||||
m_tableModel->setSourceModel(m_filterModel.get());
|
||||
|
||||
m_modelTesterAssetBrowser = AZStd::make_unique<QAbstractItemModelTester>(m_assetBrowserComponent->GetAssetBrowserModel());
|
||||
m_modelTesterFilterModel = AZStd::make_unique<QAbstractItemModelTester>(m_filterModel.get());
|
||||
m_modelTesterTableModel = AZStd::make_unique<QAbstractItemModelTester>(m_tableModel.get());
|
||||
m_searchWidget = AZStd::make_unique<AzToolsFramework::AssetBrowser::SearchWidget>();
|
||||
|
||||
// Setup String filters
|
||||
m_searchWidget->Setup(true, true);
|
||||
m_filterModel->SetFilter(m_searchWidget->GetFilter());
|
||||
|
||||
SetupAssetBrowser();
|
||||
}
|
||||
|
||||
void AssetBrowserTest::TearDownEditorFixtureImpl()
|
||||
{
|
||||
m_modelTesterAssetBrowser.reset();
|
||||
m_modelTesterFilterModel.reset();
|
||||
m_modelTesterTableModel.reset();
|
||||
|
||||
m_tableModel.reset();
|
||||
m_filterModel.reset();
|
||||
m_assetBrowserComponent->Deactivate();
|
||||
|
||||
m_assetBrowserComponent.reset();
|
||||
m_searchWidget.reset();
|
||||
}
|
||||
|
||||
void AssetBrowserTest::AddScanFolder(
|
||||
AZ::s64 folderID, AZStd::string folderPath, AZStd::string displayName, FolderType folderType /*= FolderType::File*/)
|
||||
{
|
||||
AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry scanFolder = AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry();
|
||||
scanFolder.m_scanFolderID = folderID;
|
||||
scanFolder.m_scanFolder = folderPath;
|
||||
scanFolder.m_displayName = displayName;
|
||||
scanFolder.m_isRoot = folderType == FolderType::Root;
|
||||
GetRootEntry()->AddScanFolder(scanFolder);
|
||||
}
|
||||
|
||||
AZ::Uuid AssetBrowserTest::CreateSourceEntry(
|
||||
AZ::s64 fileID, AZ::s64 parentFolderID, AZStd::string filename, AssetEntryType sourceType /*= AssetEntryType::Source*/)
|
||||
{
|
||||
AzToolsFramework::AssetDatabase::FileDatabaseEntry entry = AzToolsFramework::AssetDatabase::FileDatabaseEntry();
|
||||
entry.m_scanFolderPK = parentFolderID;
|
||||
entry.m_fileID = fileID;
|
||||
entry.m_fileName = filename;
|
||||
entry.m_isFolder = sourceType == AssetEntryType::Folder;
|
||||
GetRootEntry()->AddFile(entry);
|
||||
|
||||
if (!entry.m_isFolder)
|
||||
{
|
||||
AzToolsFramework::AssetBrowser::SourceWithFileID entrySource = AzToolsFramework::AssetBrowser::SourceWithFileID();
|
||||
entrySource.first = entry.m_fileID;
|
||||
entrySource.second = AzToolsFramework::AssetDatabase::SourceDatabaseEntry();
|
||||
entrySource.second.m_scanFolderPK = parentFolderID;
|
||||
entrySource.second.m_sourceName = filename;
|
||||
entrySource.second.m_sourceID = fileID;
|
||||
entrySource.second.m_sourceGuid = AZ::Uuid::CreateRandom();
|
||||
|
||||
GetRootEntry()->AddSource(entrySource);
|
||||
|
||||
return entrySource.second.m_sourceGuid;
|
||||
}
|
||||
|
||||
return AZ::Uuid::CreateNull();
|
||||
}
|
||||
|
||||
void AssetBrowserTest::CreateProduct(AZ::s64 productID, AZ::Uuid sourceUuid, AZStd::string productName)
|
||||
{
|
||||
AzToolsFramework::AssetBrowser::ProductWithUuid product = AzToolsFramework::AssetBrowser::ProductWithUuid();
|
||||
product.first = sourceUuid;
|
||||
product.second = AzToolsFramework::AssetDatabase::ProductDatabaseEntry();
|
||||
product.second.m_productID = productID;
|
||||
|
||||
product.second.m_subID = aznumeric_cast<AZ::u32>(productID);
|
||||
product.second.m_productName = productName;
|
||||
|
||||
GetRootEntry()->AddProduct(product);
|
||||
}
|
||||
|
||||
void AssetBrowserTest::SetupAssetBrowser()
|
||||
{
|
||||
// RootEntries : 1 | Folders : 4 | SourceEntries : 5 | ProductEntries : 9
|
||||
m_assetBrowserHierarchy = R"(
|
||||
D:
|
||||
\
|
||||
dev
|
||||
o3de
|
||||
GameProject
|
||||
Assets
|
||||
Source_1
|
||||
Product_1_1
|
||||
Product_1_0
|
||||
Source_0
|
||||
Product_0_3
|
||||
Product_0_2
|
||||
Product_0_1
|
||||
Product_0_0
|
||||
Scripts
|
||||
Source_3
|
||||
Source_2
|
||||
Product_2_2
|
||||
Product_2_1
|
||||
Product_2_0
|
||||
Misc
|
||||
Source_4
|
||||
Product_4_2
|
||||
Product_4_1
|
||||
Product_4_0 )";
|
||||
|
||||
namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
|
||||
|
||||
AddScanFolder(m_folderIds.at(2), "D:/dev/o3de/GameProject/Misc", "Misc");
|
||||
AZ::Uuid sourceUuid_4 = CreateSourceEntry(m_sourceIDs.at(4), m_folderIds.at(2), "Source_4");
|
||||
CreateProduct(m_productIDs.at(0), sourceUuid_4, "Product_4_0");
|
||||
CreateProduct(m_productIDs.at(1), sourceUuid_4, "Product_4_1");
|
||||
CreateProduct(m_productIDs.at(2), sourceUuid_4, "Product_4_2");
|
||||
|
||||
AddScanFolder(m_folderIds.at(1), "D:/dev/o3de/GameProject/Scripts", "Scripts");
|
||||
|
||||
AZ::Uuid sourceUuid_2 = CreateSourceEntry(m_sourceIDs.at(2), m_folderIds.at(1), "Source_2");
|
||||
CreateProduct(m_productIDs.at(0), sourceUuid_2, "Product_2_0");
|
||||
CreateProduct(m_productIDs.at(1), sourceUuid_2, "Product_2_1");
|
||||
CreateProduct(m_productIDs.at(2), sourceUuid_2, "Product_2_2");
|
||||
|
||||
CreateSourceEntry(m_sourceIDs.at(3), m_folderIds.at(1), "Source_3");
|
||||
|
||||
AddScanFolder(m_folderIds.at(0), "D:/dev/o3de/GameProject/Assets", "Assets");
|
||||
|
||||
AZ::Uuid sourceUuid_0 = CreateSourceEntry(m_sourceIDs.at(0), m_folderIds.at(0), "Source_0");
|
||||
CreateProduct(m_productIDs.at(0), sourceUuid_0, "Product_0_0");
|
||||
CreateProduct(m_productIDs.at(1), sourceUuid_0, "Product_0_1");
|
||||
CreateProduct(m_productIDs.at(2), sourceUuid_0, "Product_0_2");
|
||||
CreateProduct(m_productIDs.at(3), sourceUuid_0, "Product_0_3");
|
||||
|
||||
AZ::Uuid sourceUuid_1 = CreateSourceEntry(m_sourceIDs.at(1), m_folderIds.at(0), "Source_1");
|
||||
CreateProduct(m_productIDs.at(0), sourceUuid_1, "Product_1_0");
|
||||
CreateProduct(m_productIDs.at(1), sourceUuid_1, "Product_1_1");
|
||||
}
|
||||
|
||||
void AssetBrowserTest::PrintModel(const QAbstractItemModel* model, AZStd::function<void(const QString&)> printer)
|
||||
{
|
||||
AZStd::deque<AZStd::pair<QModelIndex, int>> indices;
|
||||
indices.push_back({ model->index(0, 0), 0 });
|
||||
while (!indices.empty())
|
||||
{
|
||||
auto [index, depth] = indices.front();
|
||||
indices.pop_front();
|
||||
|
||||
QString indentString;
|
||||
for (int i = 0; i < depth; ++i)
|
||||
{
|
||||
indentString += " ";
|
||||
}
|
||||
const QString message = indentString + index.data(Qt::DisplayRole).toString();
|
||||
printer(message);
|
||||
|
||||
for (int i = 0; i < model->rowCount(index); ++i)
|
||||
{
|
||||
indices.emplace_front(model->index(i, 0, index), depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex AssetBrowserTest::GetModelIndex(const QAbstractItemModel* model, int targetDepth, int row)
|
||||
{
|
||||
AZStd::deque<AZStd::pair<QModelIndex, int>> indices;
|
||||
indices.push_back({ model->index(0, 0), 0 });
|
||||
while (!indices.empty())
|
||||
{
|
||||
auto [index, depth] = indices.front();
|
||||
indices.pop_front();
|
||||
|
||||
for (int i = 0; i < model->rowCount(index); ++i)
|
||||
{
|
||||
if (depth + 1 == targetDepth && row == i)
|
||||
{
|
||||
return model->index(i, 0, index);
|
||||
}
|
||||
indices.emplace_front(model->index(i, 0, index), depth + 1);
|
||||
}
|
||||
}
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<AzToolsFramework::AssetBrowser::RootAssetBrowserEntry> AssetBrowserTest::GetRootEntry()
|
||||
{
|
||||
return m_assetBrowserComponent->GetAssetBrowserModel()->GetRootEntry();
|
||||
}
|
||||
|
||||
AZStd::vector<QString> AssetBrowserTest::GetVectorFromFormattedString(const QString& formattedString)
|
||||
{
|
||||
AZStd::vector<QString> hierarchySections;
|
||||
QStringList splittedList = formattedString.split('\n', Qt::SkipEmptyParts);
|
||||
|
||||
for (auto& str : splittedList)
|
||||
{
|
||||
str.replace(" ", "");
|
||||
hierarchySections.push_back(str);
|
||||
}
|
||||
return hierarchySections;
|
||||
}
|
||||
|
||||
TEST_F(AssetBrowserTest, CheckCorrectNumberOfEntriesInTableView)
|
||||
{
|
||||
m_filterModel->FilterUpdatedSlotImmediate();
|
||||
const int tableViewRowcount = m_tableModel->rowCount();
|
||||
|
||||
// RowCount should be 17 -> 5 SourceEntries + 12 ProductEntries)
|
||||
EXPECT_EQ(tableViewRowcount, 17);
|
||||
}
|
||||
|
||||
TEST_F(AssetBrowserTest, CheckCorrectNumberOfEntriesInTableViewAfterStringFilter)
|
||||
{
|
||||
/*
|
||||
*-Source_1
|
||||
* |
|
||||
* |-product_1_0
|
||||
* |-product_1_1
|
||||
*
|
||||
*
|
||||
* Matching entries = 3
|
||||
*/
|
||||
|
||||
// Apply string filter
|
||||
m_searchWidget->SetTextFilter(QString("source_1"));
|
||||
m_filterModel->FilterUpdatedSlotImmediate();
|
||||
|
||||
const int tableViewRowcount = m_tableModel->rowCount();
|
||||
EXPECT_EQ(tableViewRowcount, 3);
|
||||
}
|
||||
|
||||
TEST_F(AssetBrowserTest, CheckScanFolderAddition)
|
||||
{
|
||||
EXPECT_EQ(m_assetBrowserComponent->GetAssetBrowserModel()->rowCount(), 1);
|
||||
const int newFolderId = 20;
|
||||
AddScanFolder(newFolderId, "E:/TestFolder/TestFolder2", "TestFolder");
|
||||
|
||||
// Since the folder is empty it shouldn't be added to the model.
|
||||
EXPECT_EQ(m_assetBrowserComponent->GetAssetBrowserModel()->rowCount(), 1);
|
||||
|
||||
CreateSourceEntry(123, newFolderId, "DummyFile");
|
||||
|
||||
// When we add a file to the folder it should be added to the model
|
||||
EXPECT_EQ(m_assetBrowserComponent->GetAssetBrowserModel()->rowCount(), 2);
|
||||
}
|
||||
|
||||
} // namespace UnitTest
|
||||
@@ -126,6 +126,7 @@ set(FILES
|
||||
UI/EntityIdQLineEditTests.cpp
|
||||
UI/EntityOutlinerTests.cpp
|
||||
UI/EntityPropertyEditorTests.cpp
|
||||
UI/AssetBrowserTests.cpp
|
||||
UndoStack.cpp
|
||||
Viewport/ClusterTests.cpp
|
||||
Viewport/ViewportEditorModeTests.cpp
|
||||
|
||||
@@ -76,6 +76,12 @@ foreach(project_name project_path IN ZIP_LISTS LY_PROJECTS_TARGET_NAME LY_PROJEC
|
||||
Legacy::CrySystem
|
||||
)
|
||||
|
||||
if(PAL_TRAIT_BUILD_SERVER_SUPPORTED)
|
||||
set(server_runtime_dependencies
|
||||
Legacy::CrySystem
|
||||
)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
|
||||
@@ -333,11 +333,6 @@ void CXConsole::Init(ISystem* pSystem)
|
||||
|
||||
m_nLoadingBackTexID = -1;
|
||||
|
||||
if (gEnv->IsDedicated())
|
||||
{
|
||||
m_bConsoleActive = true;
|
||||
}
|
||||
|
||||
REGISTER_COMMAND("ConsoleShow", &ConsoleShow, VF_NULL, "Opens the console");
|
||||
REGISTER_COMMAND("ConsoleHide", &ConsoleHide, VF_NULL, "Closes the console");
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <ScreenHeaderWidget.h>
|
||||
#include <GemCatalog/GemModel.h>
|
||||
#include <GemCatalog/GemCatalogScreen.h>
|
||||
#include <GemRepo/GemRepoScreen.h>
|
||||
#include <ProjectUtils.h>
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
@@ -47,9 +48,14 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_gemCatalogScreen = new GemCatalogScreen(this);
|
||||
m_stack->addWidget(m_gemCatalogScreen);
|
||||
|
||||
m_gemRepoScreen = new GemRepoScreen(this);
|
||||
m_stack->addWidget(m_gemRepoScreen);
|
||||
|
||||
vLayout->addWidget(m_stack);
|
||||
|
||||
connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest);
|
||||
connect(m_gemRepoScreen, &GemRepoScreen::OnRefresh, m_gemCatalogScreen, &GemCatalogScreen::Refresh);
|
||||
|
||||
// When there are multiple project templates present, we re-gather the gems when changing the selected the project template.
|
||||
connect(m_newProjectSettingsScreen, &NewProjectSettingsScreen::OnTemplateSelectionChanged, this, [=](int oldIndex, [[maybe_unused]] int newIndex)
|
||||
@@ -89,6 +95,9 @@ namespace O3DE::ProjectManager
|
||||
buttons->setObjectName("footer");
|
||||
vLayout->addWidget(buttons);
|
||||
|
||||
m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole);
|
||||
connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton);
|
||||
|
||||
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
connect(m_newProjectSettingsScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest);
|
||||
|
||||
@@ -100,8 +109,6 @@ namespace O3DE::ProjectManager
|
||||
Update();
|
||||
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
|
||||
m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole);
|
||||
connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton);
|
||||
|
||||
setLayout(vLayout);
|
||||
}
|
||||
@@ -122,6 +129,9 @@ namespace O3DE::ProjectManager
|
||||
|
||||
// Gather the enabled gems from the default project template when starting the create new project workflow.
|
||||
ReinitGemCatalogForSelectedTemplate();
|
||||
|
||||
// make sure the gem repo has the latest details
|
||||
m_gemRepoScreen->Reinit();
|
||||
}
|
||||
|
||||
void CreateProjectCtrl::HandleBackButton()
|
||||
@@ -160,12 +170,21 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
m_header->setSubTitle(tr("Configure project with Gems"));
|
||||
m_secondaryButton->setVisible(false);
|
||||
m_primaryButton->setVisible(true);
|
||||
}
|
||||
else if (m_stack->currentWidget() == m_gemRepoScreen)
|
||||
{
|
||||
m_header->setSubTitle(tr("Gem Repositories"));
|
||||
m_secondaryButton->setVisible(true);
|
||||
m_secondaryButton->setText(tr("Back"));
|
||||
m_primaryButton->setVisible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_header->setSubTitle(tr("Enter Project Details"));
|
||||
m_secondaryButton->setVisible(true);
|
||||
m_secondaryButton->setText(tr("Configure Gems"));
|
||||
m_primaryButton->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +194,10 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
HandleSecondaryButton();
|
||||
}
|
||||
else if (screen == ProjectManagerScreen::GemRepos)
|
||||
{
|
||||
NextScreen();
|
||||
}
|
||||
else
|
||||
{
|
||||
emit ChangeScreenRequest(screen);
|
||||
@@ -230,6 +253,12 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
if (m_newProjectSettingsScreen->Validate())
|
||||
{
|
||||
if (!m_gemCatalogScreen->GetDownloadController()->IsDownloadQueueEmpty())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Gems downloading"), tr("You must wait for gems to finish downloading before continuing."));
|
||||
return;
|
||||
}
|
||||
|
||||
ProjectInfo projectInfo = m_newProjectSettingsScreen->GetProjectInfo();
|
||||
QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath();
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace O3DE::ProjectManager
|
||||
QT_FORWARD_DECLARE_CLASS(ScreenHeader)
|
||||
QT_FORWARD_DECLARE_CLASS(NewProjectSettingsScreen)
|
||||
QT_FORWARD_DECLARE_CLASS(GemCatalogScreen)
|
||||
QT_FORWARD_DECLARE_CLASS(GemRepoScreen)
|
||||
|
||||
class CreateProjectCtrl
|
||||
: public ScreenWidget
|
||||
@@ -64,6 +65,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
NewProjectSettingsScreen* m_newProjectSettingsScreen = nullptr;
|
||||
GemCatalogScreen* m_gemCatalogScreen = nullptr;
|
||||
GemRepoScreen* m_gemRepoScreen = nullptr;
|
||||
};
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -82,12 +82,13 @@ namespace O3DE::ProjectManager
|
||||
succeeded = false;
|
||||
}
|
||||
|
||||
QString gemName = m_gemNames.front();
|
||||
m_gemNames.erase(m_gemNames.begin());
|
||||
emit Done(succeeded);
|
||||
emit Done(gemName, succeeded);
|
||||
|
||||
if (!m_gemNames.empty())
|
||||
{
|
||||
emit StartGemDownload(m_gemNames[0]);
|
||||
emit StartGemDownload(m_gemNames.front());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
signals:
|
||||
void StartGemDownload(const QString& gemName);
|
||||
void Done(bool success = true);
|
||||
void Done(const QString& gemName, bool success = true);
|
||||
void GemDownloadProgress(int percentage);
|
||||
|
||||
private:
|
||||
|
||||
@@ -39,6 +39,10 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_tabWidget->addTab(m_engineSettingsScreen, tr("General"));
|
||||
m_tabWidget->addTab(m_gemRepoScreen, tr("Gem Repositories"));
|
||||
|
||||
// when tab changes, notify the current screen so it can refresh
|
||||
connect(m_tabWidget, &QTabWidget::currentChanged, this, &EngineScreenCtrl::TabChanged);
|
||||
|
||||
topBarHLayout->addWidget(m_tabWidget);
|
||||
|
||||
vLayout->addWidget(topBarFrameWidget);
|
||||
@@ -46,6 +50,11 @@ namespace O3DE::ProjectManager
|
||||
setLayout(vLayout);
|
||||
}
|
||||
|
||||
void EngineScreenCtrl::TabChanged([[maybe_unused]] int index)
|
||||
{
|
||||
NotifyCurrentScreen();
|
||||
}
|
||||
|
||||
ProjectManagerScreen EngineScreenCtrl::GetScreenEnum()
|
||||
{
|
||||
return ProjectManagerScreen::UpdateProject;
|
||||
@@ -71,6 +80,15 @@ namespace O3DE::ProjectManager
|
||||
return false;
|
||||
}
|
||||
|
||||
void EngineScreenCtrl::NotifyCurrentScreen()
|
||||
{
|
||||
ScreenWidget* screen = reinterpret_cast<ScreenWidget*>(m_tabWidget->currentWidget());
|
||||
if (screen)
|
||||
{
|
||||
screen->NotifyCurrentScreen();
|
||||
}
|
||||
}
|
||||
|
||||
void EngineScreenCtrl::GoToScreen(ProjectManagerScreen screen)
|
||||
{
|
||||
if (screen == m_engineSettingsScreen->GetScreenEnum())
|
||||
|
||||
@@ -30,6 +30,10 @@ namespace O3DE::ProjectManager
|
||||
bool IsTab() override;
|
||||
bool ContainsScreen(ProjectManagerScreen screen) override;
|
||||
void GoToScreen(ProjectManagerScreen screen) override;
|
||||
void NotifyCurrentScreen() override;
|
||||
|
||||
public slots:
|
||||
void TabChanged(int index);
|
||||
|
||||
QTabWidget* m_tabWidget = nullptr;
|
||||
EngineSettingsScreen* m_engineSettingsScreen = nullptr;
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
else
|
||||
{
|
||||
tagContainer->Update(ConvertFromModelIndices(tagIndices));
|
||||
tagContainer->Update(GetTagsFromModelIndices(tagIndices));
|
||||
label->setText(QString("%1 %2").arg(tagIndices.size()).arg(tagIndices.size() == 1 ? singularTitle : pluralTitle));
|
||||
widget->show();
|
||||
}
|
||||
@@ -234,17 +234,23 @@ namespace O3DE::ProjectManager
|
||||
for (int downloadingGemNumber = 0; downloadingGemNumber < downloadQueue.size(); ++downloadingGemNumber)
|
||||
{
|
||||
QHBoxLayout* nameProgressLayout = new QHBoxLayout();
|
||||
TagWidget* newTag = new TagWidget(downloadQueue[downloadingGemNumber]);
|
||||
|
||||
const QString& gemName = downloadQueue[downloadingGemNumber];
|
||||
TagWidget* newTag = new TagWidget({gemName, gemName});
|
||||
nameProgressLayout->addWidget(newTag);
|
||||
|
||||
QLabel* progress = new QLabel(downloadingGemNumber == 0? QString("%1%").arg(downloadProgress) : tr("Queued"));
|
||||
nameProgressLayout->addWidget(progress);
|
||||
|
||||
QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
nameProgressLayout->addSpacerItem(spacer);
|
||||
QLabel* cancelText = new QLabel(QString("<a href=\"%1\">Cancel</a>").arg(downloadQueue[downloadingGemNumber]));
|
||||
|
||||
QLabel* cancelText = new QLabel(QString("<a href=\"%1\">Cancel</a>").arg(gemName));
|
||||
cancelText->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
|
||||
connect(cancelText, &QLabel::linkActivated, this, &CartOverlayWidget::OnCancelDownloadActivated);
|
||||
nameProgressLayout->addWidget(cancelText);
|
||||
downloadingItemLayout->addLayout(nameProgressLayout);
|
||||
|
||||
QProgressBar* downloadProgessBar = new QProgressBar();
|
||||
downloadingItemLayout->addWidget(downloadProgessBar);
|
||||
downloadProgessBar->setValue(downloadingGemNumber == 0 ? downloadProgress : 0);
|
||||
@@ -255,7 +261,7 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
};
|
||||
|
||||
auto downloadEnded = [=](bool /*success*/)
|
||||
auto downloadEnded = [=](const QString& /*gemName*/, bool /*success*/)
|
||||
{
|
||||
update(0); // update the list to remove the gem that has finished
|
||||
};
|
||||
@@ -265,15 +271,15 @@ namespace O3DE::ProjectManager
|
||||
update(0);
|
||||
}
|
||||
|
||||
QStringList CartOverlayWidget::ConvertFromModelIndices(const QVector<QModelIndex>& gems) const
|
||||
QVector<Tag> CartOverlayWidget::GetTagsFromModelIndices(const QVector<QModelIndex>& gems) const
|
||||
{
|
||||
QStringList gemNames;
|
||||
gemNames.reserve(gems.size());
|
||||
QVector<Tag> tags;
|
||||
tags.reserve(gems.size());
|
||||
for (const QModelIndex& modelIndex : gems)
|
||||
{
|
||||
gemNames.push_back(GemModel::GetDisplayName(modelIndex));
|
||||
tags.push_back({ GemModel::GetDisplayName(modelIndex), GemModel::GetName(modelIndex) });
|
||||
}
|
||||
return gemNames;
|
||||
return tags;
|
||||
}
|
||||
|
||||
CartButton::CartButton(GemModel* gemModel, DownloadController* downloadController, QWidget* parent)
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace O3DE::ProjectManager
|
||||
CartOverlayWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent = nullptr);
|
||||
|
||||
private:
|
||||
QStringList ConvertFromModelIndices(const QVector<QModelIndex>& gems) const;
|
||||
QVector<Tag> GetTagsFromModelIndices(const QVector<QModelIndex>& gems) const;
|
||||
|
||||
using GetTagIndicesCallback = AZStd::function<QVector<QModelIndex>()>;
|
||||
void CreateGemSection(const QString& singularTitle, const QString& pluralTitle, GetTagIndicesCallback getTagIndices);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <QStandardPaths>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QHash>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -32,6 +33,9 @@ namespace O3DE::ProjectManager
|
||||
m_gemModel = new GemModel(this);
|
||||
m_proxyModel = new GemSortFilterProxyModel(m_gemModel, this);
|
||||
|
||||
// default to sort by gem name
|
||||
m_proxyModel->setSortRole(GemModel::RoleName);
|
||||
|
||||
QVBoxLayout* vLayout = new QVBoxLayout();
|
||||
vLayout->setMargin(0);
|
||||
vLayout->setSpacing(0);
|
||||
@@ -45,6 +49,7 @@ namespace O3DE::ProjectManager
|
||||
connect(m_gemModel, &GemModel::gemStatusChanged, this, &GemCatalogScreen::OnGemStatusChanged);
|
||||
connect(m_headerWidget, &GemCatalogHeaderWidget::OpenGemsRepo, this, &GemCatalogScreen::HandleOpenGemRepo);
|
||||
connect(m_headerWidget, &GemCatalogHeaderWidget::AddGem, this, &GemCatalogScreen::OnAddGemClicked);
|
||||
connect(m_downloadController, &DownloadController::Done, this, &GemCatalogScreen::OnGemDownloadResult);
|
||||
|
||||
QHBoxLayout* hLayout = new QHBoxLayout();
|
||||
hLayout->setMargin(0);
|
||||
@@ -54,7 +59,7 @@ namespace O3DE::ProjectManager
|
||||
m_gemInspector = new GemInspector(m_gemModel, this);
|
||||
m_gemInspector->setFixedWidth(240);
|
||||
|
||||
connect(m_gemInspector, &GemInspector::TagClicked, this, &GemCatalogScreen::SelectGem);
|
||||
connect(m_gemInspector, &GemInspector::TagClicked, [=](const Tag& tag) { SelectGem(tag.id); });
|
||||
|
||||
QWidget* filterWidget = new QWidget(this);
|
||||
filterWidget->setFixedWidth(240);
|
||||
@@ -82,11 +87,13 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void GemCatalogScreen::ReinitForProject(const QString& projectPath)
|
||||
{
|
||||
m_projectPath = projectPath;
|
||||
m_gemModel->Clear();
|
||||
m_gemsToRegisterWithProject.clear();
|
||||
FillModel(projectPath);
|
||||
|
||||
m_proxyModel->ResetFilters();
|
||||
m_proxyModel->sort(/*column=*/0);
|
||||
|
||||
if (m_filterWidget)
|
||||
{
|
||||
@@ -144,11 +151,80 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
m_gemModel->AddGem(gemInfoResult.GetValue<GemInfo>());
|
||||
m_gemModel->UpdateGemDependencies();
|
||||
m_proxyModel->sort(/*column=*/0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GemCatalogScreen::Refresh()
|
||||
{
|
||||
QHash<QString, GemInfo> gemInfoHash;
|
||||
|
||||
// create a hash with the gem name as key
|
||||
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(m_projectPath);
|
||||
if (allGemInfosResult.IsSuccess())
|
||||
{
|
||||
const QVector<GemInfo>& gemInfos = allGemInfosResult.GetValue();
|
||||
for (const GemInfo& gemInfo : gemInfos)
|
||||
{
|
||||
gemInfoHash.insert(gemInfo.m_name, gemInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// add all the gem repos into the hash
|
||||
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos();
|
||||
if (allRepoGemInfosResult.IsSuccess())
|
||||
{
|
||||
const QVector<GemInfo>& allRepoGemInfos = allRepoGemInfosResult.GetValue();
|
||||
for (const GemInfo& gemInfo : allRepoGemInfos)
|
||||
{
|
||||
if (!gemInfoHash.contains(gemInfo.m_name))
|
||||
{
|
||||
gemInfoHash.insert(gemInfo.m_name, gemInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove gems from the model that no longer exist in the hash and are not project dependencies
|
||||
int i = 0;
|
||||
while (i < m_gemModel->rowCount())
|
||||
{
|
||||
QModelIndex index = m_gemModel->index(i,0);
|
||||
QString gemName = m_gemModel->GetName(index);
|
||||
const bool gemFound = gemInfoHash.contains(gemName);
|
||||
if (!gemFound && !m_gemModel->IsAdded(index) && !m_gemModel->IsAddedDependency(index))
|
||||
{
|
||||
m_gemModel->removeRow(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!gemFound && (m_gemModel->IsAdded(index) || m_gemModel->IsAddedDependency(index)))
|
||||
{
|
||||
const QString error = tr("Gem %1 was removed or unregistered, but is still used by the project.").arg(gemName);
|
||||
AZ_Warning("Project Manager", false, error.toUtf8().constData());
|
||||
QMessageBox::warning(this, tr("Gem not found"), error.toUtf8().constData());
|
||||
}
|
||||
|
||||
gemInfoHash.remove(gemName);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// add all gems remaining in the hash that were not removed
|
||||
for(auto iter = gemInfoHash.begin(); iter != gemInfoHash.end(); ++iter)
|
||||
{
|
||||
m_gemModel->AddGem(iter.value());
|
||||
}
|
||||
|
||||
m_gemModel->UpdateGemDependencies();
|
||||
m_proxyModel->sort(/*column=*/0);
|
||||
|
||||
// temporary, until we can refresh filter counts
|
||||
m_proxyModel->ResetFilters();
|
||||
m_filterWidget->ResetAllFilters();
|
||||
}
|
||||
|
||||
void GemCatalogScreen::OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies)
|
||||
{
|
||||
if (m_notificationsEnabled)
|
||||
@@ -236,20 +312,22 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void GemCatalogScreen::FillModel(const QString& projectPath)
|
||||
{
|
||||
AZ::Outcome<QVector<GemInfo>, AZStd::string> allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath);
|
||||
m_projectPath = projectPath;
|
||||
|
||||
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath);
|
||||
if (allGemInfosResult.IsSuccess())
|
||||
{
|
||||
// Add all available gems to the model.
|
||||
const QVector<GemInfo> allGemInfos = allGemInfosResult.GetValue();
|
||||
const QVector<GemInfo>& allGemInfos = allGemInfosResult.GetValue();
|
||||
for (const GemInfo& gemInfo : allGemInfos)
|
||||
{
|
||||
m_gemModel->AddGem(gemInfo);
|
||||
}
|
||||
|
||||
AZ::Outcome<QVector<GemInfo>, AZStd::string> allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos();
|
||||
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allRepoGemInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoGemsInfos();
|
||||
if (allRepoGemInfosResult.IsSuccess())
|
||||
{
|
||||
const QVector<GemInfo> allRepoGemInfos = allRepoGemInfosResult.GetValue();
|
||||
const QVector<GemInfo>& allRepoGemInfos = allRepoGemInfosResult.GetValue();
|
||||
for (const GemInfo& gemInfo : allRepoGemInfos)
|
||||
{
|
||||
// do not add gems that have already been downloaded
|
||||
@@ -268,10 +346,10 @@ namespace O3DE::ProjectManager
|
||||
m_notificationsEnabled = false;
|
||||
|
||||
// Gather enabled gems for the given project.
|
||||
auto enabledGemNamesResult = PythonBindingsInterface::Get()->GetEnabledGemNames(projectPath);
|
||||
const auto& enabledGemNamesResult = PythonBindingsInterface::Get()->GetEnabledGemNames(projectPath);
|
||||
if (enabledGemNamesResult.IsSuccess())
|
||||
{
|
||||
const QVector<AZStd::string> enabledGemNames = enabledGemNamesResult.GetValue();
|
||||
const QVector<AZStd::string>& enabledGemNames = enabledGemNamesResult.GetValue();
|
||||
for (const AZStd::string& enabledGemName : enabledGemNames)
|
||||
{
|
||||
const QModelIndex modelIndex = m_gemModel->FindIndexByNameString(enabledGemName.c_str());
|
||||
@@ -331,12 +409,24 @@ namespace O3DE::ProjectManager
|
||||
|
||||
for (const QModelIndex& modelIndex : toBeAdded)
|
||||
{
|
||||
const QString gemPath = GemModel::GetPath(modelIndex);
|
||||
const QString& gemPath = GemModel::GetPath(modelIndex);
|
||||
|
||||
// make sure any remote gems we added were downloaded successfully
|
||||
if (GemModel::GetGemOrigin(modelIndex) == GemInfo::Remote && GemModel::GetDownloadStatus(modelIndex) != GemInfo::Downloaded)
|
||||
{
|
||||
QMessageBox::critical(
|
||||
nullptr, "Cannot add gem that isn't downloaded",
|
||||
tr("Cannot add gem %1 to project because it isn't downloaded yet or failed to download.")
|
||||
.arg(GemModel::GetDisplayName(modelIndex)));
|
||||
|
||||
return EnableDisableGemsResult::Failed;
|
||||
}
|
||||
|
||||
const AZ::Outcome<void, AZStd::string> result = pythonBindings->AddGemToProject(gemPath, projectPath);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
QMessageBox::critical(nullptr, "Operation failed",
|
||||
QString("Cannot add gem %1 to project.\n\nError:\n%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str()));
|
||||
QMessageBox::critical(nullptr, "Failed to add gem to project",
|
||||
tr("Cannot add gem %1 to project.<br><br>Error:<br>%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str()));
|
||||
|
||||
return EnableDisableGemsResult::Failed;
|
||||
}
|
||||
@@ -354,8 +444,8 @@ namespace O3DE::ProjectManager
|
||||
const AZ::Outcome<void, AZStd::string> result = pythonBindings->RemoveGemFromProject(gemPath, projectPath);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
QMessageBox::critical(nullptr, "Operation failed",
|
||||
QString("Cannot remove gem %1 from project.\n\nError:\n%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str()));
|
||||
QMessageBox::critical(nullptr, "Failed to remove gem from project",
|
||||
tr("Cannot remove gem %1 from project.<br><br>Error:<br>%2").arg(GemModel::GetDisplayName(modelIndex), result.GetError().c_str()));
|
||||
|
||||
return EnableDisableGemsResult::Failed;
|
||||
}
|
||||
@@ -366,23 +456,35 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void GemCatalogScreen::HandleOpenGemRepo()
|
||||
{
|
||||
QVector<QModelIndex> gemsToBeAdded = m_gemModel->GatherGemsToBeAdded(true);
|
||||
QVector<QModelIndex> gemsToBeRemoved = m_gemModel->GatherGemsToBeRemoved(true);
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::GemRepos);
|
||||
}
|
||||
|
||||
if (!gemsToBeAdded.empty() || !gemsToBeRemoved.empty())
|
||||
void GemCatalogScreen::OnGemDownloadResult(const QString& gemName, bool succeeded)
|
||||
{
|
||||
if (succeeded)
|
||||
{
|
||||
QMessageBox::StandardButton warningResult = QMessageBox::warning(
|
||||
nullptr, "Pending Changes",
|
||||
"There are some unsaved changes to the gem selection,<br> they will be lost if you change screens.<br> Are you sure?",
|
||||
QMessageBox::No | QMessageBox::Yes);
|
||||
|
||||
if (warningResult != QMessageBox::Yes)
|
||||
// refresh the information for downloaded gems
|
||||
const AZ::Outcome<QVector<GemInfo>, AZStd::string>& allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(m_projectPath);
|
||||
if (allGemInfosResult.IsSuccess())
|
||||
{
|
||||
return;
|
||||
// we should find the gem name now in all gem infos
|
||||
for (const GemInfo& gemInfo : allGemInfosResult.GetValue())
|
||||
{
|
||||
if (gemInfo.m_name == gemName)
|
||||
{
|
||||
QModelIndex index = m_gemModel->FindIndexByNameString(gemName);
|
||||
if (index.isValid())
|
||||
{
|
||||
m_gemModel->setData(index, GemInfo::Downloaded, GemModel::RoleDownloadStatus);
|
||||
m_gemModel->setData(index, gemInfo.m_path, GemModel::RolePath);
|
||||
m_gemModel->setData(index, gemInfo.m_path, GemModel::RoleDirectoryLink);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::GemRepos);
|
||||
}
|
||||
|
||||
ProjectManagerScreen GemCatalogScreen::GetScreenEnum()
|
||||
|
||||
@@ -49,6 +49,8 @@ namespace O3DE::ProjectManager
|
||||
void OnGemStatusChanged(const QString& gemName, uint32_t numChangedDependencies);
|
||||
void OnAddGemClicked();
|
||||
void SelectGem(const QString& gemName);
|
||||
void OnGemDownloadResult(const QString& gemName, bool succeeded = true);
|
||||
void Refresh();
|
||||
|
||||
protected:
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
@@ -75,5 +77,6 @@ namespace O3DE::ProjectManager
|
||||
DownloadController* m_downloadController = nullptr;
|
||||
bool m_notificationsEnabled = true;
|
||||
QSet<QString> m_gemsToRegisterWithProject;
|
||||
QString m_projectPath = nullptr;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -106,10 +106,10 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
|
||||
// Depending gems
|
||||
QStringList dependingGems = m_model->GetDependingGemNames(modelIndex);
|
||||
if (!dependingGems.isEmpty())
|
||||
const QVector<Tag>& dependingGemTags = m_model->GetDependingGemTags(modelIndex);
|
||||
if (!dependingGemTags.isEmpty())
|
||||
{
|
||||
m_dependingGems->Update(tr("Depending Gems"), tr("The following Gems will be automatically enabled with this Gem."), dependingGems);
|
||||
m_dependingGems->Update(tr("Depending Gems"), tr("The following Gems will be automatically enabled with this Gem."), dependingGemTags);
|
||||
m_dependingGems->show();
|
||||
}
|
||||
else
|
||||
@@ -120,7 +120,8 @@ namespace O3DE::ProjectManager
|
||||
// Additional information
|
||||
m_versionLabel->setText(tr("Gem Version: %1").arg(m_model->GetVersion(modelIndex)));
|
||||
m_lastUpdatedLabel->setText(tr("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex)));
|
||||
m_binarySizeLabel->setText(tr("Binary Size: %1 KB").arg(m_model->GetBinarySizeInKB(modelIndex)));
|
||||
const int binarySize = m_model->GetBinarySizeInKB(modelIndex);
|
||||
m_binarySizeLabel->setText(tr("Binary Size: %1").arg(binarySize ? tr("%1 KB").arg(binarySize) : tr("Unknown")));
|
||||
|
||||
m_mainWidget->adjustSize();
|
||||
m_mainWidget->show();
|
||||
@@ -222,7 +223,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
// Depending gems
|
||||
m_dependingGems = new GemsSubWidget();
|
||||
connect(m_dependingGems, &GemsSubWidget::TagClicked, this, [=](const QString& tag){ emit TagClicked(tag); });
|
||||
connect(m_dependingGems, &GemsSubWidget::TagClicked, this, [=](const Tag& tag){ emit TagClicked(tag); });
|
||||
m_mainLayout->addWidget(m_dependingGems);
|
||||
m_mainLayout->addSpacing(20);
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace O3DE::ProjectManager
|
||||
inline constexpr static const char* s_textColor = "#DDDDDD";
|
||||
|
||||
signals:
|
||||
void TagClicked(const QString& tag);
|
||||
void TagClicked(const Tag& tag);
|
||||
|
||||
private slots:
|
||||
void OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace O3DE::ProjectManager
|
||||
: QStandardItemModel(parent)
|
||||
{
|
||||
m_selectionModel = new QItemSelectionModel(this, parent);
|
||||
connect(this, &QAbstractItemModel::rowsAboutToBeRemoved, this, &GemModel::OnRowsAboutToBeRemoved);
|
||||
}
|
||||
|
||||
QItemSelectionModel* GemModel::GetSelectionModel() const
|
||||
@@ -64,7 +65,6 @@ namespace O3DE::ProjectManager
|
||||
appendRow(item);
|
||||
|
||||
const QModelIndex modelIndex = index(rowCount()-1, 0);
|
||||
m_nameToIndexMap[gemInfo.m_displayName] = modelIndex;
|
||||
m_nameToIndexMap[gemInfo.m_name] = modelIndex;
|
||||
}
|
||||
|
||||
@@ -177,18 +177,6 @@ namespace O3DE::ProjectManager
|
||||
return {};
|
||||
}
|
||||
|
||||
void GemModel::FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames)
|
||||
{
|
||||
for (QString& name : inOutGemNames)
|
||||
{
|
||||
QModelIndex modelIndex = FindIndexByNameString(name);
|
||||
if (modelIndex.isValid())
|
||||
{
|
||||
name = GetDisplayName(modelIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QStringList GemModel::GetDependingGems(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleDependingGems).toStringList();
|
||||
@@ -208,16 +196,23 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
}
|
||||
|
||||
QStringList GemModel::GetDependingGemNames(const QModelIndex& modelIndex)
|
||||
QVector<Tag> GemModel::GetDependingGemTags(const QModelIndex& modelIndex)
|
||||
{
|
||||
QStringList result = GetDependingGems(modelIndex);
|
||||
if (result.isEmpty())
|
||||
QVector<Tag> tags;
|
||||
|
||||
QStringList dependingGemNames = GetDependingGems(modelIndex);
|
||||
tags.reserve(dependingGemNames.size());
|
||||
|
||||
for (QString& gemName : dependingGemNames)
|
||||
{
|
||||
return {};
|
||||
const QModelIndex& dependingIndex = FindIndexByNameString(gemName);
|
||||
if (dependingIndex.isValid())
|
||||
{
|
||||
tags.push_back({ GetDisplayName(dependingIndex), GetName(dependingIndex) });
|
||||
}
|
||||
}
|
||||
|
||||
FindGemDisplayNamesByNameStrings(result);
|
||||
return result;
|
||||
return tags;
|
||||
}
|
||||
|
||||
QString GemModel::GetVersion(const QModelIndex& modelIndex)
|
||||
@@ -372,6 +367,16 @@ namespace O3DE::ProjectManager
|
||||
gemModel->emit gemStatusChanged(gemName, numChangedDependencies);
|
||||
}
|
||||
|
||||
void GemModel::OnRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last)
|
||||
{
|
||||
for (int i = first; i <= last; ++i)
|
||||
{
|
||||
QModelIndex modelIndex = index(i, 0, parent);
|
||||
const QString& gemName = GetName(modelIndex);
|
||||
m_nameToIndexMap.remove(gemName);
|
||||
}
|
||||
}
|
||||
|
||||
void GemModel::SetIsAddedDependency(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isAdded)
|
||||
{
|
||||
model.setData(modelIndex, isAdded, RoleIsAddedDependency);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <GemCatalog/GemInfo.h>
|
||||
#include <TagWidget.h>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QStandardItemModel>
|
||||
#include <QItemSelectionModel>
|
||||
@@ -26,12 +27,39 @@ namespace O3DE::ProjectManager
|
||||
explicit GemModel(QObject* parent = nullptr);
|
||||
QItemSelectionModel* GetSelectionModel() const;
|
||||
|
||||
enum UserRole
|
||||
{
|
||||
RoleName = Qt::UserRole,
|
||||
RoleDisplayName,
|
||||
RoleCreator,
|
||||
RoleGemOrigin,
|
||||
RolePlatforms,
|
||||
RoleSummary,
|
||||
RoleWasPreviouslyAdded,
|
||||
RoleWasPreviouslyAddedDependency,
|
||||
RoleIsAdded,
|
||||
RoleIsAddedDependency,
|
||||
RoleDirectoryLink,
|
||||
RoleDocLink,
|
||||
RoleDependingGems,
|
||||
RoleVersion,
|
||||
RoleLastUpdated,
|
||||
RoleBinarySize,
|
||||
RoleFeatures,
|
||||
RoleTypes,
|
||||
RolePath,
|
||||
RoleRequirement,
|
||||
RoleDownloadStatus,
|
||||
RoleLicenseText,
|
||||
RoleLicenseLink
|
||||
};
|
||||
|
||||
void AddGem(const GemInfo& gemInfo);
|
||||
void Clear();
|
||||
void UpdateGemDependencies();
|
||||
|
||||
QModelIndex FindIndexByNameString(const QString& nameString) const;
|
||||
QStringList GetDependingGemNames(const QModelIndex& modelIndex);
|
||||
QVector<Tag> GetDependingGemTags(const QModelIndex& modelIndex);
|
||||
bool HasDependentGems(const QModelIndex& modelIndex) const;
|
||||
|
||||
static QString GetName(const QModelIndex& modelIndex);
|
||||
@@ -82,38 +110,13 @@ namespace O3DE::ProjectManager
|
||||
signals:
|
||||
void gemStatusChanged(const QString& gemName, uint32_t numChangedDependencies);
|
||||
|
||||
protected slots:
|
||||
void OnRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last);
|
||||
|
||||
private:
|
||||
void FindGemDisplayNamesByNameStrings(QStringList& inOutGemNames);
|
||||
void GetAllDependingGems(const QModelIndex& modelIndex, QSet<QModelIndex>& inOutGems);
|
||||
QStringList GetDependingGems(const QModelIndex& modelIndex);
|
||||
|
||||
enum UserRole
|
||||
{
|
||||
RoleName = Qt::UserRole,
|
||||
RoleDisplayName,
|
||||
RoleCreator,
|
||||
RoleGemOrigin,
|
||||
RolePlatforms,
|
||||
RoleSummary,
|
||||
RoleWasPreviouslyAdded,
|
||||
RoleWasPreviouslyAddedDependency,
|
||||
RoleIsAdded,
|
||||
RoleIsAddedDependency,
|
||||
RoleDirectoryLink,
|
||||
RoleDocLink,
|
||||
RoleDependingGems,
|
||||
RoleVersion,
|
||||
RoleLastUpdated,
|
||||
RoleBinarySize,
|
||||
RoleFeatures,
|
||||
RoleTypes,
|
||||
RolePath,
|
||||
RoleRequirement,
|
||||
RoleDownloadStatus,
|
||||
RoleLicenseText,
|
||||
RoleLicenseLink
|
||||
};
|
||||
|
||||
QHash<QString, QModelIndex> m_nameToIndexMap;
|
||||
QItemSelectionModel* m_selectionModel = nullptr;
|
||||
QHash<QString, QSet<QModelIndex>> m_gemDependencyMap;
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
|
||||
// Included Gems
|
||||
m_includedGems->Update(tr("Included Gems"), "", m_model->GetIncludedGemNames(modelIndex));
|
||||
m_includedGems->Update(tr("Included Gems"), "", m_model->GetIncludedGemTags(modelIndex));
|
||||
|
||||
m_mainWidget->adjustSize();
|
||||
m_mainWidget->show();
|
||||
|
||||
@@ -103,17 +103,17 @@ namespace O3DE::ProjectManager
|
||||
return modelIndex.data(RoleIncludedGems).toStringList();
|
||||
}
|
||||
|
||||
QStringList GemRepoModel::GetIncludedGemNames(const QModelIndex& modelIndex)
|
||||
QVector<Tag> GemRepoModel::GetIncludedGemTags(const QModelIndex& modelIndex)
|
||||
{
|
||||
QStringList gemNames;
|
||||
QVector<GemInfo> gemInfos = GetIncludedGemInfos(modelIndex);
|
||||
|
||||
QVector<Tag> tags;
|
||||
const QVector<GemInfo>& gemInfos = GetIncludedGemInfos(modelIndex);
|
||||
tags.reserve(gemInfos.size());
|
||||
for (const GemInfo& gemInfo : gemInfos)
|
||||
{
|
||||
gemNames.append(gemInfo.m_displayName);
|
||||
tags.append({ gemInfo.m_displayName, gemInfo.m_name });
|
||||
}
|
||||
|
||||
return gemNames;
|
||||
return tags;
|
||||
}
|
||||
|
||||
QVector<GemInfo> GemRepoModel::GetIncludedGemInfos(const QModelIndex& modelIndex)
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace O3DE::ProjectManager
|
||||
static QString GetPath(const QModelIndex& modelIndex);
|
||||
|
||||
static QStringList GetIncludedGemPaths(const QModelIndex& modelIndex);
|
||||
static QStringList GetIncludedGemNames(const QModelIndex& modelIndex);
|
||||
static QVector<Tag> GetIncludedGemTags(const QModelIndex& modelIndex);
|
||||
static QVector<GemInfo> GetIncludedGemInfos(const QModelIndex& modelIndex);
|
||||
|
||||
static bool IsEnabled(const QModelIndex& modelIndex);
|
||||
|
||||
@@ -52,6 +52,11 @@ namespace O3DE::ProjectManager
|
||||
Reinit();
|
||||
}
|
||||
|
||||
void GemRepoScreen::NotifyCurrentScreen()
|
||||
{
|
||||
Reinit();
|
||||
}
|
||||
|
||||
void GemRepoScreen::Reinit()
|
||||
{
|
||||
m_gemRepoModel->clear();
|
||||
@@ -91,6 +96,7 @@ namespace O3DE::ProjectManager
|
||||
if (addGemRepoResult)
|
||||
{
|
||||
Reinit();
|
||||
emit OnRefresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -116,6 +122,7 @@ namespace O3DE::ProjectManager
|
||||
if (removeGemRepoResult)
|
||||
{
|
||||
Reinit();
|
||||
emit OnRefresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -130,6 +137,7 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
bool refreshResult = PythonBindingsInterface::Get()->RefreshAllGemRepos();
|
||||
Reinit();
|
||||
emit OnRefresh();
|
||||
|
||||
if (!refreshResult)
|
||||
{
|
||||
@@ -146,6 +154,7 @@ namespace O3DE::ProjectManager
|
||||
if (refreshResult.IsSuccess())
|
||||
{
|
||||
Reinit();
|
||||
emit OnRefresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace O3DE::ProjectManager
|
||||
class GemRepoScreen
|
||||
: public ScreenWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GemRepoScreen(QWidget* parent = nullptr);
|
||||
~GemRepoScreen() = default;
|
||||
@@ -37,12 +38,18 @@ namespace O3DE::ProjectManager
|
||||
|
||||
GemRepoModel* GetGemRepoModel() const { return m_gemRepoModel; }
|
||||
|
||||
void NotifyCurrentScreen() override;
|
||||
|
||||
signals:
|
||||
void OnRefresh();
|
||||
|
||||
public slots:
|
||||
void HandleAddRepoButton();
|
||||
void HandleRemoveRepoButton(const QModelIndex& modelIndex);
|
||||
void HandleRefreshAllButton();
|
||||
void HandleRefreshRepoButton(const QModelIndex& modelIndex);
|
||||
|
||||
|
||||
private:
|
||||
void FillModel();
|
||||
QFrame* CreateNoReposContent();
|
||||
|
||||
@@ -33,14 +33,14 @@ namespace O3DE::ProjectManager
|
||||
m_layout->addWidget(m_textLabel);
|
||||
|
||||
m_tagWidget = new TagContainerWidget();
|
||||
connect(m_tagWidget, &TagContainerWidget::TagClicked, this, [=](const QString& tag){ emit TagClicked(tag); });
|
||||
connect(m_tagWidget, &TagContainerWidget::TagClicked, this, [=](const Tag& tag){ emit TagClicked(tag); });
|
||||
m_layout->addWidget(m_tagWidget);
|
||||
}
|
||||
|
||||
void GemsSubWidget::Update(const QString& title, const QString& text, const QStringList& gemNames)
|
||||
void GemsSubWidget::Update(const QString& title, const QString& text, const QVector<Tag>& tags)
|
||||
{
|
||||
m_titleLabel->setText(title);
|
||||
m_textLabel->setText(text);
|
||||
m_tagWidget->Update(gemNames);
|
||||
m_tagWidget->Update(tags);
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -26,10 +26,10 @@ namespace O3DE::ProjectManager
|
||||
|
||||
public:
|
||||
GemsSubWidget(QWidget* parent = nullptr);
|
||||
void Update(const QString& title, const QString& text, const QStringList& gemNames);
|
||||
void Update(const QString& title, const QString& text, const QVector<Tag>& tags);
|
||||
|
||||
signals:
|
||||
void TagClicked(const QString& tag);
|
||||
void TagClicked(const Tag& tag);
|
||||
|
||||
private:
|
||||
QLabel* m_titleLabel = nullptr;
|
||||
|
||||
@@ -53,6 +53,8 @@ namespace Platform
|
||||
|
||||
#define Py_To_String(obj) pybind11::str(obj).cast<std::string>().c_str()
|
||||
#define Py_To_String_Optional(dict, key, default_string) dict.contains(key) ? Py_To_String(dict[key]) : default_string
|
||||
#define Py_To_Int(obj) obj.cast<int>()
|
||||
#define Py_To_Int_Optional(dict, key, default_int) dict.contains(key) ? Py_To_Int(dict[key]) : default_int
|
||||
#define QString_To_Py_String(value) pybind11::str(value.toStdString())
|
||||
#define QString_To_Py_Path(value) m_pathlib.attr("Path")(value.toStdString())
|
||||
|
||||
@@ -705,7 +707,9 @@ namespace O3DE::ProjectManager
|
||||
// optional
|
||||
gemInfo.m_displayName = Py_To_String_Optional(data, "display_name", gemInfo.m_name);
|
||||
gemInfo.m_summary = Py_To_String_Optional(data, "summary", "");
|
||||
gemInfo.m_version = "";
|
||||
gemInfo.m_version = Py_To_String_Optional(data, "version", gemInfo.m_version);
|
||||
gemInfo.m_lastUpdatedDate = Py_To_String_Optional(data, "last_updated", gemInfo.m_lastUpdatedDate);
|
||||
gemInfo.m_binarySizeInKB = Py_To_Int_Optional(data, "binary_size", gemInfo.m_binarySizeInKB);
|
||||
gemInfo.m_requirement = Py_To_String_Optional(data, "requirements", "");
|
||||
gemInfo.m_creator = Py_To_String_Optional(data, "origin", "");
|
||||
gemInfo.m_documentationLink = Py_To_String_Optional(data, "documentation_url", "");
|
||||
|
||||
@@ -12,15 +12,16 @@
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
TagWidget::TagWidget(const QString& text, QWidget* parent)
|
||||
: QLabel(text, parent)
|
||||
TagWidget::TagWidget(const Tag& tag, QWidget* parent)
|
||||
: QLabel(tag.text, parent)
|
||||
, m_tag(tag)
|
||||
{
|
||||
setObjectName("TagWidget");
|
||||
}
|
||||
|
||||
void TagWidget::mousePressEvent([[maybe_unused]] QMouseEvent* event)
|
||||
{
|
||||
emit(TagClicked(text()));
|
||||
emit TagClicked(m_tag);
|
||||
}
|
||||
|
||||
TagContainerWidget::TagContainerWidget(QWidget* parent)
|
||||
@@ -39,20 +40,34 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void TagContainerWidget::Update(const QStringList& tags)
|
||||
{
|
||||
FlowLayout* flowLayout = static_cast<FlowLayout*>(layout());
|
||||
Clear();
|
||||
|
||||
// remove old tags
|
||||
foreach (const QString& tag, tags)
|
||||
{
|
||||
TagWidget* tagWidget = new TagWidget({tag, tag});
|
||||
connect(tagWidget, &TagWidget::TagClicked, this, [=](const Tag& clickedTag){ emit TagClicked(clickedTag); });
|
||||
layout()->addWidget(tagWidget);
|
||||
}
|
||||
}
|
||||
|
||||
void TagContainerWidget::Update(const QVector<Tag>& tags)
|
||||
{
|
||||
Clear();
|
||||
|
||||
foreach (const Tag& tag, tags)
|
||||
{
|
||||
TagWidget* tagWidget = new TagWidget(tag);
|
||||
connect(tagWidget, &TagWidget::TagClicked, this, [=](const Tag& clickedTag){ emit TagClicked(clickedTag); });
|
||||
layout()->addWidget(tagWidget);
|
||||
}
|
||||
}
|
||||
|
||||
void TagContainerWidget::Clear()
|
||||
{
|
||||
QLayoutItem* layoutItem = nullptr;
|
||||
while ((layoutItem = layout()->takeAt(0)) != nullptr)
|
||||
{
|
||||
layoutItem->widget()->deleteLater();
|
||||
}
|
||||
|
||||
foreach (const QString& tag, tags)
|
||||
{
|
||||
TagWidget* tagWidget = new TagWidget(tag);
|
||||
connect(tagWidget, &TagWidget::TagClicked, this, [=](const QString& tag){ emit TagClicked(tag); });
|
||||
flowLayout->addWidget(tagWidget);
|
||||
}
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -10,12 +10,19 @@
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QLabel>
|
||||
#include <QStringList>
|
||||
#include <QWidget>
|
||||
#include <QVector>
|
||||
#include <QStringList>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
struct Tag
|
||||
{
|
||||
QString text;
|
||||
QString id;
|
||||
};
|
||||
|
||||
// Single tag
|
||||
class TagWidget
|
||||
: public QLabel
|
||||
@@ -23,14 +30,17 @@ namespace O3DE::ProjectManager
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
explicit TagWidget(const QString& text, QWidget* parent = nullptr);
|
||||
explicit TagWidget(const Tag& id, QWidget* parent = nullptr);
|
||||
~TagWidget() = default;
|
||||
|
||||
signals:
|
||||
void TagClicked(const QString& tag);
|
||||
void TagClicked(const Tag& tag);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
|
||||
private:
|
||||
Tag m_tag;
|
||||
};
|
||||
|
||||
// Widget containing multiple tags, automatically wrapping based on the size
|
||||
@@ -43,9 +53,13 @@ namespace O3DE::ProjectManager
|
||||
explicit TagContainerWidget(QWidget* parent = nullptr);
|
||||
~TagContainerWidget() = default;
|
||||
|
||||
void Update(const QVector<Tag>& tags);
|
||||
void Update(const QStringList& tags);
|
||||
|
||||
signals:
|
||||
void TagClicked(const QString& tag);
|
||||
void TagClicked(const Tag& tag);
|
||||
|
||||
private:
|
||||
void Clear();
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <GemCatalog/GemCatalogScreen.h>
|
||||
#include <GemRepo/GemRepoScreen.h>
|
||||
#include <ProjectManagerDefs.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <ScreenHeaderWidget.h>
|
||||
@@ -39,10 +40,10 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_updateSettingsScreen = new UpdateProjectSettingsScreen();
|
||||
m_gemCatalogScreen = new GemCatalogScreen();
|
||||
m_gemRepoScreen = new GemRepoScreen(this);
|
||||
|
||||
connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, [this](ProjectManagerScreen screen){
|
||||
emit ChangeScreenRequest(screen);
|
||||
});
|
||||
connect(m_gemCatalogScreen, &ScreenWidget::ChangeScreenRequest, this, &UpdateProjectCtrl::OnChangeScreenRequest);
|
||||
connect(m_gemRepoScreen, &GemRepoScreen::OnRefresh, m_gemCatalogScreen, &GemCatalogScreen::Refresh);
|
||||
|
||||
m_stack = new QStackedWidget(this);
|
||||
m_stack->setObjectName("body");
|
||||
@@ -69,6 +70,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_stack->addWidget(topBarFrameWidget);
|
||||
m_stack->addWidget(m_gemCatalogScreen);
|
||||
m_stack->addWidget(m_gemRepoScreen);
|
||||
|
||||
QDialogButtonBox* backNextButtons = new QDialogButtonBox();
|
||||
backNextButtons->setObjectName("footer");
|
||||
@@ -100,6 +102,22 @@ namespace O3DE::ProjectManager
|
||||
|
||||
// Gather the available gems that will be shown in the gem catalog.
|
||||
m_gemCatalogScreen->ReinitForProject(m_projectInfo.m_path);
|
||||
|
||||
// make sure the gem repo has the latest repo details
|
||||
m_gemRepoScreen->Reinit();
|
||||
}
|
||||
|
||||
void UpdateProjectCtrl::OnChangeScreenRequest(ProjectManagerScreen screen)
|
||||
{
|
||||
if (screen == ProjectManagerScreen::GemRepos)
|
||||
{
|
||||
m_stack->setCurrentWidget(m_gemRepoScreen);
|
||||
Update();
|
||||
}
|
||||
else
|
||||
{
|
||||
emit ChangeScreenRequest(screen);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateProjectCtrl::HandleGemsButton()
|
||||
@@ -145,6 +163,7 @@ namespace O3DE::ProjectManager
|
||||
QMessageBox::critical(this, tr("Gems downloading"), tr("You must wait for gems to finish downloading before continuing."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Enable or disable the gems that got adjusted in the gem catalog and apply them to the given project.
|
||||
const GemCatalogScreen::EnableDisableGemsResult result = m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path);
|
||||
if (result == GemCatalogScreen::EnableDisableGemsResult::Failed)
|
||||
@@ -181,18 +200,26 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void UpdateProjectCtrl::Update()
|
||||
{
|
||||
if (m_stack->currentIndex() == ScreenOrder::Gems)
|
||||
if (m_stack->currentIndex() == ScreenOrder::GemRepos)
|
||||
{
|
||||
m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName()));
|
||||
m_header->setSubTitle(QString(tr("Gem Repositories")));
|
||||
m_nextButton->setVisible(false);
|
||||
}
|
||||
else if (m_stack->currentIndex() == ScreenOrder::Gems)
|
||||
{
|
||||
|
||||
m_header->setTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName()));
|
||||
m_header->setSubTitle(QString(tr("Configure Gems")));
|
||||
m_nextButton->setText(tr("Save"));
|
||||
m_nextButton->setVisible(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_header->setTitle("");
|
||||
m_header->setSubTitle(QString(tr("Edit Project Settings: \"%1\"")).arg(m_projectInfo.GetProjectDisplayName()));
|
||||
m_nextButton->setText(tr("Save"));
|
||||
m_nextButton->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,11 @@ namespace O3DE::ProjectManager
|
||||
QT_FORWARD_DECLARE_CLASS(ScreenHeader)
|
||||
QT_FORWARD_DECLARE_CLASS(UpdateProjectSettingsScreen)
|
||||
QT_FORWARD_DECLARE_CLASS(GemCatalogScreen)
|
||||
QT_FORWARD_DECLARE_CLASS(GemRepoScreen)
|
||||
|
||||
class UpdateProjectCtrl : public ScreenWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UpdateProjectCtrl(QWidget* parent = nullptr);
|
||||
~UpdateProjectCtrl() = default;
|
||||
@@ -37,6 +39,7 @@ namespace O3DE::ProjectManager
|
||||
void HandleBackButton();
|
||||
void HandleNextButton();
|
||||
void HandleGemsButton();
|
||||
void OnChangeScreenRequest(ProjectManagerScreen screen);
|
||||
void UpdateCurrentProject(const QString& projectPath);
|
||||
|
||||
private:
|
||||
@@ -47,13 +50,15 @@ namespace O3DE::ProjectManager
|
||||
enum ScreenOrder
|
||||
{
|
||||
Settings,
|
||||
Gems
|
||||
Gems,
|
||||
GemRepos
|
||||
};
|
||||
|
||||
ScreenHeader* m_header = nullptr;
|
||||
QStackedWidget* m_stack = nullptr;
|
||||
UpdateProjectSettingsScreen* m_updateSettingsScreen = nullptr;
|
||||
GemCatalogScreen* m_gemCatalogScreen = nullptr;
|
||||
GemRepoScreen* m_gemRepoScreen = nullptr;
|
||||
|
||||
QPushButton* m_backButton = nullptr;
|
||||
QPushButton* m_nextButton = nullptr;
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
ProcessingOverlayWidget::ProcessingOverlayWidget(UI::OverlayWidget* overlay, Layout layout, Uuid traceTag)
|
||||
: QWidget()
|
||||
: QWidget(nullptr, Qt::Tool | Qt::WindowStaysOnTopHint)
|
||||
, m_traceTag(traceTag)
|
||||
, ui(new Ui::ProcessingOverlayWidget())
|
||||
, m_overlay(overlay)
|
||||
|
||||
@@ -100,13 +100,6 @@ namespace ImageProcessingAtom
|
||||
//compare whether two images are same. return true if they are same.
|
||||
virtual bool CompareImage(const IImageObjectPtr otherImage) const = 0;
|
||||
|
||||
// Writes this image to file used for runtime, overwrites any existing file.
|
||||
// It may write alpha image as attached image into the same file
|
||||
// outFilePaths will save filenames finally saved to since the image might be split and saved to multiple files
|
||||
virtual bool SaveImage(const char* filename, IImageObjectPtr alphaImage, AZStd::vector<AZStd::string>& outFilePaths) const = 0;
|
||||
virtual bool SaveImage(AZ::IO::SystemFileStream& out) const = 0;
|
||||
virtual bool SaveMipToFile(AZ::u32 mip, const AZStd::string& filename) const = 0;
|
||||
|
||||
//get total image data size in memory of all mipmaps. Not includs header and flags.
|
||||
virtual AZ::u32 GetTextureMemory() const = 0;
|
||||
|
||||
@@ -135,9 +128,6 @@ namespace ImageProcessingAtom
|
||||
// The algorithm is based on the Frequency Domain Normal Mapping implementation presented by Neubelt and Pettineo at Siggraph 2013.
|
||||
virtual void GlossFromNormals(bool hasAuthoredGloss) = 0;
|
||||
|
||||
//convert gloss map from legacy distribution to new one. New World is still using legacy gloss map.
|
||||
virtual void ConvertLegacyGloss() = 0;
|
||||
|
||||
//clear image with color
|
||||
virtual void ClearColor(float r, float g, float b, float a) = 0;
|
||||
};
|
||||
|
||||
@@ -45,10 +45,7 @@ namespace ImageProcessingAtom
|
||||
->Field("MinTextureSize", &PresetSettings::m_minTextureSize)
|
||||
->Field("IsPowerOf2", &PresetSettings::m_isPowerOf2)
|
||||
->Field("SizeReduceLevel", &PresetSettings::m_sizeReduceLevel)
|
||||
->Field("IsColorChart", &PresetSettings::m_isColorChart)
|
||||
->Field("HighPassMip", &PresetSettings::m_highPassMip)
|
||||
->Field("GlossFromNormal", &PresetSettings::m_glossFromNormals)
|
||||
->Field("UseLegacyGloss", &PresetSettings::m_isLegacyGloss)
|
||||
->Field("MipRenormalize", &PresetSettings::m_isMipRenormalize)
|
||||
->Field("NumberResidentMips", &PresetSettings::m_numResidentMips)
|
||||
->Field("Swizzle", &PresetSettings::m_swizzle)
|
||||
@@ -200,10 +197,7 @@ namespace ImageProcessingAtom
|
||||
m_maxTextureSize == other.m_maxTextureSize &&
|
||||
m_isPowerOf2 == other.m_isPowerOf2 &&
|
||||
m_sizeReduceLevel == other.m_sizeReduceLevel &&
|
||||
m_isColorChart == other.m_isColorChart &&
|
||||
m_highPassMip == other.m_highPassMip &&
|
||||
m_glossFromNormals == other.m_glossFromNormals &&
|
||||
m_isLegacyGloss == other.m_isLegacyGloss &&
|
||||
m_swizzle == other.m_swizzle &&
|
||||
m_isMipRenormalize == other.m_isMipRenormalize &&
|
||||
m_numResidentMips == other.m_numResidentMips;
|
||||
@@ -239,10 +233,7 @@ namespace ImageProcessingAtom
|
||||
m_maxTextureSize = other.m_maxTextureSize;
|
||||
m_isPowerOf2 = other.m_isPowerOf2;
|
||||
m_sizeReduceLevel = other.m_sizeReduceLevel;
|
||||
m_isColorChart = other.m_isColorChart;
|
||||
m_highPassMip = other.m_highPassMip;
|
||||
m_glossFromNormals = other.m_glossFromNormals;
|
||||
m_isLegacyGloss = other.m_isLegacyGloss;
|
||||
m_swizzle = other.m_swizzle;
|
||||
m_isMipRenormalize = other.m_isMipRenormalize;
|
||||
m_numResidentMips = other.m_numResidentMips;
|
||||
|
||||
@@ -84,16 +84,7 @@ namespace ImageProcessingAtom
|
||||
|
||||
//settings for mipmap generation. it's null if this preset disable mipmap.
|
||||
AZStd::unique_ptr<MipmapSettings> m_mipmapSetting;
|
||||
|
||||
//some specific settings
|
||||
// "colorchart". This is to indicate if need to extract color chart from the image and output the color chart data.
|
||||
// This is very specific usage for cryEngine. Check ColorChart.cpp for better explanation.
|
||||
bool m_isColorChart = false;
|
||||
|
||||
//"highpass". Defines which mip level is subtracted when applying the high pass filter
|
||||
//this is only used for terrain asset. we might remove it later since it can be done with source image directly
|
||||
AZ::u32 m_highPassMip = 0;
|
||||
|
||||
|
||||
//"glossfromnormals". Bake normal variance into smoothness stored in alpha channel
|
||||
AZ::u32 m_glossFromNormals = 0;
|
||||
|
||||
@@ -109,10 +100,6 @@ namespace ImageProcessingAtom
|
||||
//that add up to 64K or lower
|
||||
AZ::u8 m_numResidentMips = 0;
|
||||
|
||||
//legacy options might be removed later
|
||||
//"glosslegacydist". If the gloss map use legacy distribution. NW is still using legacy dist
|
||||
bool m_isLegacyGloss = false;
|
||||
|
||||
//"swizzle". need to be 4 character and each character need to be one of "rgba01"
|
||||
AZStd::string m_swizzle;
|
||||
|
||||
|
||||
@@ -1,307 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <Processing/ImageObjectImpl.h>
|
||||
#include <Processing/ImageToProcess.h>
|
||||
|
||||
namespace ImageProcessingAtom
|
||||
{
|
||||
const int COLORCHART_IMAGE_WIDTH = 78;
|
||||
const int COLORCHART_IMAGE_HEIGHT = 66;
|
||||
|
||||
// color chart in cry engine is a special image data, with size 78x66, you may see in game screenshot which is defined by a rectangle
|
||||
// area with a yellow-black dash line boarder
|
||||
// Create color chart function is to read that block of image data and convert it to a color table then save it to another image
|
||||
// with size 256x16.
|
||||
|
||||
class C3dLutColorChart
|
||||
{
|
||||
public:
|
||||
C3dLutColorChart() {}
|
||||
~C3dLutColorChart() {};
|
||||
|
||||
//generate default color chart data
|
||||
void GenerateDefault();
|
||||
|
||||
//generate color chart data from input image
|
||||
bool GenerateFromInput(IImageObjectPtr image);
|
||||
|
||||
//ouput the color chart data to an image object
|
||||
IImageObjectPtr GenerateChartImage();
|
||||
|
||||
protected:
|
||||
//extract color chart data from specified location in an image
|
||||
void ExtractFromImageAt(IImageObjectPtr pImg, AZ::u32 x, AZ::u32 y);
|
||||
|
||||
//find color chart location in an image
|
||||
static bool FindColorChart(const IImageObjectPtr pImg, AZ::u32& outLocX, AZ::u32& outLocY);
|
||||
|
||||
//if there is a color chart at specified location
|
||||
static bool IsColorChartAt(AZ::u32 x, AZ::u32 y, void* pData, AZ::u32 pitch);
|
||||
|
||||
private:
|
||||
enum EPrimaryShades
|
||||
{
|
||||
ePS_Red = 16,
|
||||
ePS_Green = 16,
|
||||
ePS_Blue = 16,
|
||||
|
||||
ePS_NumColors = ePS_Red * ePS_Green * ePS_Blue
|
||||
};
|
||||
|
||||
struct SColor
|
||||
{
|
||||
unsigned char r, g, b, _padding;
|
||||
};
|
||||
|
||||
typedef AZStd::vector<SColor> ColorMapping;
|
||||
|
||||
ColorMapping m_mapping;
|
||||
};
|
||||
|
||||
void C3dLutColorChart::GenerateDefault()
|
||||
{
|
||||
m_mapping.reserve(ePS_NumColors);
|
||||
|
||||
for (int b = 0; b < ePS_Blue; ++b)
|
||||
{
|
||||
for (int g = 0; g < ePS_Green; ++g)
|
||||
{
|
||||
for (int r = 0; r < ePS_Red; ++r)
|
||||
{
|
||||
SColor col;
|
||||
col.r = static_cast<unsigned char>(255 * r / (ePS_Red));
|
||||
col.g = static_cast<unsigned char>(255 * g / (ePS_Green));
|
||||
col.b = static_cast<unsigned char>(255 * b / (ePS_Blue));
|
||||
int l = 255 - (col.r * 3 + col.g * 6 + col.b) / 10;
|
||||
col.r = col.g = col.b = (unsigned char)l;
|
||||
m_mapping.push_back(col);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//find color chart location in a image
|
||||
bool C3dLutColorChart::FindColorChart(const IImageObjectPtr pImg, AZ::u32& outLocX, AZ::u32& outLocY)
|
||||
{
|
||||
const AZ::u32 width = pImg->GetWidth(0);
|
||||
const AZ::u32 height = pImg->GetHeight(0);
|
||||
|
||||
//the origin image is too small to have a color chart
|
||||
if (width < COLORCHART_IMAGE_WIDTH || height < COLORCHART_IMAGE_HEIGHT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::u8* pData;
|
||||
AZ::u32 pitch;
|
||||
pImg->GetImagePointer(0, pData, pitch);
|
||||
|
||||
//check all the posible start location on whether there might be a color chart
|
||||
for (AZ::u32 y = 0; y <= height - COLORCHART_IMAGE_HEIGHT; ++y)
|
||||
{
|
||||
for (AZ::u32 x = 0; x <= width - COLORCHART_IMAGE_WIDTH; ++x)
|
||||
{
|
||||
if (IsColorChartAt(x, y, pData, pitch))
|
||||
{
|
||||
outLocX = x;
|
||||
outLocY = y;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool C3dLutColorChart::GenerateFromInput(IImageObjectPtr image)
|
||||
{
|
||||
AZ::u32 outLocX, outLocY;
|
||||
if (FindColorChart(image, outLocX, outLocY))
|
||||
{
|
||||
ExtractFromImageAt(image, outLocX, outLocY);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
IImageObjectPtr C3dLutColorChart::GenerateChartImage()
|
||||
{
|
||||
IImageObjectPtr image(IImageObject::CreateImage(ePS_Red* ePS_Blue, ePS_Green, 1, ePixelFormat_R8G8B8A8));
|
||||
|
||||
{
|
||||
AZ::u8* pData;
|
||||
AZ::u32 pitch;
|
||||
image->GetImagePointer(0, pData, pitch);
|
||||
|
||||
size_t nSlicePitch = (pitch / ePS_Blue);
|
||||
AZ::u32 src = 0;
|
||||
for (int b = 0; b < ePS_Blue; ++b)
|
||||
{
|
||||
for (int g = 0; g < ePS_Green; ++g)
|
||||
{
|
||||
AZ::u8* p = pData + g * pitch + b * nSlicePitch;
|
||||
for (int r = 0; r < ePS_Red; ++r)
|
||||
{
|
||||
const SColor& c = m_mapping[src];
|
||||
p[0] = c.r;
|
||||
p[1] = c.g;
|
||||
p[2] = c.b;
|
||||
p[3] = 255;
|
||||
++src;
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void C3dLutColorChart::ExtractFromImageAt(IImageObjectPtr image, AZ::u32 x, AZ::u32 y)
|
||||
{
|
||||
int ox = x + 1;
|
||||
int oy = y + 1;
|
||||
|
||||
AZ::u8* pData;
|
||||
AZ::u32 pitch;
|
||||
image->GetImagePointer(0, pData, pitch);
|
||||
|
||||
m_mapping.reserve(ePS_NumColors);
|
||||
|
||||
for (int b = 0; b < ePS_Blue; ++b)
|
||||
{
|
||||
int px = ox + ePS_Red * (b % 4);
|
||||
int py = oy + ePS_Green * (b / 4);
|
||||
|
||||
for (int g = 0; g < ePS_Green; ++g)
|
||||
{
|
||||
for (int r = 0; r < ePS_Red; ++r)
|
||||
{
|
||||
AZ::u8* p = pData + pitch * (py + g) + (px + r) * 4;
|
||||
|
||||
SColor col;
|
||||
col.r = p[0];
|
||||
col.g = p[1];
|
||||
col.b = p[2];
|
||||
m_mapping.push_back(col);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//check if image data at location x and y could be a color chart
|
||||
//based on if the boarder is dash lines with two pixel each segement
|
||||
//the idea and implementation are both coming from CryEngine.
|
||||
bool C3dLutColorChart::IsColorChartAt(AZ::u32 x, AZ::u32 y, void* pData, AZ::u32 pitch)
|
||||
{
|
||||
struct Color
|
||||
{
|
||||
private:
|
||||
int c[3];
|
||||
|
||||
public:
|
||||
Color(AZ::u32 x, AZ::u32 y, void* pPixels, AZ::u32 pitch)
|
||||
{
|
||||
const uint8* p = (const uint8*)pPixels + pitch * y + x * 4;
|
||||
c[0] = p[0];
|
||||
c[1] = p[1];
|
||||
c[2] = p[2];
|
||||
}
|
||||
|
||||
bool isSimilar(const Color& a, int maxDiff) const
|
||||
{
|
||||
return
|
||||
abs(a.c[0] - c[0]) <= maxDiff &&
|
||||
abs(a.c[1] - c[1]) <= maxDiff &&
|
||||
abs(a.c[2] - c[2]) <= maxDiff;
|
||||
}
|
||||
};
|
||||
|
||||
const Color colorRef[2] =
|
||||
{
|
||||
Color(x, y, pData, pitch),
|
||||
Color(x + 2, y, pData, pitch)
|
||||
};
|
||||
|
||||
// We require two colors of the border to be at least a bit different
|
||||
if (colorRef[0].isSimilar(colorRef[1], 15))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static const int kMaxDiff = 3;
|
||||
|
||||
int refIdx = 0;
|
||||
//rectangle's top
|
||||
for (int i = 0; i < COLORCHART_IMAGE_WIDTH; i += 2)
|
||||
{
|
||||
if (!colorRef[refIdx].isSimilar(Color(x + i, y, pData, pitch), kMaxDiff) ||
|
||||
!colorRef[refIdx].isSimilar(Color(x + i + 1, y, pData, pitch), kMaxDiff))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
refIdx ^= 1;
|
||||
}
|
||||
|
||||
refIdx = 0;
|
||||
//left
|
||||
for (int i = 0; i < COLORCHART_IMAGE_HEIGHT; i += 2)
|
||||
{
|
||||
if (!colorRef[refIdx].isSimilar(Color(x, y + i, pData, pitch), kMaxDiff) ||
|
||||
!colorRef[refIdx].isSimilar(Color(x, y + i + 1, pData, pitch), kMaxDiff))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
refIdx ^= 1;
|
||||
}
|
||||
|
||||
refIdx = 0;
|
||||
//right
|
||||
for (int i = 0; i < COLORCHART_IMAGE_HEIGHT; i += 2)
|
||||
{
|
||||
if (!colorRef[refIdx].isSimilar(Color(x + COLORCHART_IMAGE_WIDTH - 1, y + i, pData, pitch), kMaxDiff) ||
|
||||
!colorRef[refIdx].isSimilar(Color(x + COLORCHART_IMAGE_WIDTH - 1, y + i + 1, pData, pitch), kMaxDiff))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
refIdx ^= 1;
|
||||
}
|
||||
|
||||
refIdx = 0;
|
||||
//bottom
|
||||
for (int i = 0; i < COLORCHART_IMAGE_WIDTH; i += 2)
|
||||
{
|
||||
if (!colorRef[refIdx].isSimilar(Color(x + i, y + COLORCHART_IMAGE_HEIGHT - 1, pData, pitch), kMaxDiff) ||
|
||||
!colorRef[refIdx].isSimilar(Color(x + i + 1, y + COLORCHART_IMAGE_HEIGHT - 1, pData, pitch), kMaxDiff))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
refIdx ^= 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ImageToProcess::CreateColorChart()
|
||||
{
|
||||
C3dLutColorChart colorChart;
|
||||
|
||||
//get color chart data from source image.
|
||||
if (!colorChart.GenerateFromInput(m_img))
|
||||
{
|
||||
//if load from image failed then generate default color data
|
||||
colorChart.GenerateDefault();
|
||||
}
|
||||
|
||||
//save color chart data to an image and save as current
|
||||
m_img = colorChart.GenerateChartImage();
|
||||
}
|
||||
}
|
||||
@@ -547,7 +547,7 @@ namespace ImageProcessingAtom
|
||||
}
|
||||
|
||||
//generate box filtered source image mip chain
|
||||
IImageObjectPtr mippedSourceImage(IImageObject::CreateImage(outWidth, outHeight, maxMipCount, ePixelFormat_R32G32B32A32F));
|
||||
IImageObjectPtr mippedSourceImage(IImageObject::CreateImage(outWidth, outHeight, maxMipCount, srcPixelFormat));
|
||||
mippedSourceImage->CopyPropertiesFrom(m_image->Get());
|
||||
|
||||
for (int iSide = 0; iSide < 6; ++iSide)
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <Processing/ImageObjectImpl.h>
|
||||
#include <Processing/ImageToProcess.h>
|
||||
#include <Processing/ImageConvert.h>
|
||||
#include <Processing/PixelFormatInfo.h>
|
||||
#include <Converters/FIR-Windows.h>
|
||||
#include <Converters/PixelOperation.h>
|
||||
|
||||
namespace ImageProcessingAtom
|
||||
{
|
||||
// higher mip level is subtracted by lower mip level when applying the [cheap] high pass filter
|
||||
void ImageToProcess::CreateHighPass(AZ::u32 dwMipDown)
|
||||
{
|
||||
//no need to convert if mip go down 0
|
||||
if (dwMipDown == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const EPixelFormat ePixelFormat = m_img->GetPixelFormat();
|
||||
|
||||
if (ePixelFormat != ePixelFormat_R32G32B32A32F)
|
||||
{
|
||||
AZ_Assert(false, "You need convert the orginal image to ePixelFormat_R32G32B32A32F before call this function");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
AZ::u32 dwWidth, dwHeight, dwMips;
|
||||
dwWidth = m_img->GetWidth(0);
|
||||
dwHeight = m_img->GetHeight(0);
|
||||
dwMips = m_img->GetMipCount();
|
||||
|
||||
if (dwMipDown >= dwMips)
|
||||
{
|
||||
AZ_Warning("Image Processing", false, "CreateHighPass can't go down %i MIP levels for high pass as there are not\
|
||||
enough MIP levels available, going down by %i instead", dwMipDown, dwMips - 1);
|
||||
dwMipDown = dwMips - 1;
|
||||
}
|
||||
|
||||
IImageObjectPtr newImage(IImageObject::CreateImage(dwWidth, dwHeight, dwMips, ePixelFormat));
|
||||
newImage->CopyPropertiesFrom(m_img);
|
||||
|
||||
IPixelOperationPtr pixelOp = CreatePixelOperation(ePixelFormat);
|
||||
AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(ePixelFormat)->bitsPerBlock / 8;
|
||||
|
||||
AZ::u32 dstMips = newImage->GetMipCount();
|
||||
for (AZ::u32 dstMip = 0; dstMip < dwMipDown; ++dstMip)
|
||||
{
|
||||
// linear interpolation
|
||||
FilterImage(MipGenType::triangle, MipGenEvalType::sum, 0.0f, 0.0f, m_img, dwMipDown, newImage, dstMip, NULL, NULL);
|
||||
|
||||
//substraction
|
||||
AZ::u8* srcPixelBuf;
|
||||
AZ::u32 srcPitch;
|
||||
m_img->GetImagePointer(dstMip, srcPixelBuf, srcPitch);
|
||||
AZ::u8* dstPixelBuf;
|
||||
AZ::u32 dstPitch;
|
||||
newImage->GetImagePointer(dstMip, dstPixelBuf, dstPitch);
|
||||
const AZ::u32 pixelCount = newImage->GetPixelCount(dstMip);
|
||||
|
||||
for (AZ::u32 i = 0; i < pixelCount; ++i, srcPixelBuf += pixelBytes, dstPixelBuf += pixelBytes)
|
||||
{
|
||||
float r1, g1, b1, a1, r2, g2, b2, a2;
|
||||
pixelOp->GetRGBA(srcPixelBuf, r1, g1, b1, a1);
|
||||
pixelOp->GetRGBA(dstPixelBuf, r2, g2, b2, a2);
|
||||
|
||||
r2 = AZ::GetClamp<float>(r1 - r2 + 0.5f, 0.0f, 1.0f);
|
||||
g2 = AZ::GetClamp<float>(g1 - g2 + 0.5f, 0.0f, 1.0f);
|
||||
b2 = AZ::GetClamp<float>(b1 - b2 + 0.5f, 0.0f, 1.0f);
|
||||
a2 = AZ::GetClamp<float>(a1 - a2 + 0.5f, 0.0f, 1.0f);
|
||||
pixelOp->SetRGBA(dstPixelBuf, r2, g2, b2, a2);
|
||||
}
|
||||
}
|
||||
|
||||
// mips below the chosen highpass mip are grey
|
||||
for (AZ::u32 dstMip = dwMipDown; dstMip < dstMips; ++dstMip)
|
||||
{
|
||||
AZ::u8* dstPixelBuf;
|
||||
AZ::u32 dstPitch;
|
||||
newImage->GetImagePointer(dstMip, dstPixelBuf, dstPitch);
|
||||
const AZ::u32 pixelCount = newImage->GetPixelCount(dstMip);
|
||||
|
||||
for (AZ::u32 i = 0; i < pixelCount; ++i, dstPixelBuf += pixelBytes)
|
||||
{
|
||||
pixelOp->SetRGBA(dstPixelBuf, 0.5f, 0.5f, 0.5f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
m_img = newImage;
|
||||
}
|
||||
} // namespace ImageProcessingAtom
|
||||
@@ -82,10 +82,7 @@ namespace ImageProcessingAtomEditor
|
||||
presetInfoText += "\n";
|
||||
presetInfoText += QString("Suppress Engine Reduce: %1\n").arg(presetSettings->m_suppressEngineReduce ? "True" : "False");
|
||||
presetInfoText += QString("Discard Alpha: %1\n").arg(presetSettings->m_discardAlpha ? "True" : "False");
|
||||
presetInfoText += QString("Is Color Chart: %1\n").arg(presetSettings->m_isColorChart ? "True" : "False");
|
||||
presetInfoText += QString("High Pass Mip: %1\n").arg(presetSettings->m_highPassMip);
|
||||
presetInfoText += QString("Gloss From Normal: %1\n").arg(presetSettings->m_glossFromNormals);
|
||||
presetInfoText += QString("Use Legacy Gloss: %1\n").arg(presetSettings->m_isLegacyGloss ? "True" : "False");
|
||||
presetInfoText += QString("Mip Re-normalize: %1\n").arg(presetSettings->m_isMipRenormalize ? "True" : "False");
|
||||
presetInfoText += QString("Resident Mips Number: %1\n").arg(presetSettings->m_numResidentMips);
|
||||
presetInfoText += QString("Swizzle: %1\n").arg(presetSettings->m_swizzle.c_str());
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace ImageProcessingAtom
|
||||
builderDescriptor.m_busId = azrtti_typeid<ImageBuilderWorker>();
|
||||
builderDescriptor.m_createJobFunction = AZStd::bind(&ImageBuilderWorker::CreateJobs, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
builderDescriptor.m_processJobFunction = AZStd::bind(&ImageBuilderWorker::ProcessJob, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
||||
builderDescriptor.m_version = 25; // [ATOM-16575]
|
||||
builderDescriptor.m_version = 26; // [ATOM-15086]
|
||||
builderDescriptor.m_analysisFingerprint = ImageProcessingAtom::BuilderSettingManager::Instance()->GetAnalysisFingerprint();
|
||||
m_imageBuilder.BusConnect(builderDescriptor.m_busId);
|
||||
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDescriptor);
|
||||
|
||||
@@ -46,7 +46,6 @@ namespace ImageProcessingAtom
|
||||
enum ConvertStep
|
||||
{
|
||||
StepValidateInput = 0,
|
||||
StepGenerateColorChart,
|
||||
StepConvertToLinear,
|
||||
StepSwizzle,
|
||||
StepCubemapLayout,
|
||||
@@ -55,9 +54,7 @@ namespace ImageProcessingAtom
|
||||
StepMipmap,
|
||||
StepGlossFromNormal,
|
||||
StepPostNormalize,
|
||||
StepCreateHighPass,
|
||||
StepConvertOutputColorSpace,
|
||||
StepAlphaImage,
|
||||
StepConvertPixelFormat,
|
||||
StepSaveToFile,
|
||||
StepAll
|
||||
@@ -66,7 +63,6 @@ namespace ImageProcessingAtom
|
||||
[[maybe_unused]] const char ProcessStepNames[StepAll][64] =
|
||||
{
|
||||
"ValidateInput",
|
||||
"GenerateColorChart",
|
||||
"ConvertToLinear",
|
||||
"Swizzle",
|
||||
"CubemapLayout",
|
||||
@@ -75,9 +71,7 @@ namespace ImageProcessingAtom
|
||||
"Mipmap",
|
||||
"GlossFromNormal",
|
||||
"PostNormalize",
|
||||
"CreateHighPass",
|
||||
"ConvertOutputColorSpace",
|
||||
"AlphaImage",
|
||||
"ConvertPixelFormat",
|
||||
"SaveToFile",
|
||||
};
|
||||
@@ -94,11 +88,6 @@ namespace ImageProcessingAtom
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IImageObjectPtr ImageConvertProcess::GetOutputAlphaImage()
|
||||
{
|
||||
return m_alphaImage;
|
||||
}
|
||||
|
||||
IImageObjectPtr ImageConvertProcess::GetOutputIBLSpecularCubemap()
|
||||
{
|
||||
return m_iblSpecularCubemapImage;
|
||||
@@ -180,6 +169,58 @@ namespace ImageProcessingAtom
|
||||
m_image = new ImageToProcess(IImageObjectPtr(m_input->m_inputImage->Clone(mipsToClone)));
|
||||
}
|
||||
|
||||
break;
|
||||
case StepConvertToLinear:
|
||||
// convert to linear space and the output image pixel format should be rgba32f
|
||||
ConvertToLinear();
|
||||
break;
|
||||
case StepSwizzle:
|
||||
{
|
||||
// swizzle if swizzle was set or decard alpha
|
||||
bool swizzleWasSet = m_input->m_presetSetting.m_swizzle.size() >= 4;
|
||||
if (swizzleWasSet || m_input->m_presetSetting.m_discardAlpha)
|
||||
{
|
||||
AZStd::string swizzle = "rgba";
|
||||
if (swizzleWasSet)
|
||||
{
|
||||
swizzle = m_input->m_presetSetting.m_swizzle.substr(0, 4);
|
||||
}
|
||||
|
||||
if (m_input->m_presetSetting.m_discardAlpha)
|
||||
{
|
||||
swizzle[3] = '1';
|
||||
}
|
||||
|
||||
m_image->Get()->Swizzle(swizzle.c_str());
|
||||
if (!m_input->m_presetSetting.m_discardAlpha)
|
||||
{
|
||||
m_alphaContent = EAlphaContent::eAlphaContent_Absent;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_alphaContent = m_image->Get()->GetAlphaContent();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case StepCubemapLayout:
|
||||
// convert cubemap image's layout to vertical strip used in game.
|
||||
if (IsConvertToCubemap())
|
||||
{
|
||||
if (!m_image->ConvertCubemapLayout(CubemapLayoutVertical))
|
||||
{
|
||||
m_image->Set(nullptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case StepPreNormalize:
|
||||
// normalize base image before mipmap generation if glossfromnormals is enabled and require normalize
|
||||
if (m_input->m_presetSetting.m_isMipRenormalize && m_input->m_presetSetting.m_glossFromNormals)
|
||||
{
|
||||
// Normalize the base mip map. This has to be done explicitly because we need to disable mip renormalization to
|
||||
// preserve the normal length when deriving the normal variance
|
||||
m_image->Get()->NormalizeVectors(0, 1);
|
||||
}
|
||||
break;
|
||||
case StepGenerateIBL:
|
||||
if (IsConvertToCubemap())
|
||||
@@ -204,56 +245,6 @@ namespace ImageProcessingAtom
|
||||
m_isFinished = true;
|
||||
}
|
||||
break;
|
||||
case StepGenerateColorChart:
|
||||
// GenerateColorChart.
|
||||
if (m_input->m_presetSetting.m_isColorChart)
|
||||
{
|
||||
// Convert to uncompressed format if it's compressed format. For example, loaded from DDS file.
|
||||
if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_image->Get()->GetPixelFormat()))
|
||||
{
|
||||
m_image->ConvertFormat(ePixelFormat_R32G32B32A32F);
|
||||
}
|
||||
|
||||
m_image->CreateColorChart();
|
||||
}
|
||||
break;
|
||||
case StepConvertToLinear:
|
||||
// convert to linear space and the output image pixel format should be rgba32f
|
||||
ConvertToLinear();
|
||||
break;
|
||||
case StepSwizzle:
|
||||
// convert texture format.
|
||||
if (m_input->m_presetSetting.m_swizzle.size() >= 4)
|
||||
{
|
||||
m_image->Get()->Swizzle(m_input->m_presetSetting.m_swizzle.substr(0, 4).c_str());
|
||||
m_alphaContent = m_image->Get()->GetAlphaContent();
|
||||
}
|
||||
|
||||
// convert gloss map (alhpa channel) from legacy distribution to new one
|
||||
if (m_input->m_presetSetting.m_isLegacyGloss)
|
||||
{
|
||||
m_image->Get()->ConvertLegacyGloss();
|
||||
}
|
||||
break;
|
||||
case StepCubemapLayout:
|
||||
// convert cubemap image's layout to vertical strip used in game.
|
||||
if (IsConvertToCubemap())
|
||||
{
|
||||
if (!m_image->ConvertCubemapLayout(CubemapLayoutVertical))
|
||||
{
|
||||
m_image->Set(nullptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case StepPreNormalize:
|
||||
// normalize base image before mipmap generation if glossfromnormals is enabled and require normalize
|
||||
if (m_input->m_presetSetting.m_isMipRenormalize && m_input->m_presetSetting.m_glossFromNormals)
|
||||
{
|
||||
// Normalize the base mip map. This has to be done explicitly because we need to disable mip renormalization to
|
||||
// preserve the normal length when deriving the normal variance
|
||||
m_image->Get()->NormalizeVectors(0, 1);
|
||||
}
|
||||
break;
|
||||
case StepMipmap:
|
||||
// generate mipmaps
|
||||
if (IsConvertToCubemap())
|
||||
@@ -304,20 +295,10 @@ namespace ImageProcessingAtom
|
||||
m_image->Get()->AddImageFlags(EIF_RenormalizedTexture);
|
||||
}
|
||||
break;
|
||||
case StepCreateHighPass:
|
||||
if (m_input->m_presetSetting.m_highPassMip > 0)
|
||||
{
|
||||
m_image->CreateHighPass(m_input->m_presetSetting.m_highPassMip);
|
||||
}
|
||||
break;
|
||||
case StepConvertOutputColorSpace:
|
||||
// convert image from linear space to desired output color space
|
||||
ConvertToOuputColorSpace();
|
||||
break;
|
||||
case StepAlphaImage:
|
||||
// save alpha channel to separate image if it's needed
|
||||
CreateAlphaImage();
|
||||
break;
|
||||
case StepConvertPixelFormat:
|
||||
// convert pixel format
|
||||
ConvertPixelformat();
|
||||
@@ -411,12 +392,6 @@ namespace ImageProcessingAtom
|
||||
return;
|
||||
}
|
||||
|
||||
// don't do any reduce for color chart
|
||||
if (presetSettings->m_isColorChart)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// get suitable size for dest pixel format
|
||||
CPixelFormats::GetInstance().GetSuitableImageSize(presetSettings->m_pixelFormat, inputWidth, inputHeight,
|
||||
outWidth, outHeight);
|
||||
@@ -510,52 +485,6 @@ namespace ImageProcessingAtom
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImageConvertProcess::CreateAlphaImage()
|
||||
{
|
||||
// if alpha content doesn't have alpha or we need to discard alpha, skip
|
||||
// we won't create alpha image for cubemap too
|
||||
if (m_alphaContent == EAlphaContent::eAlphaContent_Absent
|
||||
|| m_alphaContent == EAlphaContent::eAlphaContent_OnlyWhite
|
||||
|| m_input->m_presetSetting.m_discardAlpha || IsConvertToCubemap())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// if dest format could save alpha, skip too
|
||||
if (!CPixelFormats::GetInstance().IsPixelFormatWithoutAlpha(m_input->m_presetSetting.m_pixelFormat))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// now create alpha image
|
||||
ImageToProcess alphaImage(m_image->Get());
|
||||
alphaImage.ConvertFormat(ePixelFormat_A8);
|
||||
|
||||
// validate pixelformatalpha
|
||||
if (CPixelFormats::GetInstance().IsFormatSingleChannel(m_input->m_presetSetting.m_pixelFormatAlpha))
|
||||
{
|
||||
alphaImage.ConvertFormat(m_input->m_presetSetting.m_pixelFormatAlpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
//For ASTC compression we need to clear out the alpha to get accurate rgb compression.
|
||||
if (IsASTCFormat(m_input->m_presetSetting.m_pixelFormat))
|
||||
{
|
||||
alphaImage.ConvertFormat(ePixelFormat_R8G8B8X8);
|
||||
alphaImage.ConvertFormat(m_input->m_presetSetting.m_pixelFormatAlpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "PixelFormatAlpha only supports single channel pixel formats or ASTC formats");
|
||||
}
|
||||
}
|
||||
|
||||
// get final result and save it to member variable for later use
|
||||
m_alphaImage = alphaImage.Get();
|
||||
|
||||
m_image->Get()->AddImageFlags(EIF_AttachedAlpha);
|
||||
}
|
||||
|
||||
// pixel format conversion
|
||||
bool ImageConvertProcess::ConvertPixelformat()
|
||||
{
|
||||
@@ -575,12 +504,6 @@ namespace ImageProcessingAtom
|
||||
m_image->GetCompressOption().rgbWeight = m_input->m_presetSetting.GetColorWeight();
|
||||
m_image->GetCompressOption().discardAlpha = m_input->m_presetSetting.m_discardAlpha;
|
||||
|
||||
//For ASTC compression we need to clear out the alpha to get accurate rgb compression.
|
||||
if(m_alphaImage && IsASTCFormat(m_input->m_presetSetting.m_pixelFormat))
|
||||
{
|
||||
m_image->GetCompressOption().discardAlpha = true;
|
||||
}
|
||||
|
||||
m_image->ConvertFormat(m_input->m_presetSetting.m_pixelFormat);
|
||||
|
||||
return true;
|
||||
@@ -762,7 +685,6 @@ namespace ImageProcessingAtom
|
||||
if (ImageProcess##PrivateName::DoesSupport(m_input->m_platform)) \
|
||||
{ \
|
||||
ImageProcess##PrivateName::PrepareImageForExport(m_image->Get()); \
|
||||
ImageProcess##PrivateName::PrepareImageForExport(m_alphaImage); \
|
||||
}
|
||||
AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS
|
||||
#undef AZ_RESTRICTED_PLATFORM_EXPANSION
|
||||
|
||||
@@ -115,7 +115,6 @@ namespace ImageProcessingAtom
|
||||
|
||||
//get output images
|
||||
IImageObjectPtr GetOutputImage();
|
||||
IImageObjectPtr GetOutputAlphaImage();
|
||||
IImageObjectPtr GetOutputIBLSpecularCubemap();
|
||||
IImageObjectPtr GetOutputIBLDiffuseCubemap();
|
||||
|
||||
@@ -131,8 +130,6 @@ namespace ImageProcessingAtom
|
||||
//for alpha
|
||||
//to indicate the current alpha channel content
|
||||
EAlphaContent m_alphaContent;
|
||||
//An image object to hold alpha channel in a separate image
|
||||
IImageObjectPtr m_alphaImage;
|
||||
|
||||
//output results of IBL cubemap generation, used in unit tests
|
||||
IImageObjectPtr m_iblSpecularCubemapImage;
|
||||
@@ -171,9 +168,6 @@ namespace ImageProcessingAtom
|
||||
//convert to output color space before compression
|
||||
bool ConvertToOuputColorSpace();
|
||||
|
||||
//create alpha image if it's needed
|
||||
void CreateAlphaImage();
|
||||
|
||||
//pixel format convertion/compression
|
||||
bool ConvertPixelformat();
|
||||
|
||||
|
||||
@@ -108,17 +108,14 @@ namespace ImageProcessingAtom
|
||||
}
|
||||
|
||||
IImageObjectPtr outputImage = m_process->GetOutputImage();
|
||||
IImageObjectPtr outputImageAlpha = m_process->GetOutputAlphaImage();
|
||||
|
||||
m_output->SetOutputImage(outputImage, ImageConvertOutput::Base);
|
||||
m_output->SetOutputImage(outputImageAlpha, ImageConvertOutput::Alpha);
|
||||
|
||||
if (!IsJobCancelled())
|
||||
{
|
||||
// For preview, combine image output with alpha if any
|
||||
m_output->SetProgress(1.0f / static_cast<float>(m_previewProcessStep));
|
||||
IImageObjectPtr combinedImage = MergeOutputImageForPreview(outputImage, outputImageAlpha);
|
||||
m_output->SetOutputImage(combinedImage, ImageConvertOutput::Preview);
|
||||
m_output->SetOutputImage(outputImage, ImageConvertOutput::Preview);
|
||||
}
|
||||
|
||||
m_output->SetReady(true);
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace ImageProcessingAtom
|
||||
const static AZ::u32 EIF_Greyscale = 0x8; // hint for the engine (e.g. greyscale light beams can be applied to shadow mask), can be for DXT1 because compression artfacts don't count as color
|
||||
const static AZ::u32 EIF_SupressEngineReduce = 0x10; // info for the engine: don't reduce texture resolution on this texture
|
||||
const static AZ::u32 EIF_UNUSED_BIT = 0x40; // Free to use
|
||||
const static AZ::u32 EIF_AttachedAlpha = 0x400; // info for the engine: it's a texture with attached alpha channel
|
||||
const static AZ::u32 EIF_AttachedAlpha = 0x400; // deprecated: info for the engine: it's a texture with attached alpha channel
|
||||
const static AZ::u32 EIF_SRGBRead = 0x800; // info for the engine: if gamma corrected rendering is on, this texture requires SRGBRead (it's not stored in linear)
|
||||
const static AZ::u32 EIF_DontResize = 0x8000; // info for the engine: for dds textures that shouldn't be resized
|
||||
const static AZ::u32 EIF_RenormalizedTexture = 0x10000; // info for the engine: for dds textures that have renormalized color range
|
||||
|
||||
@@ -316,130 +316,6 @@ namespace ImageProcessingAtom
|
||||
m_mips.clear();
|
||||
}
|
||||
|
||||
//note: there are some unreasonable parts of the save files formats for cry textures. We might need to rethink about
|
||||
// it for new renderer
|
||||
bool CImageObject::SaveImage(const char* filename, IImageObjectPtr alphaImage, AZStd::vector<AZStd::string>& outFilePaths) const
|
||||
{
|
||||
AZ::IO::SystemFile file;
|
||||
file.Open(filename, AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY);
|
||||
|
||||
AZ::IO::SystemFileStream fileSaveStream(&file, true);
|
||||
if (!fileSaveStream.IsOpen())
|
||||
{
|
||||
AZ_Warning("Image Processing", false, "%s: failed to create file %s", __FUNCTION__, filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (alphaImage)
|
||||
{
|
||||
AZ_Assert(HasImageFlags(EIF_AttachedAlpha), "attached alpha image flag wasn't set");
|
||||
AZ_Assert(!alphaImage->HasImageFlags(EIF_AttachedAlpha), "alpha image shouldn't have attached alpha image flag");
|
||||
|
||||
// inherit cubemap and decal image flags to attached alpha image
|
||||
alphaImage->AddImageFlags(GetImageFlags() & (EIF_Cubemap
|
||||
| EIF_Decal | EIF_Splitted));
|
||||
alphaImage->SetNumPersistentMips(m_numPersistentMips);
|
||||
}
|
||||
|
||||
bool bOk = SaveImage(fileSaveStream);
|
||||
bool hasSplitFlag = HasImageFlags(EIF_Splitted);
|
||||
|
||||
//append alpha image data in the end if there is no split
|
||||
if (bOk && alphaImage && !hasSplitFlag)
|
||||
{
|
||||
//4 bytes extension tag, 4 bytes attached alpha tag, then 4 bytes of chunk size
|
||||
fileSaveStream.Write(sizeof(FOURCC_CExt), &FOURCC_CExt); // marker for the start of O3DE Extended data
|
||||
fileSaveStream.Write(sizeof(FOURCC_AttC), &FOURCC_AttC); // Attached Channel chunk
|
||||
|
||||
uint32_t size = 0;
|
||||
uint32_t sizeBytes = sizeof(size);
|
||||
fileSaveStream.Write(sizeBytes, &size); //size of attached chunk
|
||||
|
||||
//save alpha image and get the size
|
||||
AZ::IO::SizeType startPos = fileSaveStream.GetCurPos();
|
||||
bOk = alphaImage->SaveImage(fileSaveStream);
|
||||
AZ::IO::SizeType endPos = fileSaveStream.GetCurPos();
|
||||
size = static_cast<uint32_t>(endPos - startPos);
|
||||
|
||||
//move back to beginning of chunk and write chunk size then move back to end
|
||||
fileSaveStream.Seek(startPos - sizeBytes, AZ::IO::GenericStream::ST_SEEK_BEGIN);
|
||||
fileSaveStream.Write(sizeBytes, &size);
|
||||
fileSaveStream.Seek(endPos, AZ::IO::GenericStream::ST_SEEK_BEGIN);
|
||||
|
||||
// marker for the end of O3DE Extended data
|
||||
fileSaveStream.Write(sizeof(FOURCC_CEnd), &FOURCC_CEnd);
|
||||
}
|
||||
|
||||
if (!bOk)
|
||||
{
|
||||
AZ::IO::SystemFile::Delete(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
// It's important to maintain the product output sequence. Asset Database/Browser will use the first product to determine the source type!
|
||||
outFilePaths.push_back(filename);
|
||||
|
||||
// save stand alone products
|
||||
if (hasSplitFlag)
|
||||
{
|
||||
// alpha
|
||||
if (alphaImage)
|
||||
{
|
||||
AZStd::string alphaFile = AZStd::string::format("%s.a", filename);
|
||||
|
||||
AZ::IO::SystemFile outAlphaFile;
|
||||
outAlphaFile.Open(alphaFile.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY);
|
||||
|
||||
AZ::IO::SystemFileStream alphaFileSaveStream(&outAlphaFile, true);
|
||||
|
||||
if (alphaFileSaveStream.IsOpen())
|
||||
{
|
||||
alphaImage->SaveImage(alphaFileSaveStream);
|
||||
outFilePaths.push_back(alphaFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("Image Processing", false, "%s: failed to create file %s", __FUNCTION__, alphaFile.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// mips
|
||||
AZ::u32 numStreamable = GetMipCount() - m_numPersistentMips;
|
||||
for (AZ::u32 mip = 0; mip < numStreamable; mip++)
|
||||
{
|
||||
AZ::u32 nameIdx = numStreamable - mip;
|
||||
AZStd::string mipFileName = AZStd::string::format("%s.%d", filename, nameIdx);
|
||||
SaveMipToFile(mip, mipFileName);
|
||||
outFilePaths.push_back(mipFileName);
|
||||
if (alphaImage)
|
||||
{
|
||||
AZStd::string mipAlphaFileName = mipFileName + "a";
|
||||
alphaImage->SaveMipToFile(mip, mipAlphaFileName);
|
||||
outFilePaths.push_back(mipAlphaFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bOk;
|
||||
}
|
||||
|
||||
bool CImageObject::SaveMipToFile(AZ::u32 mip, const AZStd::string& filename) const
|
||||
{
|
||||
AZ::IO::SystemFile saveFile;
|
||||
saveFile.Open(filename.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY);
|
||||
|
||||
AZ::IO::SystemFileStream saveFileStream(&saveFile, true);
|
||||
|
||||
if (!saveFileStream.IsOpen())
|
||||
{
|
||||
AZ_Warning("Image Processing", false, "%s: failed to create file %s", __FUNCTION__, filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
saveFileStream.Write(GetMipBufSize(mip), m_mips[mip]->m_pData);
|
||||
return true;
|
||||
}
|
||||
|
||||
float CImageObject::CalculateAverageBrightness() const
|
||||
{
|
||||
//if it's compressed format, return a default value
|
||||
@@ -642,63 +518,6 @@ namespace ImageProcessingAtom
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CImageObject::SaveImage(AZ::IO::SystemFileStream& saveFileStream) const
|
||||
{
|
||||
DDS_FILE_DESC_LEGACY desc;
|
||||
DDS_HEADER_DXT10 exthead;
|
||||
|
||||
desc.dwMagic = FOURCC_DDS;
|
||||
|
||||
if (!BuildSurfaceHeader(desc.header))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (desc.header.IsDX10Ext() && !BuildSurfaceExtendedHeader(exthead))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
saveFileStream.Write(sizeof(desc), &desc);
|
||||
|
||||
if (desc.header.IsDX10Ext())
|
||||
{
|
||||
saveFileStream.Write(sizeof(exthead), &exthead);
|
||||
}
|
||||
|
||||
AZ::u32 faces = 1;
|
||||
|
||||
//for cubemap. export each face and its mipmap
|
||||
if (HasImageFlags(EIF_Cubemap))
|
||||
{
|
||||
faces = 6;
|
||||
}
|
||||
|
||||
AZ::u32 mipStart = 0;
|
||||
if (HasImageFlags(EIF_Splitted))
|
||||
{
|
||||
if (m_numPersistentMips < m_mips.size())
|
||||
{
|
||||
mipStart = (AZ::u32)m_mips.size() - m_numPersistentMips;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "numPersistentMips wasn't setup correctly");
|
||||
}
|
||||
}
|
||||
|
||||
for (AZ::u32 face = 0; face < faces; face++)
|
||||
{
|
||||
for (AZ::u32 mip = mipStart; mip < m_mips.size(); ++mip)
|
||||
{
|
||||
const MipLevel& level = *m_mips[mip];
|
||||
AZ::u32 faceBufSize = level.m_pitch * level.m_rowCount / faces;
|
||||
saveFileStream.Write(faceBufSize, level.m_pData + faceBufSize * face);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CImageObject::GetExtent(AZ::u32& width, AZ::u32& height, AZ::u32& mipCount) const
|
||||
{
|
||||
mipCount = (AZ::u32)m_mips.size();
|
||||
@@ -953,35 +772,4 @@ namespace ImageProcessingAtom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CImageObject::ConvertLegacyGloss()
|
||||
{
|
||||
if (!(CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat)))
|
||||
{
|
||||
AZ_Assert(false, "%s function only works with uncompressed pixel format", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
//create pixel operation function
|
||||
IPixelOperationPtr pixelOp = CreatePixelOperation(m_pixelFormat);
|
||||
|
||||
//get count of bytes per pixel
|
||||
AZ::u32 pixelBytes = CPixelFormats::GetInstance().GetPixelFormatInfo(m_pixelFormat)->bitsPerBlock / 8;
|
||||
|
||||
const AZ::u32 mips = (AZ::u32)m_mips.size();
|
||||
float color[4];
|
||||
for (AZ::u32 mip = 0; mip < mips; ++mip)
|
||||
{
|
||||
AZ::u8* pixelBuf = m_mips[mip]->m_pData;
|
||||
const AZ::u32 pixelCount = GetPixelCount(mip);
|
||||
|
||||
for (AZ::u32 i = 0; i < pixelCount; ++i, pixelBuf += pixelBytes)
|
||||
{
|
||||
pixelOp->GetRGBA(pixelBuf, color[0], color[1], color[2], color[3]);
|
||||
// Convert from (1 - s * 0.7)^6 to (1 - s)^2
|
||||
color[3] = 1 - pow(1.0f - color[3] * 0.7f, 3.0f);
|
||||
pixelOp->SetRGBA(pixelBuf, color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace ImageProcessingAtom
|
||||
|
||||
@@ -57,10 +57,6 @@ namespace ImageProcessingAtom
|
||||
|
||||
bool CompareImage(const IImageObjectPtr otherImage) const override;
|
||||
|
||||
bool SaveImage(const char* filename, IImageObjectPtr alphaImage, AZStd::vector<AZStd::string>& outFilePaths) const override;
|
||||
bool SaveImage(AZ::IO::SystemFileStream& out) const override;
|
||||
bool SaveMipToFile(AZ::u32 mip, const AZStd::string& filename) const override;
|
||||
|
||||
uint32_t GetTextureMemory() const override;
|
||||
|
||||
EAlphaContent GetAlphaContent() const override;
|
||||
@@ -79,7 +75,6 @@ namespace ImageProcessingAtom
|
||||
void SetNumPersistentMips(AZ::u32 nMips) override;
|
||||
|
||||
void GlossFromNormals(bool hasAuthoredGloss) override;
|
||||
void ConvertLegacyGloss() override;
|
||||
void ClearColor(float r, float g, float b, float a) override;
|
||||
//end virtual functions from IImageObject
|
||||
|
||||
|
||||
@@ -66,13 +66,6 @@ namespace ImageProcessingAtom
|
||||
bool GammaToLinearRGBA32F(bool bDeGamma);
|
||||
void LinearToGamma();
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// Tools for A32B32G32R32F
|
||||
|
||||
void CreateHighPass(uint32 dwMipDown);
|
||||
|
||||
void CreateColorChart();
|
||||
|
||||
//convert various original cubemap layouts to new layout
|
||||
bool ConvertCubemapLayout(CubemapLayoutType newLayout);
|
||||
};
|
||||
|
||||
@@ -988,7 +988,6 @@ namespace UnitTest
|
||||
ASSERT_TRUE(process->IsSucceed());
|
||||
|
||||
SaveImageToFile(process->GetOutputImage(), "rgb", 10);
|
||||
SaveImageToFile(process->GetOutputAlphaImage(), "alpha", 10);
|
||||
|
||||
process->GetAppendOutputProducts(outProducts);
|
||||
|
||||
|
||||
@@ -103,8 +103,6 @@ set(FILES
|
||||
Source/Converters/ConvertPixelFormat.cpp
|
||||
Source/Converters/Cubemap.h
|
||||
Source/Converters/Cubemap.cpp
|
||||
Source/Converters/ColorChart.cpp
|
||||
Source/Converters/HighPass.cpp
|
||||
Source/Converters/Histogram.cpp
|
||||
Source/Converters/Histogram.h
|
||||
../External/CubeMapGen/CBBoxInt32.cpp
|
||||
|
||||
+1
-1
@@ -153,7 +153,7 @@ struct StandardMaterialInputs
|
||||
|
||||
float2 m_vertexUv[UvSetCount];
|
||||
float3x3 m_uvMatrix;
|
||||
float m_normal;
|
||||
float3 m_normal;
|
||||
float3 m_tangents[UvSetCount];
|
||||
float3 m_bitangents[UvSetCount];
|
||||
|
||||
|
||||
@@ -50,8 +50,13 @@ float3 GetBackLighting(Surface surface, LightingData lightingData, float3 lightI
|
||||
// Thin object mode, using thin-film assumption proposed by Jimenez J. et al, 2010, "Real-Time Realistic Skin Translucency"
|
||||
// http://www.iryoku.com/translucency/downloads/Real-Time-Realistic-Skin-Translucency.pdf
|
||||
|
||||
result = shadowRatio ? float3(0.0, 0.0, 0.0) : TransmissionKernel(surface.transmission.thickness * transmissionParams.w, rcp(transmissionParams.xyz)) *
|
||||
saturate(dot(-surface.normal, dirToLight)) * lightIntensity * shadowRatio;
|
||||
float litRatio = 1.0 - shadowRatio;
|
||||
if (litRatio)
|
||||
{
|
||||
result = TransmissionKernel(surface.transmission.thickness * transmissionParams.w, rcp(transmissionParams.xyz)) *
|
||||
saturate(dot(-surface.normal, dirToLight)) * lightIntensity * litRatio;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"description": "",
|
||||
"parentMaterial": "",
|
||||
"materialType": "Materials/Types/EnhancedPBR.materialtype",
|
||||
"materialTypeVersion": 4,
|
||||
"properties": {
|
||||
"baseColor": {
|
||||
"color": [
|
||||
0.027664607390761375,
|
||||
0.1926604062318802,
|
||||
0.013916227966547012,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
"general": {
|
||||
"doubleSided": true
|
||||
},
|
||||
"subsurfaceScattering": {
|
||||
"thickness": 0.20000000298023224,
|
||||
"transmissionMode": "ThinObject",
|
||||
"transmissionTint": [
|
||||
0.009140154346823692,
|
||||
0.19806210696697235,
|
||||
0.01095597818493843,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -43,7 +43,7 @@ namespace AtomToolsFramework
|
||||
m_propertyEditor->Setup(context, instanceNotificationHandler, false);
|
||||
m_propertyEditor->AddInstance(instance, instanceClassId, nullptr, instanceToCompare);
|
||||
m_propertyEditor->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
m_propertyEditor->InvalidateAll();
|
||||
m_propertyEditor->QueueInvalidation(AzToolsFramework::PropertyModificationRefreshLevel::Refresh_EntireTree);
|
||||
|
||||
m_layout->addWidget(m_propertyEditor);
|
||||
setLayout(m_layout);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
@echo off
|
||||
:: Keep changes local
|
||||
SETLOCAL enableDelayedExpansion
|
||||
|
||||
REM
|
||||
REM Copyright (c) Contributors to the Open 3D Engine Project
|
||||
@@ -13,7 +15,7 @@ REM
|
||||
:: Puts you in the CMD within the dev environment
|
||||
|
||||
:: Set up window
|
||||
TITLE O3DE Asset Gem Cmd
|
||||
TITLE O3DE DCC Scripting Interface Cmd
|
||||
:: Use obvious color to prevent confusion (Grey with Yellow Text)
|
||||
COLOR 8E
|
||||
|
||||
@@ -21,15 +23,12 @@ COLOR 8E
|
||||
cd %~dp0
|
||||
PUSHD %~dp0
|
||||
|
||||
:: Keep changes local
|
||||
SETLOCAL enableDelayedExpansion
|
||||
|
||||
CALL %~dp0\Project_Env.bat
|
||||
|
||||
echo.
|
||||
echo _____________________________________________________________________
|
||||
echo.
|
||||
echo ~ O3DE Asset Gem CMD ...
|
||||
echo ~ O3DE %O3DE_PROJECT% Asset Gem CMD ...
|
||||
echo _____________________________________________________________________
|
||||
echo.
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
:: Launches maya wityh a bunch of local hooks for Lumberyard
|
||||
:: ToDo: move all of this to a .json data driven boostrapping system
|
||||
|
||||
@echo off
|
||||
|
||||
REM
|
||||
@@ -37,7 +34,7 @@ echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION%
|
||||
IF EXIST "%~dp0Project_Env.bat" CALL %~dp0Project_Env.bat
|
||||
|
||||
echo ________________________________
|
||||
echo Launching Maya %DCCSI_MAYA_VERSION% for Lumberyard...
|
||||
echo Launching Maya %DCCSI_MAYA_VERSION% for O3DE: %O3DE_PROJECT%...
|
||||
|
||||
:::: Set Maya native project acess to this project
|
||||
::set MAYA_PROJECT=%LY_PROJECT%
|
||||
@@ -47,8 +44,10 @@ echo Launching Maya %DCCSI_MAYA_VERSION% for Lumberyard...
|
||||
Set MAYA_VP2_DEVICE_OVERRIDE = VirtualDeviceDx11
|
||||
|
||||
:: Default to the right version of Maya if we can detect it... and launch
|
||||
IF EXIST "%MAYA_LOCATION%\bin\Maya.exe" (
|
||||
start "" "%MAYA_LOCATION%\bin\Maya.exe" %*
|
||||
echo MAYA_BIN_PATH = %MAYA_BIN_PATH%
|
||||
|
||||
IF EXIST "%MAYA_BIN_PATH%\Maya.exe" (
|
||||
start "" "%MAYA_BIN_PATH%\Maya.exe" %*
|
||||
) ELSE (
|
||||
Where maya.exe 2> NUL
|
||||
IF ERRORLEVEL 1 (
|
||||
|
||||
@@ -29,23 +29,23 @@ PUSHD %~dp0
|
||||
set ABS_PATH=%~dp0
|
||||
|
||||
:: project name as a str tag
|
||||
IF "%LY_PROJECT_NAME%"=="" (
|
||||
for %%I in ("%~dp0.") do for %%J in ("%%~dpI.") do set LY_PROJECT_NAME=%%~nxJ
|
||||
IF "%O3DE_PROJECT%"=="" (
|
||||
for %%I in ("%~dp0.") do for %%J in ("%%~dpI.") do set O3DE_PROJECT=%%~nxJ
|
||||
)
|
||||
|
||||
echo.
|
||||
echo _____________________________________________________________________
|
||||
echo.
|
||||
echo ~ Setting up O3DE %LY_PROJECT_NAME% Environment ...
|
||||
echo ~ Setting up O3DE %O3DE_PROJECT% Environment ...
|
||||
echo _____________________________________________________________________
|
||||
echo.
|
||||
echo LY_PROJECT_NAME = %LY_PROJECT_NAME%
|
||||
echo O3DE_PROJECT = %O3DE_PROJECT%
|
||||
|
||||
:: if the user has set up a custom env call it
|
||||
:: this should allow the user to locally
|
||||
:: set env hooks like LY_DEV or LY_PROJECT
|
||||
:: set env hooks like O3DE_DEV or O3DE_PROJECT_PATH
|
||||
IF EXIST "%~dp0User_Env.bat" CALL %~dp0User_Env.bat
|
||||
echo LY_DEV = %LY_DEV%
|
||||
echo O3DE_DEV = %O3DE_DEV%
|
||||
|
||||
:: Constant Vars (Global)
|
||||
:: global debug flag (propogates)
|
||||
@@ -74,23 +74,20 @@ echo DCCSI_LOGLEVEL = %DCCSI_LOGLEVEL%
|
||||
IF "%DCCSI_MAYA_VERSION%"=="" (set DCCSI_MAYA_VERSION=2020)
|
||||
echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION%
|
||||
|
||||
:: LY_PROJECT is ideally treated as a full path in the env launchers
|
||||
:: O3DE_PROJECT_PATH is ideally treated as a full path in the env launchers
|
||||
:: do to changes in o3de, external engine/project/gem folder structures, etc.
|
||||
IF "%LY_PROJECT%"=="" (
|
||||
for %%i in ("%~dp0..") do set "LY_PROJECT=%%~fi"
|
||||
IF "%O3DE_PROJECT_PATH%"=="" (
|
||||
for %%i in ("%~dp0..") do set "O3DE_PROJECT_PATH=%%~fi"
|
||||
)
|
||||
echo LY_PROJECT = %LY_PROJECT%
|
||||
echo O3DE_PROJECT_PATH = %O3DE_PROJECT_PATH%
|
||||
|
||||
:: this is here for archaic reasons, WILL DEPRECATE
|
||||
IF "%LY_PROJECT_PATH%"=="" (set LY_PROJECT_PATH=%LY_PROJECT%)
|
||||
echo LY_PROJECT_PATH = %LY_PROJECT_PATH%
|
||||
:: Change to root O3DE dev dir
|
||||
IF "%O3DE_DEV%"=="" echo ~ You must set O3DE_DEV in a User_Env.bat to match your local engine repo!
|
||||
IF "%O3DE_DEV%"=="" echo ~ Using default O3DE_DEV=C:\Depot\o3de-engine
|
||||
IF "%O3DE_DEV%"=="" (set O3DE_DEV=C:\Depot\o3de-engine)
|
||||
echo O3DE_DEV = %O3DE_DEV%
|
||||
|
||||
:: Change to root Lumberyard dev dir
|
||||
:: You must set this in a User_Env.bat to match youe engine repo location!
|
||||
IF "%LY_DEV%"=="" (set LY_DEV=C:\Depot\o3de-engine)
|
||||
echo LY_DEV = %LY_DEV%
|
||||
|
||||
CALL %LY_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Launchers\Windows\Env_Maya.bat
|
||||
CALL %O3DE_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Tools\Dev\Windows\Env_Maya.bat
|
||||
|
||||
:: Restore original directory
|
||||
popd
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
@echo off
|
||||
:: Keep changes local
|
||||
SETLOCAL enableDelayedExpansion
|
||||
|
||||
REM
|
||||
REM Copyright (c) Contributors to the Open 3D Engine Project
|
||||
@@ -21,15 +23,12 @@ COLOR 8E
|
||||
cd %~dp0
|
||||
PUSHD %~dp0
|
||||
|
||||
:: Keep changes local
|
||||
SETLOCAL enableDelayedExpansion
|
||||
|
||||
CALL %~dp0\Project_Env.bat
|
||||
|
||||
echo.
|
||||
echo _____________________________________________________________________
|
||||
echo.
|
||||
echo ~ LY DCC Scripting Interface CMD ...
|
||||
echo ~ O3DE %O3DE_PROJECT% Asset Gem CMD ...
|
||||
echo _____________________________________________________________________
|
||||
echo.
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION%
|
||||
IF EXIST "%~dp0Project_Env.bat" CALL %~dp0Project_Env.bat
|
||||
|
||||
echo ________________________________
|
||||
echo Launching Maya %DCCSI_MAYA_VERSION% for Lumberyard...
|
||||
echo Launching Maya %DCCSI_MAYA_VERSION% for O3DE: %O3DE_PROJECT%...
|
||||
|
||||
:::: Set Maya native project acess to this project
|
||||
::set MAYA_PROJECT=%LY_PROJECT%
|
||||
@@ -44,8 +44,10 @@ echo Launching Maya %DCCSI_MAYA_VERSION% for Lumberyard...
|
||||
Set MAYA_VP2_DEVICE_OVERRIDE = VirtualDeviceDx11
|
||||
|
||||
:: Default to the right version of Maya if we can detect it... and launch
|
||||
IF EXIST "%MAYA_LOCATION%\bin\Maya.exe" (
|
||||
start "" "%MAYA_LOCATION%\bin\Maya.exe" %*
|
||||
echo MAYA_BIN_PATH = %MAYA_BIN_PATH%
|
||||
|
||||
IF EXIST "%MAYA_BIN_PATH%\Maya.exe" (
|
||||
start "" "%MAYA_BIN_PATH%\Maya.exe" %*
|
||||
) ELSE (
|
||||
Where maya.exe 2> NUL
|
||||
IF ERRORLEVEL 1 (
|
||||
|
||||
@@ -29,23 +29,23 @@ PUSHD %~dp0
|
||||
set ABS_PATH=%~dp0
|
||||
|
||||
:: project name as a str tag
|
||||
IF "%LY_PROJECT_NAME%"=="" (
|
||||
for %%I in ("%~dp0.") do for %%J in ("%%~dpI.") do set LY_PROJECT_NAME=%%~nxJ
|
||||
IF "%O3DE_PROJECT%"=="" (
|
||||
for %%I in ("%~dp0.") do for %%J in ("%%~dpI.") do set O3DE_PROJECT=%%~nxJ
|
||||
)
|
||||
|
||||
echo.
|
||||
echo _____________________________________________________________________
|
||||
echo.
|
||||
echo ~ Setting up O3DE %LY_PROJECT_NAME% Environment ...
|
||||
echo ~ Setting up O3DE %O3DE_PROJECT% Environment ...
|
||||
echo _____________________________________________________________________
|
||||
echo.
|
||||
echo LY_PROJECT_NAME = %LY_PROJECT_NAME%
|
||||
echo O3DE_PROJECT = %O3DE_PROJECT%
|
||||
|
||||
:: if the user has set up a custom env call it
|
||||
:: this should allow the user to locally
|
||||
:: set env hooks like LY_DEV or LY_PROJECT
|
||||
:: set env hooks like O3DE_DEV or O3DE_PROJECT_PATH
|
||||
IF EXIST "%~dp0User_Env.bat" CALL %~dp0User_Env.bat
|
||||
echo LY_DEV = %LY_DEV%
|
||||
echo O3DE_DEV = %O3DE_DEV%
|
||||
|
||||
:: Constant Vars (Global)
|
||||
:: global debug flag (propogates)
|
||||
@@ -74,23 +74,20 @@ echo DCCSI_LOGLEVEL = %DCCSI_LOGLEVEL%
|
||||
IF "%DCCSI_MAYA_VERSION%"=="" (set DCCSI_MAYA_VERSION=2020)
|
||||
echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION%
|
||||
|
||||
:: LY_PROJECT is ideally treated as a full path in the env launchers
|
||||
:: O3DE_PROJECT_PATH is ideally treated as a full path in the env launchers
|
||||
:: do to changes in o3de, external engine/project/gem folder structures, etc.
|
||||
IF "%LY_PROJECT%"=="" (
|
||||
for %%i in ("%~dp0..") do set "LY_PROJECT=%%~fi"
|
||||
IF "%O3DE_PROJECT_PATH%"=="" (
|
||||
for %%i in ("%~dp0..") do set "O3DE_PROJECT_PATH=%%~fi"
|
||||
)
|
||||
echo LY_PROJECT = %LY_PROJECT%
|
||||
echo O3DE_PROJECT_PATH = %O3DE_PROJECT_PATH%
|
||||
|
||||
:: this is here for archaic reasons, WILL DEPRECATE
|
||||
IF "%LY_PROJECT_PATH%"=="" (set LY_PROJECT_PATH=%LY_PROJECT%)
|
||||
echo LY_PROJECT_PATH = %LY_PROJECT_PATH%
|
||||
:: Change to root O3DE dev dir
|
||||
IF "%O3DE_DEV%"=="" echo ~ You must set O3DE_DEV in a User_Env.bat to match your local engine repo!
|
||||
IF "%O3DE_DEV%"=="" echo ~ Using default O3DE_DEV=C:\Depot\o3de-engine
|
||||
IF "%O3DE_DEV%"=="" (set O3DE_DEV=C:\Depot\o3de-engine)
|
||||
echo O3DE_DEV = %O3DE_DEV%
|
||||
|
||||
:: Change to root Lumberyard dev dir
|
||||
:: You must set this in a User_Env.bat to match youe engine repo location!
|
||||
IF "%LY_DEV%"=="" (set LY_DEV=C:\Depot\o3de-engine)
|
||||
echo LY_DEV = %LY_DEV%
|
||||
|
||||
CALL %LY_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Launchers\Windows\Env_Maya.bat
|
||||
CALL %O3DE_DEV%\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\Tools\Dev\Windows\Env_Maya.bat
|
||||
|
||||
:: Restore original directory
|
||||
popd
|
||||
|
||||
@@ -799,7 +799,8 @@ namespace AZ::AtomBridge
|
||||
const float startAngle = DegToRad(startAngleDegrees);
|
||||
const float stopAngle = DegToRad(sweepAngleDegrees) + startAngle;
|
||||
SingleColorDynamicSizeLineHelper lines(1+static_cast<int>(sweepAngleDegrees/angularStepDegrees));
|
||||
AZ::Vector3 radiusV3 = AZ::Vector3(radius);
|
||||
float aspectRadius = radius / GetAspectRatio();
|
||||
AZ::Vector3 radiusV3 = AZ::Vector3(aspectRadius, radius, radius);
|
||||
AZ::Vector3 pos = AZ::Vector3(center.GetX(), center.GetY(), z);
|
||||
CreateAxisAlignedArc(
|
||||
lines,
|
||||
|
||||
@@ -111,6 +111,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
RUNTIME_DEPENDENCIES
|
||||
Gem::Atom_RPI.Editor
|
||||
Gem::Atom_Feature_Common.Editor
|
||||
Gem::AtomToolsFramework.Editor
|
||||
Legacy::EditorCommon
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ echo DCCSI_PY_VERSION_RELEASE = %DCCSI_PY_VERSION_RELEASE%
|
||||
echo DCCSI_MAYA_VERSION = %DCCSI_MAYA_VERSION%
|
||||
|
||||
:::: Set Maya native project acess to this project
|
||||
IF "%MAYA_PROJECT%"=="" (set MAYA_PROJECT=%O3DE_PROJECT%)
|
||||
IF "%MAYA_PROJECT%"=="" (set MAYA_PROJECT=%O3DE_PROJECT_PATH%)
|
||||
echo MAYA_PROJECT = %MAYA_PROJECT%
|
||||
|
||||
:: maya sdk path
|
||||
|
||||
@@ -102,7 +102,9 @@ namespace UnitTest
|
||||
prefabBuilderComponent.Activate();
|
||||
|
||||
AZStd::vector<AssetBuilderSDK::JobProduct> jobProducts;
|
||||
auto&& prefabDom = prefabSystemComponentInterface->FindTemplateDom(parentInstance->GetTemplateId());
|
||||
// Make a copy of the template DOM, as the prefab system still owns the existing template
|
||||
AzToolsFramework::Prefab::PrefabDom prefabDom;
|
||||
prefabDom.CopyFrom(prefabSystemComponentInterface->FindTemplateDom(parentInstance->GetTemplateId()), prefabDom.GetAllocator(), false);
|
||||
|
||||
ASSERT_TRUE(prefabBuilderComponent.ProcessPrefab({AZ::Crc32("pc")}, "parent.prefab", "unused", AZ::Uuid(), prefabDom, jobProducts));
|
||||
|
||||
|
||||
@@ -282,7 +282,7 @@ namespace Profiler
|
||||
m_stackLevel = 0;
|
||||
m_cachedTimeRegionMap.clear();
|
||||
m_timeRegionStack.clear();
|
||||
m_cachedTimeRegions.clear();
|
||||
ResetCachedData();
|
||||
}
|
||||
|
||||
timeRegion.m_stackDepth = aznumeric_cast<uint16_t>(m_stackLevel);
|
||||
@@ -329,8 +329,21 @@ namespace Profiler
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Add an entry to the cached region
|
||||
m_cachedTimeRegions.push_back(timeRegionCached);
|
||||
// Add an entry to the cached region. Discard excess data in case there is too much to handle.
|
||||
if (m_cachedTimeRegions.size() < TimeRegionStackSize)
|
||||
{
|
||||
m_cachedTimeRegions.push_back(timeRegionCached);
|
||||
}
|
||||
// Warn only once per thread if the cached data limit has been reached.
|
||||
else if (!m_cachedDataLimitReached)
|
||||
{
|
||||
AZ_Warning(
|
||||
"Profiler", false,
|
||||
"Limit for profiling data has been reached by thread %i. Excess data will be discarded. Considering moving or reducing "
|
||||
"profiler markers to prevent data loss.",
|
||||
m_executingThreadId);
|
||||
m_cachedDataLimitReached = true;
|
||||
}
|
||||
|
||||
// If the stack is empty, add it to the local cache map. Only gets called when the stack is empty
|
||||
// NOTE: this is where the largest overhead will be, but due to it only being called when the stack is empty
|
||||
@@ -354,7 +367,7 @@ namespace Profiler
|
||||
}
|
||||
|
||||
// Clear the cached regions
|
||||
m_cachedTimeRegions.clear();
|
||||
ResetCachedData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,10 +384,17 @@ namespace Profiler
|
||||
m_cachedTimeRegionMap.clear();
|
||||
m_hitSizeLimitMap.clear();
|
||||
}
|
||||
|
||||
m_cachedTimeRegionMutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void CpuTimingLocalStorage::ResetCachedData()
|
||||
{
|
||||
m_cachedTimeRegions.clear();
|
||||
m_cachedDataLimitReached = false;
|
||||
}
|
||||
|
||||
// --- CpuProfilingStatisticsSerializer ---
|
||||
|
||||
CpuProfilingStatisticsSerializer::CpuProfilingStatisticsSerializer(const AZStd::ring_buffer<CpuProfiler::TimeRegionMap>& continuousData)
|
||||
|
||||
@@ -50,6 +50,9 @@ namespace Profiler
|
||||
// Tries to flush the map to the passed parameter, only if the thread's mutex is unlocked
|
||||
void TryFlushCachedMap(CpuProfiler::ThreadTimeRegionMap& cachedRegionMap);
|
||||
|
||||
// Clears m_cachedTimeRegions and resets m_cachedDataLimitReached flag.
|
||||
void ResetCachedData();
|
||||
|
||||
AZStd::thread_id m_executingThreadId;
|
||||
// Keeps track of the current thread's stack depth
|
||||
uint32_t m_stackLevel = 0u;
|
||||
@@ -75,6 +78,9 @@ namespace Profiler
|
||||
|
||||
// Keep track of the regions that have hit the size limit so we don't have to lock to check
|
||||
AZStd::map<AZStd::string, bool> m_hitSizeLimitMap;
|
||||
|
||||
// Keeps track of the first time cached data limit was reached.
|
||||
bool m_cachedDataLimitReached = false;
|
||||
};
|
||||
|
||||
//! CpuProfiler will keep track of the registered threads, and
|
||||
|
||||
@@ -232,9 +232,10 @@ float4x4 GetObject_WorldMatrix()
|
||||
|
||||
float GetHeight(float2 origUv)
|
||||
{
|
||||
float2 uv = clamp(origUv + (ObjectSrg::m_terrainData.m_uvStep * 0.5f), 0.0f, 1.0f);
|
||||
float height = 0.0f;
|
||||
float2 halfStep = ObjectSrg::m_terrainData.m_uvStep * 0.5;
|
||||
float2 uv = origUv * (1.0 - ObjectSrg::m_terrainData.m_uvStep) + halfStep;
|
||||
|
||||
float height = 0.0f;
|
||||
if (o_useTerrainSmoothing)
|
||||
{
|
||||
float2 textureSize;
|
||||
|
||||
@@ -151,24 +151,32 @@ namespace Terrain
|
||||
{
|
||||
float maxSample = 0.0f;
|
||||
terrainExists = false;
|
||||
|
||||
GradientSignal::GradientSampleParams params(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f));
|
||||
|
||||
// Right now, when the list contains multiple entries, we will use the highest point from each gradient.
|
||||
// This is needed in part because gradients don't really have world bounds, so they exist everywhere but generally have a value
|
||||
// of 0 outside their data bounds if they're using bounded data. We should examine the possibility of extending the gradient API
|
||||
// to provide actual bounds so that it's possible to detect if the gradient even 'exists' in an area, at which point we could just
|
||||
// make this list a prioritized list from top to bottom for any points that overlap.
|
||||
for (auto& gradientId : m_configuration.m_gradientEntities)
|
||||
AZ_WarningOnce("Terrain", !m_isRequestInProgress, "Detected cyclic dependences with terrain height entity references");
|
||||
if (!m_isRequestInProgress)
|
||||
{
|
||||
// If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain
|
||||
// to *not* exist at a specific point.
|
||||
terrainExists = true;
|
||||
m_isRequestInProgress = true;
|
||||
GradientSignal::GradientSampleParams params(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f));
|
||||
|
||||
float sample = 0.0f;
|
||||
GradientSignal::GradientRequestBus::EventResult(
|
||||
sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params);
|
||||
maxSample = AZ::GetMax(maxSample, sample);
|
||||
// Right now, when the list contains multiple entries, we will use the highest point from each gradient.
|
||||
// This is needed in part because gradients don't really have world bounds, so they exist everywhere but generally have a value
|
||||
// of 0 outside their data bounds if they're using bounded data. We should examine the possibility of extending the gradient
|
||||
// API to provide actual bounds so that it's possible to detect if the gradient even 'exists' in an area, at which point we
|
||||
// could just make this list a prioritized list from top to bottom for any points that overlap.
|
||||
for (auto& gradientId : m_configuration.m_gradientEntities)
|
||||
{
|
||||
if (gradientId.IsValid())
|
||||
{
|
||||
// If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain
|
||||
// to *not* exist at a specific point.
|
||||
terrainExists = true;
|
||||
|
||||
float sample = 0.0f;
|
||||
GradientSignal::GradientRequestBus::EventResult(
|
||||
sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params);
|
||||
maxSample = AZ::GetMax(maxSample, sample);
|
||||
}
|
||||
}
|
||||
m_isRequestInProgress = false;
|
||||
}
|
||||
|
||||
const float height = AZ::Lerp(m_cachedShapeBounds.GetMin().GetZ(), m_cachedShapeBounds.GetMax().GetZ(), maxSample);
|
||||
|
||||
@@ -91,6 +91,9 @@ namespace Terrain
|
||||
AZ::Vector2 m_cachedHeightQueryResolution{ 1.0f, 1.0f };
|
||||
AZ::Aabb m_cachedShapeBounds;
|
||||
|
||||
// prevent recursion in case user attaches cyclic dependences
|
||||
mutable bool m_isRequestInProgress{ false };
|
||||
|
||||
LmbrCentral::DependencyMonitor m_dependencyMonitor;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -218,6 +218,12 @@ namespace Terrain
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
|
||||
queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution);
|
||||
|
||||
// Take the dirty region and adjust the Z values to the world min/max so that even if the dirty region falls outside the current
|
||||
// world bounds, we still update the wireframe accordingly.
|
||||
AZ::Aabb dirtyRegion2D = AZ::Aabb::CreateFromMinMaxValues(
|
||||
dirtyRegion.GetMin().GetX(), dirtyRegion.GetMin().GetY(), worldBounds.GetMin().GetZ(),
|
||||
dirtyRegion.GetMax().GetX(), dirtyRegion.GetMax().GetY(), worldBounds.GetMax().GetZ());
|
||||
|
||||
// Calculate the world size of each sector. Note that this size actually ends at the last point, not the last square.
|
||||
// So for example, the sector size for 3 points will go from (*--*--*) even though it will be used to draw (*--*--*--).
|
||||
const float xSectorSize = (queryResolution.GetX() * SectorSizeInGridPoints);
|
||||
@@ -230,7 +236,7 @@ namespace Terrain
|
||||
|
||||
// If we haven't cached anything before, or if the world bounds has changed, clear our cache structure and repopulate it
|
||||
// with WireframeSector entries with the proper AABB sizes.
|
||||
if (!m_wireframeBounds.IsValid() || !dirtyRegion.IsValid() || !m_wireframeBounds.IsClose(worldBounds))
|
||||
if (!m_wireframeBounds.IsValid() || !dirtyRegion2D.IsValid() || !m_wireframeBounds.IsClose(worldBounds))
|
||||
{
|
||||
m_wireframeBounds = worldBounds;
|
||||
|
||||
@@ -266,7 +272,7 @@ namespace Terrain
|
||||
// For each sector, if it overlaps with the dirty region, clear it out and recache the wireframe line data.
|
||||
for (auto& sector : m_wireframeSectors)
|
||||
{
|
||||
if (dirtyRegion.IsValid() && !dirtyRegion.Overlaps(sector.m_aabb))
|
||||
if (dirtyRegion2D.IsValid() && !dirtyRegion2D.Overlaps(sector.m_aabb))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace Terrain
|
||||
->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_4096Meters, "4 Kilometers")
|
||||
->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_8192Meters, "8 Kilometers")
|
||||
->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_16384Meters, "16 Kilometers")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, false) // Keeping invisible until it's hooked up under the hood
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -972,10 +972,10 @@ namespace Terrain
|
||||
m_areaData.m_rebuildSectors = false;
|
||||
|
||||
m_sectorData.clear();
|
||||
const float xFirstPatchStart = terrainBounds.GetMin().GetX() - fmod(terrainBounds.GetMin().GetX(), GridMeters);
|
||||
const float xLastPatchStart = terrainBounds.GetMax().GetX() - fmod(terrainBounds.GetMax().GetX(), GridMeters);
|
||||
const float yFirstPatchStart = terrainBounds.GetMin().GetY() - fmod(terrainBounds.GetMin().GetY(), GridMeters);
|
||||
const float yLastPatchStart = terrainBounds.GetMax().GetY() - fmod(terrainBounds.GetMax().GetY(), GridMeters);
|
||||
const float xFirstPatchStart = AZStd::floorf(terrainBounds.GetMin().GetX() / GridMeters) * GridMeters;
|
||||
const float xLastPatchStart = AZStd::floorf(terrainBounds.GetMax().GetX() / GridMeters) * GridMeters;
|
||||
const float yFirstPatchStart = AZStd::floorf(terrainBounds.GetMin().GetY() / GridMeters) * GridMeters;
|
||||
const float yLastPatchStart = AZStd::floorf(terrainBounds.GetMax().GetY() / GridMeters) * GridMeters;
|
||||
|
||||
const auto& materialAsset = m_materialInstance->GetAsset();
|
||||
const auto& shaderAsset = materialAsset->GetMaterialTypeAsset()->GetShaderAssetForObjectSrg();
|
||||
@@ -1217,7 +1217,7 @@ namespace Terrain
|
||||
|
||||
// For every distance doubling beyond a minDistanceForLod0, we only need half the mesh density. Each LOD
|
||||
// is exactly half the resolution of the last.
|
||||
const float lodForCamera = floorf(AZ::GetMax(0.0f, log2f(sectorDistance / minDistanceForLod0)));
|
||||
const float lodForCamera = AZStd::floorf(AZ::GetMax(0.0f, log2f(sectorDistance / minDistanceForLod0)));
|
||||
|
||||
// All cameras should render the same LOD so effects like shadows are consistent.
|
||||
lodChoice = AZ::GetMin(lodChoice, aznumeric_cast<uint8_t>(lodForCamera));
|
||||
|
||||
@@ -222,20 +222,31 @@ float TerrainSystem::GetHeightSynchronous(float x, float y, Sampler sampler, boo
|
||||
|
||||
float TerrainSystem::GetTerrainAreaHeight(float x, float y, bool& terrainExists) const
|
||||
{
|
||||
AZ::Vector3 inPosition((float)x, (float)y, m_currentSettings.m_worldBounds.GetMin().GetZ());
|
||||
float height = m_currentSettings.m_worldBounds.GetMin().GetZ();
|
||||
const float worldMin = m_currentSettings.m_worldBounds.GetMin().GetZ();
|
||||
AZ::Vector3 inPosition(x, y, worldMin);
|
||||
float height = worldMin;
|
||||
terrainExists = false;
|
||||
|
||||
AZStd::shared_lock<AZStd::shared_mutex> lock(m_areaMutex);
|
||||
|
||||
for (auto& [areaId, areaBounds] : m_registeredAreas)
|
||||
for (auto& [areaId, areaData] : m_registeredAreas)
|
||||
{
|
||||
inPosition.SetZ(areaBounds.GetMin().GetZ());
|
||||
if (areaBounds.Contains(inPosition))
|
||||
const float areaMin = areaData.m_areaBounds.GetMin().GetZ();
|
||||
inPosition.SetZ(areaMin);
|
||||
if (areaData.m_areaBounds.Contains(inPosition))
|
||||
{
|
||||
AZ::Vector3 outPosition;
|
||||
Terrain::TerrainAreaHeightRequestBus::Event(
|
||||
areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, terrainExists);
|
||||
height = outPosition.GetZ();
|
||||
if (!terrainExists)
|
||||
{
|
||||
// If the terrain height provider doesn't have any data, then check the area's "use ground plane" setting.
|
||||
// If it's set, then create a default ground plane by saying terrain exists at the minimum height for the area.
|
||||
// Otherwise, we'll set the height at the terrain world minimum and say it doesn't exist.
|
||||
terrainExists = areaData.m_useGroundPlane;
|
||||
height = areaData.m_useGroundPlane ? areaMin : worldMin;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -395,12 +406,12 @@ AZ::EntityId TerrainSystem::FindBestAreaEntityAtPosition(float x, float y, AZ::A
|
||||
AZStd::shared_lock<AZStd::shared_mutex> lock(m_areaMutex);
|
||||
|
||||
// The areas are sorted into priority order: the first area that contains inPosition is the most suitable.
|
||||
for (const auto& [areaId, areaBounds] : m_registeredAreas)
|
||||
for (const auto& [areaId, areaData] : m_registeredAreas)
|
||||
{
|
||||
inPosition.SetZ(areaBounds.GetMin().GetZ());
|
||||
if (areaBounds.Contains(inPosition))
|
||||
inPosition.SetZ(areaData.m_areaBounds.GetMin().GetZ());
|
||||
if (areaData.m_areaBounds.Contains(inPosition))
|
||||
{
|
||||
bounds = areaBounds;
|
||||
bounds = areaData.m_areaBounds;
|
||||
return areaId;
|
||||
}
|
||||
}
|
||||
@@ -548,7 +559,12 @@ void TerrainSystem::RegisterArea(AZ::EntityId areaId)
|
||||
AZStd::unique_lock<AZStd::shared_mutex> lock(m_areaMutex);
|
||||
AZ::Aabb aabb = AZ::Aabb::CreateNull();
|
||||
LmbrCentral::ShapeComponentRequestsBus::EventResult(aabb, areaId, &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb);
|
||||
m_registeredAreas[areaId] = aabb;
|
||||
|
||||
// Cache off whether or not this layer spawner should have a default ground plane when no other terrain height data exists.
|
||||
bool useGroundPlane = false;
|
||||
Terrain::TerrainSpawnerRequestBus::EventResult(useGroundPlane, areaId, &Terrain::TerrainSpawnerRequestBus::Events::GetUseGroundPlane);
|
||||
|
||||
m_registeredAreas[areaId] = { aabb, useGroundPlane };
|
||||
m_dirtyRegion.AddAabb(aabb);
|
||||
m_terrainHeightDirty = true;
|
||||
m_terrainSurfacesDirty = true;
|
||||
@@ -565,10 +581,10 @@ void TerrainSystem::UnregisterArea(AZ::EntityId areaId)
|
||||
m_registeredAreas,
|
||||
[areaId, this](const auto& item)
|
||||
{
|
||||
auto const& [entityId, aabb] = item;
|
||||
auto const& [entityId, areaData] = item;
|
||||
if (areaId == entityId)
|
||||
{
|
||||
m_dirtyRegion.AddAabb(aabb);
|
||||
m_dirtyRegion.AddAabb(areaData.m_areaBounds);
|
||||
m_terrainHeightDirty = true;
|
||||
m_terrainSurfacesDirty = true;
|
||||
return true;
|
||||
@@ -585,10 +601,10 @@ void TerrainSystem::RefreshArea(AZ::EntityId areaId, AzFramework::Terrain::Terra
|
||||
|
||||
auto areaAabb = m_registeredAreas.find(areaId);
|
||||
|
||||
AZ::Aabb oldAabb = (areaAabb != m_registeredAreas.end()) ? areaAabb->second : AZ::Aabb::CreateNull();
|
||||
AZ::Aabb oldAabb = (areaAabb != m_registeredAreas.end()) ? areaAabb->second.m_areaBounds : AZ::Aabb::CreateNull();
|
||||
AZ::Aabb newAabb = AZ::Aabb::CreateNull();
|
||||
LmbrCentral::ShapeComponentRequestsBus::EventResult(newAabb, areaId, &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb);
|
||||
m_registeredAreas[areaId] = newAabb;
|
||||
m_registeredAreas[areaId].m_areaBounds = newAabb;
|
||||
|
||||
AZ::Aabb expandedAabb = oldAabb;
|
||||
expandedAabb.AddAabb(newAabb);
|
||||
|
||||
@@ -168,7 +168,14 @@ namespace Terrain
|
||||
bool m_terrainSurfacesDirty = false;
|
||||
AZ::Aabb m_dirtyRegion;
|
||||
|
||||
// Cached data for each terrain area to use when looking up terrain data.
|
||||
struct TerrainAreaData
|
||||
{
|
||||
AZ::Aabb m_areaBounds{ AZ::Aabb::CreateNull() };
|
||||
bool m_useGroundPlane{ false };
|
||||
};
|
||||
|
||||
mutable AZStd::shared_mutex m_areaMutex;
|
||||
AZStd::map<AZ::EntityId, AZ::Aabb, TerrainLayerPriorityComparator> m_registeredAreas;
|
||||
AZStd::map<AZ::EntityId, TerrainAreaData, TerrainLayerPriorityComparator> m_registeredAreas;
|
||||
};
|
||||
} // namespace Terrain
|
||||
|
||||
Reference in New Issue
Block a user