Merge branch 'development' into issues/exception_handling

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

# Conflicts:
#	Code/Framework/AzFramework/Tests/Spawnable/SpawnableEntitiesManagerTests.cpp
This commit is contained in:
Esteban Papp
2021-10-15 08:55:35 -07:00
159 changed files with 3504 additions and 3481 deletions
-3
View File
@@ -10,8 +10,5 @@ ly_add_external_target(
NAME renderdoc
3RDPARTY_ROOT_DIRECTORY "${LY_RENDERDOC_PATH}"
VERSION
INCLUDE_DIRECTORIES
.
include
COMPILE_DEFINITIONS USE_RENDERDOC
)
@@ -7,3 +7,4 @@
#
set(RENDERDOC_RUNTIME_DEPENDENCIES "${BASE_PATH}/lib/librenderdoc.so")
set(RENDERDOC_INCLUDE_DIRECTORIES "include")
@@ -7,3 +7,4 @@
#
set(RENDERDOC_RUNTIME_DEPENDENCIES "${BASE_PATH}/renderdoc.dll")
set(RENDERDOC_INCLUDE_DIRECTORIES ".")
@@ -36,6 +36,7 @@ ly_add_target(
Gem::Atom_RPI.Edit
Gem::Atom_RPI.Public
Gem::Atom_RHI.Reflect
Gem::Atom_Feature_Common.Static
Gem::Atom_Bootstrap.Headers
)
@@ -0,0 +1,39 @@
/*
* 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
*
*/
#pragma once
#include <AzCore/Memory/Memory.h>
namespace AtomToolsFramework
{
//! Interface for describing scene content that will be rendered using the PreviewRenderer
class PreviewContent
{
public:
AZ_CLASS_ALLOCATOR(PreviewContent, AZ::SystemAllocator, 0);
PreviewContent() = default;
virtual ~PreviewContent() = default;
//! Initiate loading of scene content, models, materials, etc
virtual void Load() = 0;
//! Return true if content is loaded and ready to render
virtual bool IsReady() const = 0;
//! Return true if content failed to load
virtual bool IsError() const = 0;
//! Report any issues encountered while loading
virtual void ReportErrors() = 0;
//! Prepare or pose content before rendering
virtual void Update() = 0;
};
} // namespace AtomToolsFramework
@@ -0,0 +1,85 @@
/*
* 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
*
*/
#pragma once
#include <Atom/RPI.Public/Base.h>
#include <Atom/RPI.Public/Pass/AttachmentReadback.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewContent.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewRendererState.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewerFeatureProcessorProviderBus.h>
#include <AzFramework/Entity/GameEntityContextComponent.h>
namespace AzFramework
{
class Scene;
}
class QPixmap;
namespace AtomToolsFramework
{
//! Processes requests for setting up content that gets rendered to a texture and captured to an image
class PreviewRenderer final : public PreviewerFeatureProcessorProviderBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(PreviewRenderer, AZ::SystemAllocator, 0);
PreviewRenderer(const AZStd::string& sceneName, const AZStd::string& pipelineName);
~PreviewRenderer();
struct CaptureRequest final
{
int m_size = 512;
AZStd::shared_ptr<PreviewContent> m_content;
AZStd::function<void()> m_captureFailedCallback;
AZStd::function<void(const QPixmap&)> m_captureCompleteCallback;
};
void AddCaptureRequest(const CaptureRequest& captureRequest);
AZ::RPI::ScenePtr GetScene() const;
AZ::RPI::ViewPtr GetView() const;
AZ::Uuid GetEntityContextId() const;
void ProcessCaptureRequests();
void CancelCaptureRequest();
void CompleteCaptureRequest();
void LoadContent();
void UpdateLoadContent();
void CancelLoadContent();
void PoseContent();
bool StartCapture();
void EndCapture();
private:
//! AZ::Render::PreviewerFeatureProcessorProviderBus::Handler interface overrides...
void GetRequiredFeatureProcessors(AZStd::unordered_set<AZStd::string>& featureProcessors) const override;
static constexpr float AspectRatio = 1.0f;
static constexpr float NearDist = 0.001f;
static constexpr float FarDist = 100.0f;
static constexpr float FieldOfView = AZ::Constants::HalfPi;
AZ::RPI::ScenePtr m_scene;
AZStd::shared_ptr<AzFramework::Scene> m_frameworkScene;
AZ::RPI::RenderPipelinePtr m_renderPipeline;
AZ::RPI::ViewPtr m_view;
AZStd::vector<AZStd::string> m_passHierarchy;
AZStd::unique_ptr<AzFramework::EntityContext> m_entityContext;
//! Incoming requests are appended to this queue and processed one at a time in OnTick function.
AZStd::queue<CaptureRequest> m_captureRequestQueue;
CaptureRequest m_currentCaptureRequest;
AZStd::unique_ptr<PreviewRendererState> m_state;
};
} // namespace AtomToolsFramework
@@ -0,0 +1,29 @@
/*
* 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
*
*/
#pragma once
namespace AtomToolsFramework
{
class PreviewRenderer;
//! PreviewRendererState decouples PreviewRenderer logic into easy-to-understand and debug pieces
class PreviewRendererState
{
public:
explicit PreviewRendererState(PreviewRenderer* renderer)
: m_renderer(renderer)
{
}
virtual ~PreviewRendererState() = default;
protected:
PreviewRenderer* m_renderer = {};
};
} // namespace AtomToolsFramework
@@ -0,0 +1,25 @@
/*
* 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
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/string/string.h>
namespace AtomToolsFramework
{
//! PreviewerFeatureProcessorProviderRequests allows registering custom Feature Processors for preview image generation
class PreviewerFeatureProcessorProviderRequests : public AZ::EBusTraits
{
public:
//! Get a list of custom feature processors to register with preview image renderer
virtual void GetRequiredFeatureProcessors(AZStd::unordered_set<AZStd::string>& featureProcessors) const = 0;
};
using PreviewerFeatureProcessorProviderBus = AZ::EBus<PreviewerFeatureProcessorProviderRequests>;
} // namespace AtomToolsFramework
@@ -25,7 +25,7 @@
namespace AtomToolsFramework
{
//! The RenderViewportWidget class is a Qt wrapper around an Atom viewport.
//! RenderViewportWidget renders to an internal window using RPI::ViewportContext
//! RenderViewportWidget renders to an internal window using AZ::RPI::ViewportContext
//! and delegates input via its internal ViewportControllerList.
//! @see AZ::RPI::ViewportContext for Atom's API for setting up
class RenderViewportWidget
@@ -39,7 +39,7 @@ namespace AtomToolsFramework
public:
//! Creates a RenderViewportWidget.
//! Requires the Atom RPI to be initialized in order
//! to internally construct an RPI::ViewportContext.
//! to internally construct an AZ::RPI::ViewportContext.
//! If initializeViewportContext is set to false, nothing will be displayed on-screen until InitiliazeViewportContext is called.
explicit RenderViewportWidget(QWidget* parent = nullptr, bool shouldInitializeViewportContext = true);
~RenderViewportWidget();
@@ -0,0 +1,247 @@
/*
* 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 <Atom/Feature/Utils/FrameCaptureBus.h>
#include <Atom/RPI.Public/Pass/Specific/RenderToTexturePass.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
#include <Atom/RPI.Public/View.h>
#include <Atom/RPI.Reflect/System/RenderPipelineDescriptor.h>
#include <Atom/RPI.Reflect/System/SceneDescriptor.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewRenderer.h>
#include <AzCore/Math/MatrixUtils.h>
#include <AzCore/Math/Transform.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <PreviewRenderer/PreviewRendererCaptureState.h>
#include <PreviewRenderer/PreviewRendererIdleState.h>
#include <PreviewRenderer/PreviewRendererLoadState.h>
#include <QImage>
#include <QPixmap>
namespace AtomToolsFramework
{
PreviewRenderer::PreviewRenderer(const AZStd::string& sceneName, const AZStd::string& pipelineName)
{
PreviewerFeatureProcessorProviderBus::Handler::BusConnect();
m_entityContext = AZStd::make_unique<AzFramework::EntityContext>();
m_entityContext->InitContext();
// Create and register a scene with all required feature processors
AZStd::unordered_set<AZStd::string> featureProcessors;
PreviewerFeatureProcessorProviderBus::Broadcast(
&PreviewerFeatureProcessorProviderBus::Handler::GetRequiredFeatureProcessors, featureProcessors);
AZ::RPI::SceneDescriptor sceneDesc;
sceneDesc.m_featureProcessorNames.assign(featureProcessors.begin(), featureProcessors.end());
m_scene = AZ::RPI::Scene::CreateScene(sceneDesc);
// Bind m_frameworkScene to the entity context's AzFramework::Scene
auto sceneSystem = AzFramework::SceneSystemInterface::Get();
AZ_Assert(sceneSystem, "Failed to get scene system implementation.");
AZ::Outcome<AZStd::shared_ptr<AzFramework::Scene>, AZStd::string> createSceneOutcome = sceneSystem->CreateScene(sceneName);
AZ_Assert(createSceneOutcome, createSceneOutcome.GetError().c_str());
m_frameworkScene = createSceneOutcome.TakeValue();
m_frameworkScene->SetSubsystem(m_scene);
m_frameworkScene->SetSubsystem(m_entityContext.get());
// Create a render pipeline from the specified asset for the window context and add the pipeline to the scene
AZ::RPI::RenderPipelineDescriptor pipelineDesc;
pipelineDesc.m_mainViewTagName = "MainCamera";
pipelineDesc.m_name = pipelineName;
pipelineDesc.m_rootPassTemplate = "MainPipelineRenderToTexture";
// We have to set the samples to 4 to match the pipeline passes' setting, otherwise it may lead to device lost issue
// [GFX TODO] [ATOM-13551] Default value sand validation required to prevent pipeline crash and device lost
pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 4;
m_renderPipeline = AZ::RPI::RenderPipeline::CreateRenderPipeline(pipelineDesc);
m_scene->AddRenderPipeline(m_renderPipeline);
m_scene->Activate();
AZ::RPI::RPISystemInterface::Get()->RegisterScene(m_scene);
m_passHierarchy.push_back(pipelineName);
m_passHierarchy.push_back("CopyToSwapChain");
// Connect camera to pipeline's default view after camera entity activated
AZ::Matrix4x4 viewToClipMatrix;
AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix, FieldOfView, AspectRatio, NearDist, FarDist, true);
m_view = AZ::RPI::View::CreateView(AZ::Name("MainCamera"), AZ::RPI::View::UsageCamera);
m_view->SetViewToClipMatrix(viewToClipMatrix);
m_renderPipeline->SetDefaultView(m_view);
m_state.reset(new PreviewRendererIdleState(this));
}
PreviewRenderer::~PreviewRenderer()
{
PreviewerFeatureProcessorProviderBus::Handler::BusDisconnect();
m_state.reset();
m_currentCaptureRequest = {};
m_captureRequestQueue = {};
m_scene->Deactivate();
m_scene->RemoveRenderPipeline(m_renderPipeline->GetId());
AZ::RPI::RPISystemInterface::Get()->UnregisterScene(m_scene);
m_frameworkScene->UnsetSubsystem(m_scene);
m_frameworkScene->UnsetSubsystem(m_entityContext.get());
}
void PreviewRenderer::AddCaptureRequest(const CaptureRequest& captureRequest)
{
m_captureRequestQueue.push(captureRequest);
}
AZ::RPI::ScenePtr PreviewRenderer::GetScene() const
{
return m_scene;
}
AZ::RPI::ViewPtr PreviewRenderer::GetView() const
{
return m_view;
}
AZ::Uuid PreviewRenderer::GetEntityContextId() const
{
return m_entityContext->GetContextId();
}
void PreviewRenderer::ProcessCaptureRequests()
{
if (!m_captureRequestQueue.empty())
{
// pop the next request to be rendered from the queue
m_currentCaptureRequest = m_captureRequestQueue.front();
m_captureRequestQueue.pop();
m_state.reset();
m_state.reset(new PreviewRendererLoadState(this));
}
}
void PreviewRenderer::CancelCaptureRequest()
{
if (m_currentCaptureRequest.m_captureFailedCallback)
{
m_currentCaptureRequest.m_captureFailedCallback();
}
m_state.reset();
m_state.reset(new PreviewRendererIdleState(this));
}
void PreviewRenderer::CompleteCaptureRequest()
{
m_state.reset();
m_state.reset(new PreviewRendererIdleState(this));
}
void PreviewRenderer::LoadContent()
{
m_currentCaptureRequest.m_content->Load();
}
void PreviewRenderer::UpdateLoadContent()
{
if (m_currentCaptureRequest.m_content->IsReady())
{
m_state.reset();
m_state.reset(new PreviewRendererCaptureState(this));
return;
}
if (m_currentCaptureRequest.m_content->IsError())
{
CancelLoadContent();
return;
}
}
void PreviewRenderer::CancelLoadContent()
{
m_currentCaptureRequest.m_content->ReportErrors();
CancelCaptureRequest();
}
void PreviewRenderer::PoseContent()
{
m_currentCaptureRequest.m_content->Update();
}
bool PreviewRenderer::StartCapture()
{
auto captureCompleteCallback = m_currentCaptureRequest.m_captureCompleteCallback;
auto captureFailedCallback = m_currentCaptureRequest.m_captureFailedCallback;
auto captureCallback = [captureCompleteCallback, captureFailedCallback](const AZ::RPI::AttachmentReadback::ReadbackResult& result)
{
if (result.m_dataBuffer)
{
if (captureCompleteCallback)
{
captureCompleteCallback(QPixmap::fromImage(QImage(
result.m_dataBuffer.get()->data(), result.m_imageDescriptor.m_size.m_width,
result.m_imageDescriptor.m_size.m_height, QImage::Format_RGBA8888)));
}
}
else
{
if (captureFailedCallback)
{
captureFailedCallback();
}
}
};
if (auto renderToTexturePass = azrtti_cast<AZ::RPI::RenderToTexturePass*>(m_renderPipeline->GetRootPass().get()))
{
renderToTexturePass->ResizeOutput(m_currentCaptureRequest.m_size, m_currentCaptureRequest.m_size);
}
m_renderPipeline->AddToRenderTickOnce();
bool startedCapture = false;
AZ::Render::FrameCaptureRequestBus::BroadcastResult(
startedCapture, &AZ::Render::FrameCaptureRequestBus::Events::CapturePassAttachmentWithCallback, m_passHierarchy,
AZStd::string("Output"), captureCallback, AZ::RPI::PassAttachmentReadbackOption::Output);
return startedCapture;
}
void PreviewRenderer::EndCapture()
{
m_currentCaptureRequest = {};
m_renderPipeline->RemoveFromRenderTick();
}
void PreviewRenderer::GetRequiredFeatureProcessors(AZStd::unordered_set<AZStd::string>& featureProcessors) const
{
featureProcessors.insert({
"AZ::Render::TransformServiceFeatureProcessor",
"AZ::Render::MeshFeatureProcessor",
"AZ::Render::SimplePointLightFeatureProcessor",
"AZ::Render::SimpleSpotLightFeatureProcessor",
"AZ::Render::PointLightFeatureProcessor",
// There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow
// flickering [ATOM-13568]
// as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now.
// Possibly re-enable with [GFX TODO][ATOM-13639]
// "AZ::Render::DirectionalLightFeatureProcessor",
"AZ::Render::DiskLightFeatureProcessor",
"AZ::Render::CapsuleLightFeatureProcessor",
"AZ::Render::QuadLightFeatureProcessor",
"AZ::Render::DecalTextureArrayFeatureProcessor",
"AZ::Render::ImageBasedLightFeatureProcessor",
"AZ::Render::PostProcessFeatureProcessor",
"AZ::Render::SkyBoxFeatureProcessor" });
}
} // namespace AtomToolsFramework
@@ -0,0 +1,42 @@
/*
* 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 <AtomToolsFramework/PreviewRenderer/PreviewRenderer.h>
#include <PreviewRenderer/PreviewRendererCaptureState.h>
namespace AtomToolsFramework
{
PreviewRendererCaptureState::PreviewRendererCaptureState(PreviewRenderer* renderer)
: PreviewRendererState(renderer)
{
m_renderer->PoseContent();
AZ::TickBus::Handler::BusConnect();
}
PreviewRendererCaptureState::~PreviewRendererCaptureState()
{
AZ::Render::FrameCaptureNotificationBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
m_renderer->EndCapture();
}
void PreviewRendererCaptureState::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
if ((m_ticksToCapture-- <= 0) && m_renderer->StartCapture())
{
AZ::Render::FrameCaptureNotificationBus::Handler::BusConnect();
AZ::TickBus::Handler::BusDisconnect();
}
}
void PreviewRendererCaptureState::OnCaptureFinished(
[[maybe_unused]] AZ::Render::FrameCaptureResult result, [[maybe_unused]] const AZStd::string& info)
{
m_renderer->CompleteCaptureRequest();
}
} // namespace AtomToolsFramework
@@ -0,0 +1,37 @@
/*
* 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
*
*/
#pragma once
#include <Atom/Feature/Utils/FrameCaptureBus.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewRendererState.h>
#include <AzCore/Component/TickBus.h>
namespace AtomToolsFramework
{
//! PreviewRendererCaptureState renders a thumbnail to a pixmap and notifies MaterialOrModelThumbnail once finished
class PreviewRendererCaptureState final
: public PreviewRendererState
, public AZ::TickBus::Handler
, public AZ::Render::FrameCaptureNotificationBus::Handler
{
public:
PreviewRendererCaptureState(PreviewRenderer* renderer);
~PreviewRendererCaptureState();
private:
//! AZ::TickBus::Handler interface overrides...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
//! AZ::Render::FrameCaptureNotificationBus::Handler overrides...
void OnCaptureFinished(AZ::Render::FrameCaptureResult result, const AZStd::string& info) override;
//! This is necessary to suspend capture to allow a frame for Material and Mesh components to assign materials
int m_ticksToCapture = 1;
};
} // namespace AtomToolsFramework
@@ -0,0 +1,29 @@
/*
* 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 <AtomToolsFramework/PreviewRenderer/PreviewRenderer.h>
#include <PreviewRenderer/PreviewRendererIdleState.h>
namespace AtomToolsFramework
{
PreviewRendererIdleState::PreviewRendererIdleState(PreviewRenderer* renderer)
: PreviewRendererState(renderer)
{
AZ::TickBus::Handler::BusConnect();
}
PreviewRendererIdleState::~PreviewRendererIdleState()
{
AZ::TickBus::Handler::BusDisconnect();
}
void PreviewRendererIdleState::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
m_renderer->ProcessCaptureRequests();
}
} // namespace AtomToolsFramework
@@ -0,0 +1,29 @@
/*
* 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
*
*/
#pragma once
#include <AtomToolsFramework/PreviewRenderer/PreviewRendererState.h>
#include <AzCore/Component/TickBus.h>
namespace AtomToolsFramework
{
//! PreviewRendererIdleState checks whether there are any new thumbnails that need to be rendered every tick
class PreviewRendererIdleState final
: public PreviewRendererState
, public AZ::TickBus::Handler
{
public:
PreviewRendererIdleState(PreviewRenderer* renderer);
~PreviewRendererIdleState();
private:
//! AZ::TickBus::Handler interface overrides...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
};
} // namespace AtomToolsFramework
@@ -0,0 +1,36 @@
/*
* 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 <AtomToolsFramework/PreviewRenderer/PreviewRenderer.h>
#include <PreviewRenderer/PreviewRendererLoadState.h>
namespace AtomToolsFramework
{
PreviewRendererLoadState::PreviewRendererLoadState(PreviewRenderer* renderer)
: PreviewRendererState(renderer)
{
m_renderer->LoadContent();
AZ::TickBus::Handler::BusConnect();
}
PreviewRendererLoadState::~PreviewRendererLoadState()
{
AZ::TickBus::Handler::BusDisconnect();
}
void PreviewRendererLoadState::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
if ((m_timeRemainingS += deltaTime) > TimeOutS)
{
m_renderer->CancelLoadContent();
return;
}
m_renderer->UpdateLoadContent();
}
} // namespace AtomToolsFramework
@@ -0,0 +1,32 @@
/*
* 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
*
*/
#pragma once
#include <AzCore/Component/TickBus.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewRendererState.h>
namespace AtomToolsFramework
{
//! PreviewRendererLoadState pauses further rendering until all assets used for rendering a thumbnail have been loaded
class PreviewRendererLoadState final
: public PreviewRendererState
, public AZ::TickBus::Handler
{
public:
PreviewRendererLoadState(PreviewRenderer* renderer);
~PreviewRendererLoadState();
private:
//! AZ::TickBus::Handler interface overrides...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
static constexpr float TimeOutS = 5.0f;
float m_timeRemainingS = 0.0f;
};
} // namespace AtomToolsFramework
@@ -58,4 +58,15 @@ set(FILES
Source/Window/AtomToolsMainWindow.cpp
Source/Window/AtomToolsMainWindowSystemComponent.cpp
Source/Window/AtomToolsMainWindowSystemComponent.h
Include/AtomToolsFramework/PreviewRenderer/PreviewContent.h
Include/AtomToolsFramework/PreviewRenderer/PreviewRenderer.h
Include/AtomToolsFramework/PreviewRenderer/PreviewRendererState.h
Include/AtomToolsFramework/PreviewRenderer/PreviewerFeatureProcessorProviderBus.h
Source/PreviewRenderer/PreviewRenderer.cpp
Source/PreviewRenderer/PreviewRendererIdleState.cpp
Source/PreviewRenderer/PreviewRendererIdleState.h
Source/PreviewRenderer/PreviewRendererLoadState.cpp
Source/PreviewRenderer/PreviewRendererLoadState.h
Source/PreviewRenderer/PreviewRendererCaptureState.cpp
Source/PreviewRenderer/PreviewRendererCaptureState.h
)
@@ -1353,8 +1353,9 @@ namespace AZ::AtomBridge
// if 2d draw need to project pos to screen first
AzFramework::TextDrawParameters params;
AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext();
const auto dpiScaleFactor = viewportContext->GetDpiScalingFactor();
params.m_drawViewportId = viewportContext->GetId(); // get the viewport ID so default viewport works
params.m_position = AZ::Vector3(x, y, 1.0f);
params.m_position = AZ::Vector3(x * dpiScaleFactor, y * dpiScaleFactor, 1.0f);
params.m_color = m_rendState.m_color;
params.m_scale = AZ::Vector2(size);
params.m_hAlign = center ? AzFramework::TextHorizontalAlignment::Center : AzFramework::TextHorizontalAlignment::Left; //! Horizontal text alignment
@@ -0,0 +1,34 @@
/*
* 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
*
*/
#pragma once
#include <Atom/Feature/Material/MaterialAssignmentId.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/EBus/EBus.h>
class QPixmap;
namespace AZ
{
namespace Render
{
//! EditorMaterialSystemComponentNotifications is an interface for handling notifications from EditorMaterialSystemComponent, like
//! being informed that material preview images are available
class EditorMaterialSystemComponentNotifications : public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
//! Notify that a material preview image is ready
virtual void OnRenderMaterialPreviewComplete(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId, const QPixmap& pixmap) = 0;
};
using EditorMaterialSystemComponentNotificationBus = AZ::EBus<EditorMaterialSystemComponentNotifications>;
} // namespace Render
} // namespace AZ
@@ -5,20 +5,22 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Atom/Feature/Material/MaterialAssignmentId.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
#include <QPixmap>
namespace AZ
{
namespace Render
{
//! EditorMaterialSystemComponentRequests provides an interface to communicate with MaterialEditor
class EditorMaterialSystemComponentRequests
: public AZ::EBusTraits
//! EditorMaterialSystemComponentRequests provides an interface for interacting with EditorMaterialSystemComponent, performing
//! different operations like opening the material editor, the material instance inspector, and managing material preview images
class EditorMaterialSystemComponentRequests : public AZ::EBusTraits
{
public:
// Only a single handler is allowed
@@ -31,6 +33,14 @@ namespace AZ
//! Open material instance editor
virtual void OpenMaterialInspector(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) = 0;
//! Generate a material preview image
virtual void RenderMaterialPreview(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) = 0;
//! Get recently rendered material preview image
virtual QPixmap GetRenderedMaterialPreview(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) const = 0;
};
using EditorMaterialSystemComponentRequestBus = AZ::EBus<EditorMaterialSystemComponentRequests>;
} // namespace Render
@@ -1,33 +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
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
//! ThumbnailFeatureProcessorProviderRequests allows registering custom Feature Processors for thumbnail generation
//! Duplicates will be ignored
//! You can check minimal feature processors that are already registered in CommonThumbnailRenderer.cpp
class ThumbnailFeatureProcessorProviderRequests
: public AZ::EBusTraits
{
public:
//! Get a list of custom feature processors to register with thumbnail renderer
virtual const AZStd::vector<AZStd::string>& GetCustomFeatureProcessors() const = 0;
};
using ThumbnailFeatureProcessorProviderBus = AZ::EBus<ThumbnailFeatureProcessorProviderRequests>;
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -6,15 +6,17 @@
*
*/
#include <EditorCommonFeaturesSystemComponent.h>
#include <SkinnedMesh/SkinnedMeshDebugDisplay.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzToolsFramework/API/EditorCameraBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <EditorCommonFeaturesSystemComponent.h>
#include <SharedPreview/SharedThumbnail.h>
#include <SkinnedMesh/SkinnedMeshDebugDisplay.h>
#include <IEditor.h>
@@ -68,7 +70,7 @@ namespace AZ
void EditorCommonFeaturesSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
AZ_UNUSED(required);
required.push_back(AZ_CRC_CE("ThumbnailerService"));
}
void EditorCommonFeaturesSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
@@ -82,24 +84,23 @@ namespace AZ
void EditorCommonFeaturesSystemComponent::Activate()
{
m_renderer = AZStd::make_unique<AZ::LyIntegration::Thumbnails::CommonThumbnailRenderer>();
m_previewerFactory = AZStd::make_unique <LyIntegration::CommonPreviewerFactory>();
m_skinnedMeshDebugDisplay = AZStd::make_unique<SkinnedMeshDebugDisplay>();
AzToolsFramework::EditorLevelNotificationBus::Handler::BusConnect();
AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler::BusConnect();
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect();
}
void EditorCommonFeaturesSystemComponent::Deactivate()
{
AzToolsFramework::EditorLevelNotificationBus::Handler::BusDisconnect();
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect();
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
AzToolsFramework::EditorLevelNotificationBus::Handler::BusDisconnect();
AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler::BusDisconnect();
m_skinnedMeshDebugDisplay.reset();
m_previewerFactory.reset();
m_renderer.reset();
TeardownThumbnails();
}
void EditorCommonFeaturesSystemComponent::OnNewLevelCreated()
@@ -191,6 +192,13 @@ namespace AZ
}
}
void EditorCommonFeaturesSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile)
{
AZ::TickBus::QueueFunction([this](){
SetupThumbnails();
});
}
const AzToolsFramework::AssetBrowser::PreviewerFactory* EditorCommonFeaturesSystemComponent::GetPreviewerFactory(
const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const
{
@@ -199,7 +207,33 @@ namespace AZ
void EditorCommonFeaturesSystemComponent::OnApplicationAboutToStop()
{
TeardownThumbnails();
}
void EditorCommonFeaturesSystemComponent::SetupThumbnails()
{
using namespace AzToolsFramework::Thumbnailer;
using namespace LyIntegration;
ThumbnailerRequestsBus::Broadcast(
&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(SharedThumbnailCache),
ThumbnailContext::DefaultContext);
m_renderer = AZStd::make_unique<AZ::LyIntegration::SharedThumbnailRenderer>();
m_previewerFactory = AZStd::make_unique<LyIntegration::SharedPreviewerFactory>();
}
void EditorCommonFeaturesSystemComponent::TeardownThumbnails()
{
using namespace AzToolsFramework::Thumbnailer;
using namespace LyIntegration;
ThumbnailerRequestsBus::Broadcast(
&ThumbnailerRequests::UnregisterThumbnailProvider, SharedThumbnailCache::ProviderName,
ThumbnailContext::DefaultContext);
m_renderer.reset();
m_previewerFactory.reset();
}
} // namespace Render
} // namespace AZ
@@ -11,10 +11,10 @@
#include <AzCore/Component/Component.h>
#include <AzFramework/Application/Application.h>
#include <AzToolsFramework/API/EditorLevelNotificationBus.h>
#include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h>
#include <AzToolsFramework/AssetBrowser/Previewer/PreviewerBus.h>
#include <Thumbnails/Rendering/CommonThumbnailRenderer.h>
#include <Source/Thumbnails/Preview/CommonPreviewerFactory.h>
#include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h>
#include <SharedPreview/SharedPreviewerFactory.h>
#include <SharedPreview/SharedThumbnailRenderer.h>
namespace AZ
{
@@ -28,6 +28,7 @@ namespace AZ
, public AzToolsFramework::EditorLevelNotificationBus::Handler
, public AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler
, public AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler
, public AzFramework::AssetCatalogEventBus::Handler
, public AzFramework::ApplicationLifecycleEvents::Bus::Handler
{
public:
@@ -53,15 +54,23 @@ namespace AZ
void OnNewLevelCreated() override;
// SliceEditorEntityOwnershipServiceBus overrides ...
void OnSliceInstantiated(const AZ::Data::AssetId&, AZ::SliceComponent::SliceInstanceAddress&, const AzFramework::SliceInstantiationTicket&) override;
void OnSliceInstantiated(
const AZ::Data::AssetId&, AZ::SliceComponent::SliceInstanceAddress&, const AzFramework::SliceInstantiationTicket&) override;
void OnSliceInstantiationFailed(const AZ::Data::AssetId&, const AzFramework::SliceInstantiationTicket&) override;
// AzFramework::AssetCatalogEventBus::Handler overrides ...
void OnCatalogLoaded(const char* catalogFile) override;
// AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler overrides...
const AzToolsFramework::AssetBrowser::PreviewerFactory* GetPreviewerFactory(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const override;
const AzToolsFramework::AssetBrowser::PreviewerFactory* GetPreviewerFactory(
const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const override;
// AzFramework::ApplicationLifecycleEvents overrides...
void OnApplicationAboutToStop() override;
void SetupThumbnails();
void TeardownThumbnails();
private:
AZStd::unique_ptr<SkinnedMeshDebugDisplay> m_skinnedMeshDebugDisplay;
@@ -69,8 +78,8 @@ namespace AZ
AZStd::string m_atomLevelDefaultAssetPath{ "LevelAssets/default.slice" };
float m_envProbeHeight{ 200.0f };
AZStd::unique_ptr<AZ::LyIntegration::Thumbnails::CommonThumbnailRenderer> m_renderer;
AZStd::unique_ptr<LyIntegration::CommonPreviewerFactory> m_previewerFactory;
AZStd::unique_ptr<AZ::LyIntegration::SharedThumbnailRenderer> m_renderer;
AZStd::unique_ptr<LyIntegration::SharedPreviewerFactory> m_previewerFactory;
};
} // namespace Render
} // namespace AZ
@@ -148,11 +148,13 @@ namespace AZ
BaseClass::Activate();
MaterialReceiverNotificationBus::Handler::BusConnect(GetEntityId());
MaterialComponentNotificationBus::Handler::BusConnect(GetEntityId());
EditorMaterialSystemComponentNotificationBus::Handler::BusConnect();
UpdateMaterialSlots();
}
void EditorMaterialComponent::Deactivate()
{
EditorMaterialSystemComponentNotificationBus::Handler::BusDisconnect();
MaterialReceiverNotificationBus::Handler::BusDisconnect();
MaterialComponentNotificationBus::Handler::BusDisconnect();
BaseClass::Deactivate();
@@ -260,6 +262,18 @@ namespace AZ
}
}
void EditorMaterialComponent::OnRenderMaterialPreviewComplete(
[[maybe_unused]] const AZ::EntityId& entityId,
[[maybe_unused]] const AZ::Render::MaterialAssignmentId& materialAssignmentId,
[[maybe_unused]] const QPixmap& pixmap)
{
if (entityId == GetEntityId())
{
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_AttributesAndValues);
}
}
AZ::u32 EditorMaterialComponent::OnConfigurationChanged()
{
return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
@@ -9,6 +9,7 @@
#pragma once
#include <Atom/Feature/Utils/EditorRenderComponentAdapter.h>
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
#include <Material/EditorMaterialComponentSlot.h>
@@ -21,8 +22,9 @@ namespace AZ
//! In-editor material component for displaying and editing material assignments.
class EditorMaterialComponent final
: public EditorRenderComponentAdapter<MaterialComponentController, MaterialComponent, MaterialComponentConfig>
, private MaterialReceiverNotificationBus::Handler
, private MaterialComponentNotificationBus::Handler
, public MaterialReceiverNotificationBus::Handler
, public MaterialComponentNotificationBus::Handler
, public EditorMaterialSystemComponentNotificationBus::Handler
{
public:
using BaseClass = EditorRenderComponentAdapter<MaterialComponentController, MaterialComponent, MaterialComponentConfig>;
@@ -52,6 +54,10 @@ namespace AZ
//! MaterialComponentNotificationBus::Handler overrides...
void OnMaterialInstanceCreated(const MaterialAssignment& materialAssignment) override;
//! EditorMaterialSystemComponentNotificationBus::Handler overrides...
void OnRenderMaterialPreviewComplete(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId, const QPixmap& pixmap) override;
// Regenerates the editor component material slots based on the material and
// LOD mapping from the model or other consumer of materials.
// If any corresponding material assignments are found in the component
@@ -23,10 +23,6 @@
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/AssetBrowser/Thumbnails/ProductThumbnail.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <AzToolsFramework/Thumbnails/ThumbnailWidget.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConfig.h>
@@ -49,29 +45,18 @@ namespace AZ
MaterialPropertyInspector::MaterialPropertyInspector(QWidget* parent)
: AtomToolsFramework::InspectorWidget(parent)
{
// Create the menu button
QToolButton* menuButton = new QToolButton(this);
menuButton->setAutoRaise(true);
menuButton->setIcon(QIcon(":/Cards/img/UI20/Cards/menu_ico.svg"));
menuButton->setVisible(true);
QObject::connect(menuButton, &QToolButton::clicked, this, [this]() { OpenMenu(); });
AddHeading(menuButton);
m_messageLabel = new QLabel(this);
m_messageLabel->setWordWrap(true);
m_messageLabel->setVisible(true);
m_messageLabel->setAlignment(Qt::AlignCenter);
m_messageLabel->setText(tr("Material not available"));
AddHeading(m_messageLabel);
CreateHeading();
AZ::TickBus::Handler::BusConnect();
AZ::EntitySystemBus::Handler::BusConnect();
EditorMaterialSystemComponentNotificationBus::Handler::BusConnect();
}
MaterialPropertyInspector::~MaterialPropertyInspector()
{
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AZ::EntitySystemBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
AZ::EntitySystemBus::Handler::BusDisconnect();
EditorMaterialSystemComponentNotificationBus::Handler::BusDisconnect();
MaterialComponentNotificationBus::Handler::BusDisconnect();
}
@@ -140,7 +125,7 @@ namespace AZ
}
Populate();
m_messageLabel->setVisible(false);
LoadOverridesFromEntity();
return true;
}
@@ -152,8 +137,9 @@ namespace AZ
m_dirtyPropertyFlags.set();
m_editorFunctors = {};
m_internalEditNotification = {};
m_messageLabel->setVisible(true);
m_messageLabel->setText(tr("Material not available"));
m_updateUI = {};
m_updatePreview = {};
UpdateHeading();
}
bool MaterialPropertyInspector::IsLoaded() const
@@ -168,49 +154,63 @@ namespace AZ
m_dirtyPropertyFlags.set();
m_internalEditNotification = {};
AZ::TickBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorWidget::Reset();
}
void MaterialPropertyInspector::AddDetailsGroup()
void MaterialPropertyInspector::CreateHeading()
{
const AZStd::string& groupName = "Details";
const AZStd::string& groupDisplayName = "Details";
const AZStd::string& groupDescription = "";
// Create the menu button
QToolButton* menuButton = new QToolButton(this);
menuButton->setAutoRaise(true);
menuButton->setIcon(QIcon(":/Cards/img/UI20/Cards/menu_ico.svg"));
menuButton->setVisible(true);
QObject::connect(menuButton, &QToolButton::clicked, this, [this]() { OpenMenu(); });
AddHeading(menuButton);
auto propertyGroupContainer = new QWidget(this);
propertyGroupContainer->setLayout(new QHBoxLayout());
m_overviewImage = new QLabel(this);
m_overviewImage->setFixedSize(QSize(120, 120));
m_overviewImage->setScaledContents(true);
m_overviewImage->setVisible(false);
AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey =
MAKE_TKEY(AzToolsFramework::AssetBrowser::ProductThumbnailKey, m_editData.m_materialAssetId);
auto thumbnailWidget = new AzToolsFramework::Thumbnailer::ThumbnailWidget(this);
thumbnailWidget->setFixedSize(QSize(120, 120));
thumbnailWidget->setVisible(true);
thumbnailWidget->SetThumbnailKey(thumbnailKey, AzToolsFramework::Thumbnailer::ThumbnailContext::DefaultContext);
propertyGroupContainer->layout()->addWidget(thumbnailWidget);
auto materialInfoWidget = new QLabel(this);
m_overviewText = new QLabel(this);
QSizePolicy sizePolicy1(QSizePolicy::Ignored, QSizePolicy::Preferred);
sizePolicy1.setHorizontalStretch(0);
sizePolicy1.setVerticalStretch(0);
sizePolicy1.setHeightForWidth(materialInfoWidget->sizePolicy().hasHeightForWidth());
materialInfoWidget->setSizePolicy(sizePolicy1);
materialInfoWidget->setMinimumSize(QSize(0, 0));
materialInfoWidget->setMaximumSize(QSize(16777215, 16777215));
materialInfoWidget->setTextFormat(Qt::AutoText);
materialInfoWidget->setScaledContents(false);
materialInfoWidget->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop);
materialInfoWidget->setWordWrap(true);
sizePolicy1.setHeightForWidth(m_overviewText->sizePolicy().hasHeightForWidth());
m_overviewText->setSizePolicy(sizePolicy1);
m_overviewText->setMinimumSize(QSize(0, 0));
m_overviewText->setMaximumSize(QSize(16777215, 16777215));
m_overviewText->setTextFormat(Qt::AutoText);
m_overviewText->setScaledContents(false);
m_overviewText->setWordWrap(true);
m_overviewText->setVisible(true);
auto overviewContainer = new QWidget(this);
overviewContainer->setLayout(new QHBoxLayout());
overviewContainer->layout()->addWidget(m_overviewImage);
overviewContainer->layout()->addWidget(m_overviewText);
AddHeading(overviewContainer);
}
void MaterialPropertyInspector::UpdateHeading()
{
if (!IsLoaded())
{
m_overviewText->setText(tr("Material not available"));
m_overviewText->setAlignment(Qt::AlignCenter);
m_overviewImage->setVisible(false);
return;
}
QFileInfo materialFileInfo(AZ::RPI::AssetUtils::GetProductPathByAssetId(m_editData.m_materialAsset.GetId()).c_str());
QFileInfo materialSourceFileInfo(m_editData.m_materialSourcePath.c_str());
QFileInfo materialTypeSourceFileInfo(m_editData.m_materialTypeSourcePath.c_str());
QFileInfo materialParentSourceFileInfo(AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_editData.m_materialParentAsset.GetId()).c_str());
QFileInfo materialParentSourceFileInfo(
AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_editData.m_materialParentAsset.GetId()).c_str());
AZStd::string entityName;
AZ::ComponentApplicationBus::BroadcastResult(
entityName, &AZ::ComponentApplicationBus::Events::GetEntityName, m_entityId);
AZ::ComponentApplicationBus::BroadcastResult(entityName, &AZ::ComponentApplicationBus::Events::GetEntityName, m_entityId);
AZStd::string slotName;
MaterialComponentRequestBus::EventResult(
@@ -226,7 +226,8 @@ namespace AZ
}
if (!materialTypeSourceFileInfo.fileName().isEmpty())
{
materialInfo += tr("<tr><td><b>Material Type&emsp;</b></td><td>%1</td></tr>").arg(materialTypeSourceFileInfo.fileName());
materialInfo +=
tr("<tr><td><b>Material Type&emsp;</b></td><td>%1</td></tr>").arg(materialTypeSourceFileInfo.fileName());
}
if (!materialSourceFileInfo.fileName().isEmpty())
{
@@ -234,14 +235,21 @@ namespace AZ
}
if (!materialParentSourceFileInfo.fileName().isEmpty())
{
materialInfo += tr("<tr><td><b>Material Parent&emsp;</b></td><td>%1</td></tr>").arg(materialParentSourceFileInfo.fileName());
materialInfo +=
tr("<tr><td><b>Material Parent&emsp;</b></td><td>%1</td></tr>").arg(materialParentSourceFileInfo.fileName());
}
materialInfo += tr("</table>");
materialInfoWidget->setText(materialInfo);
propertyGroupContainer->layout()->addWidget(materialInfoWidget);
m_overviewText->setText(materialInfo);
m_overviewText->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop);
AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupContainer);
QPixmap pixmap;
EditorMaterialSystemComponentRequestBus::BroadcastResult(
pixmap, &EditorMaterialSystemComponentRequestBus::Events::GetRenderedMaterialPreview, m_entityId,
m_materialAssignmentId);
m_overviewImage->setPixmap(pixmap);
m_overviewImage->setVisible(true);
m_updatePreview |= pixmap.isNull();
}
void MaterialPropertyInspector::AddUvNamesGroup()
@@ -282,13 +290,8 @@ namespace AZ
AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupWidget);
}
void MaterialPropertyInspector::Populate()
void MaterialPropertyInspector::AddPropertiesGroup()
{
AddGroupsBegin();
AddDetailsGroup();
AddUvNamesGroup();
// Copy all of the properties from the material asset to the source data that will be exported
for (const auto& groupDefinition : m_editData.m_materialTypeSourceData.GetGroupDefinitionsInDisplayOrder())
{
@@ -327,10 +330,14 @@ namespace AZ
[this](const auto node) { return GetInstanceNodePropertyIndicator(node); }, 0);
AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupWidget);
}
}
void MaterialPropertyInspector::Populate()
{
AddGroupsBegin();
AddUvNamesGroup();
AddPropertiesGroup();
AddGroupsEnd();
LoadOverridesFromEntity();
}
void MaterialPropertyInspector::LoadOverridesFromEntity()
@@ -375,6 +382,7 @@ namespace AZ
m_dirtyPropertyFlags.set();
RunEditorMaterialFunctors();
RebuildAll();
UpdateHeading();
}
void MaterialPropertyInspector::SaveOverridesToEntity(bool commitChanges)
@@ -398,6 +406,9 @@ namespace AZ
MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited);
m_internalEditNotification = false;
}
// m_updatePreview should be set to true here for continuous preview updates as slider/color properties change but needs
// throttling
}
void MaterialPropertyInspector::RunEditorMaterialFunctors()
@@ -607,7 +618,8 @@ namespace AZ
MaterialComponentRequestBus::Event(
m_entityId, &MaterialComponentRequestBus::Events::SetPropertyOverrides, m_materialAssignmentId,
MaterialPropertyOverrideMap());
QueueUpdateUI();
m_updateUI = true;
m_updatePreview = true;
});
action->setEnabled(IsLoaded());
@@ -702,10 +714,7 @@ namespace AZ
void MaterialPropertyInspector::OnEntityActivated(const AZ::EntityId& entityId)
{
if (m_entityId == entityId)
{
QueueUpdateUI();
}
m_updateUI |= (m_entityId == entityId);
}
void MaterialPropertyInspector::OnEntityDeactivated(const AZ::EntityId& entityId)
@@ -719,25 +728,39 @@ namespace AZ
void MaterialPropertyInspector::OnEntityNameChanged(const AZ::EntityId& entityId, const AZStd::string& name)
{
AZ_UNUSED(name);
if (m_entityId == entityId)
{
QueueUpdateUI();
}
m_updateUI |= (m_entityId == entityId);
}
void MaterialPropertyInspector::OnTick(float deltaTime, ScriptTimePoint time)
{
AZ_UNUSED(time);
AZ_UNUSED(deltaTime);
UpdateUI();
AZ::TickBus::Handler::BusDisconnect();
if (m_updateUI)
{
m_updateUI = false;
UpdateUI();
}
if (m_updatePreview)
{
m_updatePreview = false;
EditorMaterialSystemComponentRequestBus::Broadcast(
&EditorMaterialSystemComponentRequestBus::Events::RenderMaterialPreview, m_entityId, m_materialAssignmentId);
}
}
void MaterialPropertyInspector::OnMaterialsEdited()
{
if (!m_internalEditNotification)
m_updateUI |= !m_internalEditNotification;
m_updatePreview = true;
}
void MaterialPropertyInspector::OnRenderMaterialPreviewComplete(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId, const QPixmap& pixmap)
{
if (m_overviewImage && m_entityId == entityId && m_materialAssignmentId == materialAssignmentId)
{
QueueUpdateUI();
m_overviewImage->setPixmap(pixmap);
}
}
@@ -761,16 +784,6 @@ namespace AZ
LoadMaterial(m_entityId, m_materialAssignmentId);
}
}
void MaterialPropertyInspector::QueueUpdateUI()
{
if (!AZ::TickBus::Handler::BusIsConnected())
{
AZ::TickBus::Handler::BusConnect();
}
}
} // namespace EditorMaterialComponentInspector
} // namespace Render
} // namespace AZ
//#include <AtomLyIntegration/CommonFeatures/moc_EditorMaterialComponentInspector.cpp>
@@ -9,6 +9,7 @@
#pragma once
#if !defined(Q_MOC_RUN)
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomToolsFramework/DynamicProperty/DynamicPropertyGroup.h>
#include <AtomToolsFramework/Inspector/InspectorWidget.h>
@@ -31,14 +32,13 @@ namespace AZ
{
namespace EditorMaterialComponentInspector
{
using PropertyChangedCallback = AZStd::function<void(const MaterialPropertyOverrideMap&)>;
class MaterialPropertyInspector
: public AtomToolsFramework::InspectorWidget
, public AzToolsFramework::IPropertyEditorNotify
, public AZ::EntitySystemBus::Handler
, public AZ::TickBus::Handler
, public MaterialComponentNotificationBus::Handler
, public EditorMaterialSystemComponentNotificationBus::Handler
{
Q_OBJECT
public:
@@ -89,11 +89,19 @@ namespace AZ
//! MaterialComponentNotificationBus::Handler overrides...
void OnMaterialsEdited() override;
void UpdateUI();
void QueueUpdateUI();
//! EditorMaterialSystemComponentNotificationBus::Handler overrides...
void OnRenderMaterialPreviewComplete(
const AZ::EntityId& entityId,
const AZ::Render::MaterialAssignmentId& materialAssignmentId,
const QPixmap& pixmap) override;
void UpdateUI();
void CreateHeading();
void UpdateHeading();
void AddDetailsGroup();
void AddUvNamesGroup();
void AddPropertiesGroup();
void LoadOverridesFromEntity();
void SaveOverridesToEntity(bool commitChanges);
@@ -115,7 +123,10 @@ namespace AZ
AZ::RPI::MaterialPropertyFlags m_dirtyPropertyFlags = {};
AZStd::unordered_map<AZStd::string, AtomToolsFramework::DynamicPropertyGroup> m_groups = {};
bool m_internalEditNotification = {};
QLabel* m_messageLabel = {};
bool m_updateUI = {};
bool m_updatePreview = {};
QLabel* m_overviewText = {};
QLabel* m_overviewImage = {};
};
} // namespace EditorMaterialComponentInspector
} // namespace Render
@@ -6,23 +6,25 @@
*
*/
#include <Material/EditorMaterialComponentSlot.h>
#include <Material/EditorMaterialComponentExporter.h>
#include <Material/EditorMaterialComponentInspector.h>
#include <Material/EditorMaterialModelUvNameMapInspector.h>
#include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Edit/Material/MaterialSourceData.h>
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h>
#include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <Material/EditorMaterialComponentExporter.h>
#include <Material/EditorMaterialComponentInspector.h>
#include <Material/EditorMaterialComponentSlot.h>
#include <Material/EditorMaterialModelUvNameMapInspector.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QMenu>
#include <QAction>
#include <QAction>
#include <QByteArray>
#include <QCursor>
#include <QDataStream>
#include <QMenu>
AZ_POP_DISABLE_WARNING
namespace AZ
@@ -100,6 +102,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &EditorMaterialComponentSlot::GetLabel)
->Attribute(AZ::Edit::Attributes::ShowProductAssetFileName, true)
->Attribute("ThumbnailCallback", &EditorMaterialComponentSlot::OpenPopupMenu)
->Attribute("ThumbnailIcon", &EditorMaterialComponentSlot::GetPreviewPixmapData)
;
}
}
@@ -118,6 +121,33 @@ namespace AZ
}
};
AZStd::vector<char> EditorMaterialComponentSlot::GetPreviewPixmapData() const
{
if (!GetActiveAssetId().IsValid())
{
return {};
}
QPixmap pixmap;
EditorMaterialSystemComponentRequestBus::BroadcastResult(
pixmap, &EditorMaterialSystemComponentRequestBus::Events::GetRenderedMaterialPreview, m_entityId, m_id);
if (pixmap.isNull())
{
if (m_updatePreview)
{
EditorMaterialSystemComponentRequestBus::Broadcast(
&EditorMaterialSystemComponentRequestBus::Events::RenderMaterialPreview, m_entityId, m_id);
m_updatePreview = false;
}
return {};
}
QByteArray pixmapBytes;
QDataStream stream(&pixmapBytes, QIODevice::WriteOnly);
stream << pixmap;
return AZStd::vector<char>(pixmapBytes.begin(), pixmapBytes.end());
}
AZ::Data::AssetId EditorMaterialComponentSlot::GetActiveAssetId() const
{
return m_materialAsset.GetId().IsValid() ? m_materialAsset.GetId() : GetDefaultAssetId();
@@ -169,14 +199,6 @@ namespace AZ
ClearOverrides();
}
void EditorMaterialComponentSlot::ClearToDefaultAsset()
{
m_materialAsset = AZ::Data::Asset<AZ::RPI::MaterialAsset>(GetDefaultAssetId(), AZ::AzTypeInfo<AZ::RPI::MaterialAsset>::Uuid());
MaterialComponentRequestBus::Event(
m_entityId, &MaterialComponentRequestBus::Events::SetMaterialOverride, m_id, m_materialAsset.GetId());
ClearOverrides();
}
void EditorMaterialComponentSlot::ClearOverrides()
{
MaterialComponentRequestBus::Event(
@@ -315,6 +337,10 @@ namespace AZ
AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
&AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, m_entityId);
EditorMaterialSystemComponentRequestBus::Broadcast(
&EditorMaterialSystemComponentRequestBus::Events::RenderMaterialPreview, m_entityId, m_id);
m_updatePreview = false;
MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited);
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
@@ -8,37 +8,52 @@
#pragma once
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/Asset/AssetCommon.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/Feature/Material/MaterialAssignment.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#include <QPixmap>
namespace AZ
{
namespace Render
{
static const size_t DefaultMaterialSlotIndex = std::numeric_limits<size_t>::max();
//! Details for a single editable material assignment
struct EditorMaterialComponentSlot final
{
AZ_RTTI(EditorMaterialComponentSlot, "{344066EB-7C3D-4E92-B53D-3C9EBD546488}");
AZ_CLASS_ALLOCATOR(EditorMaterialComponentSlot, SystemAllocator, 0);
static void Reflect(ReflectContext* context);
static bool ConvertVersion(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
static void Reflect(ReflectContext* context);
//! Get cached preview image as a buffer to use as an RPE attribute
//! If a cached image isn't avalible then a request will be made to render one
AZStd::vector<char> GetPreviewPixmapData() const;
//! Returns the overridden asset id if it's valid, otherwise gets the default asseet id
AZ::Data::AssetId GetActiveAssetId() const;
//! Returns the default asseet id of the material provded by the model
AZ::Data::AssetId GetDefaultAssetId() const;
//! Returns the display name of the material slot
AZStd::string GetLabel() const;
//! Returns true if the active material asset has a source material
bool HasSourceData() const;
//! Assign a new material override asset
void SetAsset(const Data::AssetId& assetId);
//! Assign a new material override asset
void SetAsset(const Data::Asset<RPI::MaterialAsset>& asset);
//! Remove material and prperty overrides
void Clear();
void ClearToDefaultAsset();
//! Remove prperty overrides
void ClearOverrides();
void OpenMaterialExporter();
@@ -54,6 +69,7 @@ namespace AZ
void OpenPopupMenu(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType);
void OnMaterialChanged() const;
void OnDataChanged() const;
mutable bool m_updatePreview = true;
};
// Vector of slots for assignable or overridable material data.
@@ -62,8 +78,8 @@ namespace AZ
// Table containing all editable material data that is displayed in the edit context and inspector
// The vector represents all the LODs that can have material overrides.
// The container will be populated with every potential material slot on an associated model, using its default values.
// Whenever changes are made to this container, the modified values are copied into the controller configuration material assignment map
// as overrides that will be applied to material instances
// Whenever changes are made to this container, the modified values are copied into the controller configuration material assignment
// map as overrides that will be applied to material instances
using EditorMaterialComponentSlotsByLodContainer = AZStd::vector<EditorMaterialComponentSlotContainer>;
} // namespace Render
} // namespace AZ
@@ -6,7 +6,10 @@
*
*/
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h>
#include <Atom/RHI/Factory.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
@@ -16,11 +19,10 @@
#include <AzFramework/Application/Application.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/ViewPaneOptions.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <Editor/LyViewPaneNames.h>
#include <Material/EditorMaterialComponentInspector.h>
#include <Material/EditorMaterialSystemComponent.h>
#include <Material/MaterialThumbnail.h>
#include <SharedPreview/SharedPreviewContent.h>
// Disables warning messages triggered by the Qt library
// 4251: class needs to have dll-interface to be used by clients of class
@@ -30,6 +32,8 @@ AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include <QApplication>
#include <QDockWidget>
#include <QObject>
#include <QPixmap>
#include <QImage>
#include <QProcessEnvironment>
AZ_POP_DISABLE_WARNING
@@ -72,11 +76,6 @@ namespace AZ
incompatible.push_back(AZ_CRC("EditorMaterialSystem", 0x5c93bc4e));
}
void EditorMaterialSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("ThumbnailerService", 0x65422b97));
}
void EditorMaterialSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
AZ_UNUSED(dependent);
@@ -89,25 +88,26 @@ namespace AZ
void EditorMaterialSystemComponent::Activate()
{
EditorMaterialSystemComponentNotificationBus::Handler::BusConnect();
EditorMaterialSystemComponentRequestBus::Handler::BusConnect();
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect();
AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusConnect();
AzToolsFramework::EditorMenuNotificationBus::Handler::BusConnect();
AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
SetupThumbnails();
m_materialBrowserInteractions.reset(aznew MaterialBrowserInteractions);
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect();
}
void EditorMaterialSystemComponent::Deactivate()
{
EditorMaterialSystemComponentRequestBus::Handler::BusDisconnect();
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect();
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
EditorMaterialSystemComponentNotificationBus::Handler::BusDisconnect();
EditorMaterialSystemComponentRequestBus::Handler::BusDisconnect();
AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorMenuNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
TeardownThumbnails();
m_previewRenderer.reset();
m_materialBrowserInteractions.reset();
if (m_openMaterialEditorAction)
@@ -154,11 +154,74 @@ namespace AZ
}
}
void EditorMaterialSystemComponent::OnApplicationAboutToStop()
void EditorMaterialSystemComponent::RenderMaterialPreview(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId)
{
TeardownThumbnails();
static constexpr const char* DefaultModelPath = "models/sphere.azmodel";
static constexpr const char* DefaultLightingPresetPath = "lightingpresets/thumbnail.lightingpreset.azasset";
if (m_previewRenderer)
{
AZ::Data::AssetId materialAssetId = {};
MaterialComponentRequestBus::EventResult(
materialAssetId, entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, materialAssignmentId);
if (!materialAssetId.IsValid())
{
MaterialComponentRequestBus::EventResult(
materialAssetId, entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId, materialAssignmentId);
if (!materialAssetId.IsValid())
{
return;
}
}
AZ::Render::MaterialPropertyOverrideMap propertyOverrides;
AZ::Render::MaterialComponentRequestBus::EventResult(
propertyOverrides, entityId, &AZ::Render::MaterialComponentRequestBus::Events::GetPropertyOverrides,
materialAssignmentId);
m_previewRenderer->AddCaptureRequest(
{ 128,
AZStd::make_shared<AZ::LyIntegration::SharedPreviewContent>(
m_previewRenderer->GetScene(), m_previewRenderer->GetView(), m_previewRenderer->GetEntityContextId(),
AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultModelPath), materialAssetId,
AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultLightingPresetPath), propertyOverrides),
[]()
{
// failed
},
[entityId, materialAssignmentId](const QPixmap& pixmap)
{
AZ::Render::EditorMaterialSystemComponentNotificationBus::Broadcast(
&AZ::Render::EditorMaterialSystemComponentNotificationBus::Events::OnRenderMaterialPreviewComplete, entityId,
materialAssignmentId, pixmap);
} });
}
}
QPixmap EditorMaterialSystemComponent::GetRenderedMaterialPreview(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) const
{
const auto& itr1 = m_materialPreviews.find(entityId);
if (itr1 != m_materialPreviews.end())
{
const auto& itr2 = itr1->second.find(materialAssignmentId);
if (itr2 != itr1->second.end())
{
return itr2->second;
}
}
return QPixmap();
}
void EditorMaterialSystemComponent::OnRenderMaterialPreviewComplete(
[[maybe_unused]] const AZ::EntityId& entityId,
[[maybe_unused]] const AZ::Render::MaterialAssignmentId& materialAssignmentId,
[[maybe_unused]] const QPixmap& pixmap)
{
m_materialPreviews[entityId][materialAssignmentId] = pixmap;
}
void EditorMaterialSystemComponent::OnPopulateToolMenuItems()
{
if (!m_openMaterialEditorAction)
@@ -201,24 +264,19 @@ namespace AZ
"Material Property Inspector", LyViewPane::CategoryTools, inspectorOptions);
}
void EditorMaterialSystemComponent::SetupThumbnails()
void EditorMaterialSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile)
{
using namespace AzToolsFramework::Thumbnailer;
using namespace LyIntegration;
ThumbnailerRequestsBus::Broadcast(
&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(Thumbnails::MaterialThumbnailCache),
ThumbnailContext::DefaultContext);
AZ::TickBus::QueueFunction([this](){
m_materialBrowserInteractions.reset(aznew MaterialBrowserInteractions);
m_previewRenderer.reset(aznew AtomToolsFramework::PreviewRenderer(
"EditorMaterialSystemComponent Preview Scene", "EditorMaterialSystemComponent Preview Pipeline"));
});
}
void EditorMaterialSystemComponent::TeardownThumbnails()
void EditorMaterialSystemComponent::OnApplicationAboutToStop()
{
using namespace AzToolsFramework::Thumbnailer;
using namespace LyIntegration;
ThumbnailerRequestsBus::Broadcast(
&ThumbnailerRequests::UnregisterThumbnailProvider, Thumbnails::MaterialThumbnailCache::ProviderName,
ThumbnailContext::DefaultContext);
m_previewRenderer.reset();
m_materialBrowserInteractions.reset();
}
AzToolsFramework::AssetBrowser::SourceFileDetails EditorMaterialSystemComponent::GetSourceFileDetails(
@@ -7,29 +7,31 @@
*/
#pragma once
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewRenderer.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Component.h>
#include <AzFramework/Application/Application.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <AzToolsFramework/Viewport/ActionBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h>
#include <Material/MaterialBrowserInteractions.h>
#include <QPixmap>
namespace AZ
{
namespace Render
{
//! System component that manages launching and maintaining connections with the material editor.
class EditorMaterialSystemComponent
class EditorMaterialSystemComponent final
: public AZ::Component
, private EditorMaterialSystemComponentRequestBus::Handler
, private AzFramework::ApplicationLifecycleEvents::Bus::Handler
, private AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler
, private AzToolsFramework::EditorMenuNotificationBus::Handler
, private AzToolsFramework::EditorEvents::Bus::Handler
, public EditorMaterialSystemComponentNotificationBus::Handler
, public EditorMaterialSystemComponentRequestBus::Handler
, public AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler
, public AzToolsFramework::EditorMenuNotificationBus::Handler
, public AzToolsFramework::EditorEvents::Bus::Handler
, public AzFramework::AssetCatalogEventBus::Handler
, public AzFramework::ApplicationLifecycleEvents::Bus::Handler
{
public:
AZ_COMPONENT(EditorMaterialSystemComponent, "{96652157-DA0B-420F-B49C-0207C585144C}");
@@ -38,7 +40,6 @@ namespace AZ
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
protected:
@@ -51,9 +52,13 @@ namespace AZ
//! EditorMaterialSystemComponentRequestBus::Handler overrides...
void OpenMaterialEditor(const AZStd::string& sourcePath) override;
void OpenMaterialInspector(const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) override;
void RenderMaterialPreview(const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) override;
QPixmap GetRenderedMaterialPreview(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) const override;
// AzFramework::ApplicationLifecycleEvents overrides...
void OnApplicationAboutToStop() override;
//! EditorMaterialSystemComponentNotificationBus::Handler overrides...
void OnRenderMaterialPreviewComplete(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId, const QPixmap& pixmap)override;
//! AssetBrowserInteractionNotificationBus::Handler overrides...
AzToolsFramework::AssetBrowser::SourceFileDetails GetSourceFileDetails(const char* fullSourceFileName) override;
@@ -65,12 +70,17 @@ namespace AZ
// AztoolsFramework::EditorEvents::Bus::Handler overrides...
void NotifyRegisterViews() override;
void SetupThumbnails();
void TeardownThumbnails();
// AzFramework::AssetCatalogEventBus::Handler overrides ...
void OnCatalogLoaded(const char* catalogFile) override;
// AzFramework::ApplicationLifecycleEvents overrides...
void OnApplicationAboutToStop() override;
QAction* m_openMaterialEditorAction = nullptr;
AZStd::unique_ptr<MaterialBrowserInteractions> m_materialBrowserInteractions;
AZStd::unique_ptr<AtomToolsFramework::PreviewRenderer> m_previewRenderer;
AZStd::unordered_map<AZ::EntityId, AZStd::unordered_map<AZ::Render::MaterialAssignmentId, QPixmap>> m_materialPreviews;
};
} // namespace Render
} // namespace AZ
@@ -1,112 +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 <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <QtConcurrent/QtConcurrent>
#include <Source/Material/MaterialThumbnail.h>
#include <Source/Thumbnails/ThumbnailUtils.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
static constexpr const int MaterialThumbnailSize = 512; // 512 is the default size in render to texture pass
//////////////////////////////////////////////////////////////////////////
// MaterialThumbnail
//////////////////////////////////////////////////////////////////////////
MaterialThumbnail::MaterialThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key)
: Thumbnail(key)
{
m_assetId = GetAssetId(key, RPI::MaterialAsset::RTTI_Type());
if (!m_assetId.IsValid())
{
AZ_Error("MaterialThumbnail", false, "Failed to find matching assetId for the thumbnailKey.");
m_state = State::Failed;
return;
}
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusConnect(key);
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
}
void MaterialThumbnail::LoadThread()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::QueueEvent(
RPI::MaterialAsset::RTTI_Type(),
&AzToolsFramework::Thumbnailer::ThumbnailerRendererRequests::RenderThumbnail,
m_key,
MaterialThumbnailSize);
// wait for response from thumbnail renderer
m_renderWait.acquire();
}
MaterialThumbnail::~MaterialThumbnail()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusDisconnect();
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
}
void MaterialThumbnail::ThumbnailRendered(const QPixmap& thumbnailImage)
{
m_pixmap = thumbnailImage;
m_renderWait.release();
}
void MaterialThumbnail::ThumbnailFailedToRender()
{
m_state = State::Failed;
m_renderWait.release();
}
void MaterialThumbnail::OnCatalogAssetChanged([[maybe_unused]] const AZ::Data::AssetId& assetId)
{
if (m_assetId == assetId &&
m_state == State::Ready)
{
m_state = State::Unloaded;
Load();
}
}
//////////////////////////////////////////////////////////////////////////
// MaterialThumbnailCache
//////////////////////////////////////////////////////////////////////////
MaterialThumbnailCache::MaterialThumbnailCache()
: ThumbnailCache<MaterialThumbnail>()
{
}
MaterialThumbnailCache::~MaterialThumbnailCache() = default;
int MaterialThumbnailCache::GetPriority() const
{
// Material thumbnails override default source thumbnails, so carry higher priority
return 1;
}
const char* MaterialThumbnailCache::GetProviderName() const
{
return ProviderName;
}
bool MaterialThumbnailCache::IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const
{
return
GetAssetId(key, RPI::MaterialAsset::RTTI_Type()).IsValid() &&
// in case it's a source scene file, it will contain both material and model products
// model thumbnails are handled by MeshThumbnail
!GetAssetId(key, RPI::ModelAsset::RTTI_Type()).IsValid();
}
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
#include <Material/moc_MaterialThumbnail.cpp>
@@ -1,73 +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
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/std/parallel/binary_semaphore.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <Thumbnails/Rendering/CommonThumbnailRenderer.h>
#endif
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
/**
* Custom material or model thumbnail that detects when an asset changes and updates the thumbnail
*/
class MaterialThumbnail
: public AzToolsFramework::Thumbnailer::Thumbnail
, public AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler
, private AzFramework::AssetCatalogEventBus::Handler
{
Q_OBJECT
public:
MaterialThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key);
~MaterialThumbnail() override;
//! AzToolsFramework::ThumbnailerRendererNotificationBus::Handler overrides...
void ThumbnailRendered(const QPixmap& thumbnailImage) override;
void ThumbnailFailedToRender() override;
protected:
void LoadThread() override;
private:
// AzFramework::AssetCatalogEventBus::Handler interface overrides...
void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
AZStd::binary_semaphore m_renderWait;
Data::AssetId m_assetId;
};
/**
* Cache configuration for large material thumbnails
*/
class MaterialThumbnailCache
: public AzToolsFramework::Thumbnailer::ThumbnailCache<MaterialThumbnail>
{
public:
MaterialThumbnailCache();
~MaterialThumbnailCache() override;
int GetPriority() const override;
const char* GetProviderName() const override;
static constexpr const char* ProviderName = "Material Thumbnails";
protected:
bool IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const override;
};
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -6,13 +6,10 @@
*
*/
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
#include <AzCore/Utils/Utils.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <Source/Mesh/EditorMeshSystemComponent.h>
#include <Source/Mesh/MeshThumbnail.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <Mesh/EditorMeshSystemComponent.h>
namespace AZ
{
@@ -47,11 +44,6 @@ namespace AZ
incompatible.push_back(AZ_CRC_CE("EditorMeshSystem"));
}
void EditorMeshSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC_CE("ThumbnailerService"));
}
void EditorMeshSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
AZ_UNUSED(dependent);
@@ -59,39 +51,10 @@ namespace AZ
void EditorMeshSystemComponent::Activate()
{
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect();
SetupThumbnails();
}
void EditorMeshSystemComponent::Deactivate()
{
TeardownThumbnails();
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect();
}
void EditorMeshSystemComponent::OnApplicationAboutToStop()
{
TeardownThumbnails();
}
void EditorMeshSystemComponent::SetupThumbnails()
{
using namespace AzToolsFramework::Thumbnailer;
using namespace LyIntegration;
ThumbnailerRequestsBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider,
MAKE_TCACHE(Thumbnails::MeshThumbnailCache),
ThumbnailContext::DefaultContext);
}
void EditorMeshSystemComponent::TeardownThumbnails()
{
using namespace AzToolsFramework::Thumbnailer;
using namespace LyIntegration;
ThumbnailerRequestsBus::Broadcast(&ThumbnailerRequests::UnregisterThumbnailProvider,
Thumbnails::MeshThumbnailCache::ProviderName,
ThumbnailContext::DefaultContext);
}
} // namespace Render
} // namespace AZ
@@ -8,7 +8,6 @@
#pragma once
#include <AzCore/Component/Component.h>
#include <AzFramework/Application/Application.h>
namespace AZ
{
@@ -17,7 +16,6 @@ namespace AZ
//! System component that sets up necessary logic related to EditorMeshComponent.
class EditorMeshSystemComponent
: public AZ::Component
, private AzFramework::ApplicationLifecycleEvents::Bus::Handler
{
public:
AZ_COMPONENT(EditorMeshSystemComponent, "{4D332E3D-C4FC-410B-A915-8E234CBDD4EC}");
@@ -26,20 +24,12 @@ namespace AZ
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
protected:
// AZ::Component interface overrides...
void Activate() override;
void Deactivate() override;
private:
// AzFramework::ApplicationLifecycleEvents overrides...
void OnApplicationAboutToStop() override;
void SetupThumbnails();
void TeardownThumbnails();
};
} // namespace Render
} // namespace AZ
@@ -1,109 +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 <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <QtConcurrent/QtConcurrent>
#include <Source/Mesh/MeshThumbnail.h>
#include <Source/Thumbnails/ThumbnailUtils.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
static constexpr const int MeshThumbnailSize = 512; // 512 is the default size in render to texture pass
//////////////////////////////////////////////////////////////////////////
// MeshThumbnail
//////////////////////////////////////////////////////////////////////////
MeshThumbnail::MeshThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key)
: Thumbnail(key)
{
m_assetId = GetAssetId(key, RPI::ModelAsset::RTTI_Type());
if (!m_assetId.IsValid())
{
AZ_Error("MeshThumbnail", false, "Failed to find matching assetId for the thumbnailKey.");
m_state = State::Failed;
return;
}
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusConnect(key);
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
}
void MeshThumbnail::LoadThread()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::QueueEvent(
RPI::ModelAsset::RTTI_Type(),
&AzToolsFramework::Thumbnailer::ThumbnailerRendererRequests::RenderThumbnail,
m_key,
MeshThumbnailSize);
// wait for response from thumbnail renderer
m_renderWait.acquire();
}
MeshThumbnail::~MeshThumbnail()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusDisconnect();
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
}
void MeshThumbnail::ThumbnailRendered(const QPixmap& thumbnailImage)
{
m_pixmap = thumbnailImage;
m_renderWait.release();
}
void MeshThumbnail::ThumbnailFailedToRender()
{
m_state = State::Failed;
m_renderWait.release();
}
void MeshThumbnail::OnCatalogAssetChanged([[maybe_unused]] const AZ::Data::AssetId& assetId)
{
if (m_assetId == assetId &&
m_state == State::Ready)
{
m_state = State::Unloaded;
Load();
}
}
//////////////////////////////////////////////////////////////////////////
// MeshThumbnailCache
//////////////////////////////////////////////////////////////////////////
MeshThumbnailCache::MeshThumbnailCache()
: ThumbnailCache<MeshThumbnail>()
{
}
MeshThumbnailCache::~MeshThumbnailCache() = default;
int MeshThumbnailCache::GetPriority() const
{
// Material thumbnails override default source thumbnails, so carry higher priority
return 1;
}
const char* MeshThumbnailCache::GetProviderName() const
{
return ProviderName;
}
bool MeshThumbnailCache::IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const
{
return GetAssetId(key, RPI::ModelAsset::RTTI_Type()).IsValid();
}
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
#include <Mesh/moc_MeshThumbnail.cpp>
@@ -1,72 +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
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/std/parallel/binary_semaphore.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#endif
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
/**
* Custom material or model thumbnail that detects when an asset changes and updates the thumbnail
*/
class MeshThumbnail
: public AzToolsFramework::Thumbnailer::Thumbnail
, public AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler
, private AzFramework::AssetCatalogEventBus::Handler
{
Q_OBJECT
public:
MeshThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key);
~MeshThumbnail() override;
//! AzToolsFramework::ThumbnailerRendererNotificationBus::Handler overrides...
void ThumbnailRendered(const QPixmap& thumbnailImage) override;
void ThumbnailFailedToRender() override;
protected:
void LoadThread() override;
private:
// AzFramework::AssetCatalogEventBus::Handler interface overrides...
void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
AZStd::binary_semaphore m_renderWait;
Data::AssetId m_assetId;
};
/**
* Cache configuration for large material thumbnails
*/
class MeshThumbnailCache
: public AzToolsFramework::Thumbnailer::ThumbnailCache<MeshThumbnail>
{
public:
MeshThumbnailCache();
~MeshThumbnailCache() override;
int GetPriority() const override;
const char* GetProviderName() const override;
static constexpr const char* ProviderName = "Mesh Thumbnails";
protected:
bool IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const override;
};
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -0,0 +1,173 @@
/*
* 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 <Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h>
#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
#include <Atom/Feature/Utils/LightingPreset.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Math/MatrixUtils.h>
#include <AzCore/Math/Transform.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <SharedPreview/SharedPreviewContent.h>
namespace AZ
{
namespace LyIntegration
{
SharedPreviewContent::SharedPreviewContent(
RPI::ScenePtr scene,
RPI::ViewPtr view,
AZ::Uuid entityContextId,
const Data::AssetId& modelAssetId,
const Data::AssetId& materialAssetId,
const Data::AssetId& lightingPresetAssetId,
const Render::MaterialPropertyOverrideMap& materialPropertyOverrides)
: m_scene(scene)
, m_view(view)
, m_entityContextId(entityContextId)
, m_materialPropertyOverrides(materialPropertyOverrides)
{
// Create preview model
AzFramework::EntityContextRequestBus::EventResult(
m_modelEntity, m_entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "SharedPreviewContentModel");
m_modelEntity->CreateComponent(Render::MeshComponentTypeId);
m_modelEntity->CreateComponent(Render::MaterialComponentTypeId);
m_modelEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_modelEntity->Init();
m_modelEntity->Activate();
m_modelAsset.Create(modelAssetId);
m_materialAsset.Create(materialAssetId);
m_lightingPresetAsset.Create(lightingPresetAssetId);
}
SharedPreviewContent::~SharedPreviewContent()
{
if (m_modelEntity)
{
m_modelEntity->Deactivate();
AzFramework::EntityContextRequestBus::Event(
m_entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_modelEntity);
m_modelEntity = nullptr;
}
}
void SharedPreviewContent::Load()
{
m_modelAsset.QueueLoad();
m_materialAsset.QueueLoad();
m_lightingPresetAsset.QueueLoad();
}
bool SharedPreviewContent::IsReady() const
{
return (!m_modelAsset.GetId().IsValid() || m_modelAsset.IsReady()) &&
(!m_materialAsset.GetId().IsValid() || m_materialAsset.IsReady()) &&
(!m_lightingPresetAsset.GetId().IsValid() || m_lightingPresetAsset.IsReady());
}
bool SharedPreviewContent::IsError() const
{
return m_modelAsset.IsError() || m_materialAsset.IsError() || m_lightingPresetAsset.IsError();
}
void SharedPreviewContent::ReportErrors()
{
AZ_Warning(
"SharedPreviewContent", !m_modelAsset.GetId().IsValid() || m_modelAsset.IsReady(), "Asset failed to load in time: %s",
m_modelAsset.ToString<AZStd::string>().c_str());
AZ_Warning(
"SharedPreviewContent", !m_materialAsset.GetId().IsValid() || m_materialAsset.IsReady(), "Asset failed to load in time: %s",
m_materialAsset.ToString<AZStd::string>().c_str());
AZ_Warning(
"SharedPreviewContent", !m_lightingPresetAsset.GetId().IsValid() || m_lightingPresetAsset.IsReady(),
"Asset failed to load in time: %s", m_lightingPresetAsset.ToString<AZStd::string>().c_str());
}
void SharedPreviewContent::Update()
{
UpdateModel();
UpdateLighting();
UpdateCamera();
}
void SharedPreviewContent::UpdateModel()
{
Render::MeshComponentRequestBus::Event(
m_modelEntity->GetId(), &Render::MeshComponentRequestBus::Events::SetModelAsset, m_modelAsset);
Render::MaterialComponentRequestBus::Event(
m_modelEntity->GetId(), &Render::MaterialComponentRequestBus::Events::SetMaterialOverride,
Render::DefaultMaterialAssignmentId, m_materialAsset.GetId());
Render::MaterialComponentRequestBus::Event(
m_modelEntity->GetId(), &Render::MaterialComponentRequestBus::Events::SetPropertyOverrides,
Render::DefaultMaterialAssignmentId, m_materialPropertyOverrides);
}
void SharedPreviewContent::UpdateLighting()
{
if (m_lightingPresetAsset.IsReady())
{
auto preset = m_lightingPresetAsset->GetDataAs<Render::LightingPreset>();
if (preset)
{
auto iblFeatureProcessor = m_scene->GetFeatureProcessor<Render::ImageBasedLightFeatureProcessorInterface>();
auto postProcessFeatureProcessor = m_scene->GetFeatureProcessor<Render::PostProcessFeatureProcessorInterface>();
auto postProcessSettingInterface = postProcessFeatureProcessor->GetOrCreateSettingsInterface(EntityId());
auto exposureControlSettingInterface = postProcessSettingInterface->GetOrCreateExposureControlSettingsInterface();
auto directionalLightFeatureProcessor =
m_scene->GetFeatureProcessor<Render::DirectionalLightFeatureProcessorInterface>();
auto skyboxFeatureProcessor = m_scene->GetFeatureProcessor<Render::SkyBoxFeatureProcessorInterface>();
skyboxFeatureProcessor->Enable(true);
skyboxFeatureProcessor->SetSkyboxMode(Render::SkyBoxMode::Cubemap);
Camera::Configuration cameraConfig;
cameraConfig.m_fovRadians = FieldOfView;
cameraConfig.m_nearClipDistance = NearDist;
cameraConfig.m_farClipDistance = FarDist;
cameraConfig.m_frustumWidth = 100.0f;
cameraConfig.m_frustumHeight = 100.0f;
AZStd::vector<Render::DirectionalLightFeatureProcessorInterface::LightHandle> lightHandles;
preset->ApplyLightingPreset(
iblFeatureProcessor, skyboxFeatureProcessor, exposureControlSettingInterface, directionalLightFeatureProcessor,
cameraConfig, lightHandles);
}
}
}
void SharedPreviewContent::UpdateCamera()
{
// Get bounding sphere of the model asset and estimate how far the camera needs to be see all of it
Vector3 center = {};
float radius = {};
if (m_modelAsset.IsReady())
{
m_modelAsset->GetAabb().GetAsSphere(center, radius);
}
const auto distance = radius + NearDist;
const auto cameraRotation = Quaternion::CreateFromAxisAngle(Vector3::CreateAxisZ(), CameraRotationAngle);
const auto cameraPosition = center + cameraRotation.TransformVector(Vector3(0.0f, distance, 0.0f));
const auto cameraTransform = Transform::CreateLookAt(cameraPosition, center);
m_view->SetCameraTransform(Matrix3x4::CreateFromTransform(cameraTransform));
}
} // namespace LyIntegration
} // namespace AZ
@@ -0,0 +1,67 @@
/*
* 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
*
*/
#pragma once
#include <Atom/Feature/Material/MaterialAssignment.h>
#include <Atom/RPI.Public/Base.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Atom/RPI.Reflect/System/AnyAsset.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewContent.h>
namespace AZ
{
namespace LyIntegration
{
//! Creates a simple scene used for most previews and thumbnails
class SharedPreviewContent final : public AtomToolsFramework::PreviewContent
{
public:
AZ_CLASS_ALLOCATOR(SharedPreviewContent, AZ::SystemAllocator, 0);
SharedPreviewContent(
RPI::ScenePtr scene,
RPI::ViewPtr view,
AZ::Uuid entityContextId,
const Data::AssetId& modelAssetId,
const Data::AssetId& materialAssetId,
const Data::AssetId& lightingPresetAssetId,
const Render::MaterialPropertyOverrideMap& materialPropertyOverrides);
~SharedPreviewContent() override;
void Load() override;
bool IsReady() const override;
bool IsError() const override;
void ReportErrors() override;
void Update() override;
private:
void UpdateModel();
void UpdateLighting();
void UpdateCamera();
static constexpr float AspectRatio = 1.0f;
static constexpr float NearDist = 0.001f;
static constexpr float FarDist = 100.0f;
static constexpr float FieldOfView = Constants::HalfPi;
static constexpr float CameraRotationAngle = Constants::QuarterPi / 2.0f;
RPI::ScenePtr m_scene;
RPI::ViewPtr m_view;
AZ::Uuid m_entityContextId;
Entity* m_modelEntity = nullptr;
Data::Asset<RPI::ModelAsset> m_modelAsset;
Data::Asset<RPI::MaterialAsset> m_materialAsset;
Data::Asset<RPI::AnyAsset> m_lightingPresetAsset;
Render::MaterialPropertyOverrideMap m_materialPropertyOverrides;
};
} // namespace LyIntegration
} // namespace AZ
@@ -11,37 +11,42 @@
#include <AssetBrowser/Thumbnails/SourceThumbnail.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Thumbnails/ThumbnailUtils.h>
#include <Atom/RPI.Reflect/System/AnyAsset.h>
#include <SharedPreview/SharedPreviewUtils.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
namespace SharedPreviewUtils
{
Data::AssetId GetAssetId(AzToolsFramework::Thumbnailer::SharedThumbnailKey key, const Data::AssetType& assetType)
Data::AssetId GetAssetId(
AzToolsFramework::Thumbnailer::SharedThumbnailKey key,
const Data::AssetType& assetType,
const Data::AssetId& defaultAssetId)
{
static const Data::AssetId invalidAssetId;
// if it's a source thumbnail key, find first product with a matching asset type
auto sourceKey = azrtti_cast<const AzToolsFramework::AssetBrowser::SourceThumbnailKey*>(key.data());
if (sourceKey)
{
bool foundIt = false;
AZStd::vector<Data::AssetInfo> productsAssetInfo;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(foundIt, &AzToolsFramework::AssetSystemRequestBus::Events::GetAssetsProducedBySourceUUID, sourceKey->GetSourceUuid(), productsAssetInfo);
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
foundIt, &AzToolsFramework::AssetSystemRequestBus::Events::GetAssetsProducedBySourceUUID,
sourceKey->GetSourceUuid(), productsAssetInfo);
if (!foundIt)
{
return invalidAssetId;
return defaultAssetId;
}
auto assetInfoIt = AZStd::find_if(productsAssetInfo.begin(), productsAssetInfo.end(),
auto assetInfoIt = AZStd::find_if(
productsAssetInfo.begin(), productsAssetInfo.end(),
[&assetType](const Data::AssetInfo& assetInfo)
{
return assetInfo.m_assetType == assetType;
});
if (assetInfoIt == productsAssetInfo.end())
{
return invalidAssetId;
return defaultAssetId;
}
return assetInfoIt->m_assetId;
@@ -53,10 +58,9 @@ namespace AZ
{
return productKey->GetAssetId();
}
return invalidAssetId;
return defaultAssetId;
}
QString WordWrap(const QString& string, int maxLength)
{
QString result;
@@ -81,6 +85,32 @@ namespace AZ
}
return result;
}
} // namespace Thumbnails
AZStd::unordered_set<AZ::Uuid> GetSupportedAssetTypes()
{
return { RPI::AnyAsset::RTTI_Type(), RPI::MaterialAsset::RTTI_Type(), RPI::ModelAsset::RTTI_Type() };
}
bool IsSupportedAssetType(AzToolsFramework::Thumbnailer::SharedThumbnailKey key)
{
for (const AZ::Uuid& typeId : SharedPreviewUtils::GetSupportedAssetTypes())
{
const AZ::Data::AssetId& assetId = SharedPreviewUtils::GetAssetId(key, typeId);
if (assetId.IsValid())
{
if (typeId == RPI::AnyAsset::RTTI_Type())
{
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById, assetId);
return AzFramework::StringFunc::EndsWith(assetInfo.m_relativePath.c_str(), "lightingpreset.azasset");
}
return true;
}
}
return false;
}
} // namespace SharedPreviewUtils
} // namespace LyIntegration
} // namespace AZ
@@ -18,13 +18,23 @@ namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
namespace SharedPreviewUtils
{
//! Get assetId by assetType that belongs to either source or product thumbnail key
Data::AssetId GetAssetId(AzToolsFramework::Thumbnailer::SharedThumbnailKey key, const Data::AssetType& assetType);
Data::AssetId GetAssetId(
AzToolsFramework::Thumbnailer::SharedThumbnailKey key,
const Data::AssetType& assetType,
const Data::AssetId& defaultAssetId = {});
//! Word wrap function for previewer QLabel, since by default it does not break long words such as filenames, so manual word wrap needed
//! Word wrap function for previewer QLabel, since by default it does not break long words such as filenames, so manual word
//! wrap needed
QString WordWrap(const QString& string, int maxLength);
} // namespace Thumbnails
//! Get the set of all asset types supported by the shared preview
AZStd::unordered_set<AZ::Uuid> GetSupportedAssetTypes();
//! Determine if a thumbnail key has an asset supported by the shared preview
bool IsSupportedAssetType(AzToolsFramework::Thumbnailer::SharedThumbnailKey key);
} // namespace SharedPreviewUtils
} // namespace LyIntegration
} // namespace AZ
@@ -7,23 +7,21 @@
*/
#include <AzCore/IO/FileIO.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <Source/Thumbnails/Preview/CommonPreviewer.h>
#include <Source/Thumbnails/ThumbnailUtils.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <SharedPreview/SharedPreviewUtils.h>
#include <SharedPreview/SharedPreviewer.h>
// Disables warning messages triggered by the Qt library
// 4251: class needs to have dll-interface to be used by clients of class
// 4251: class needs to have dll-interface to be used by clients of class
// 4800: forcing value to bool 'true' or 'false' (performance warning)
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include <Source/Thumbnails/Preview/ui_CommonPreviewer.h>
#include <QString>
#include <QResizeEvent>
#include <QString>
#include <SharedPreview/ui_SharedPreviewer.h>
AZ_POP_DISABLE_WARNING
namespace AZ
@@ -32,18 +30,22 @@ namespace AZ
{
static constexpr int CharWidth = 6;
CommonPreviewer::CommonPreviewer(QWidget* parent)
SharedPreviewer::SharedPreviewer(QWidget* parent)
: Previewer(parent)
, m_ui(new Ui::CommonPreviewerClass())
, m_ui(new Ui::SharedPreviewerClass())
{
m_ui->setupUi(this);
}
CommonPreviewer::~CommonPreviewer()
SharedPreviewer::~SharedPreviewer()
{
}
void CommonPreviewer::Display(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
void SharedPreviewer::Clear() const
{
}
void SharedPreviewer::Display(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
{
using namespace AzToolsFramework::AssetBrowser;
using namespace AzToolsFramework::Thumbnailer;
@@ -54,23 +56,23 @@ namespace AZ
UpdateFileInfo();
}
const QString& CommonPreviewer::GetName() const
const QString& SharedPreviewer::GetName() const
{
return m_name;
}
void CommonPreviewer::resizeEvent([[maybe_unused]] QResizeEvent* event)
void SharedPreviewer::resizeEvent([[maybe_unused]] QResizeEvent* event)
{
m_ui->m_previewWidget->setMaximumHeight(m_ui->m_previewWidget->width());
UpdateFileInfo();
}
void CommonPreviewer::UpdateFileInfo() const
void SharedPreviewer::UpdateFileInfo() const
{
m_ui->m_fileInfoLabel->setText(Thumbnails::WordWrap(m_fileInfo, m_ui->m_fileInfoLabel->width() / CharWidth));
m_ui->m_fileInfoLabel->setText(SharedPreviewUtils::WordWrap(m_fileInfo, m_ui->m_fileInfoLabel->width() / CharWidth));
}
} // namespace LyIntegration
} // namespace AZ
#include <Source/Thumbnails/Preview/moc_CommonPreviewer.cpp>
#include <SharedPreview/moc_SharedPreviewer.cpp>
@@ -5,22 +5,23 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/AssetBrowser/Previewer/Previewer.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QWidget>
#include <QScopedPointer>
#include <QWidget>
AZ_POP_DISABLE_WARNING
#endif
namespace Ui
{
class CommonPreviewerClass;
class SharedPreviewerClass;
}
namespace AzToolsFramework
@@ -30,8 +31,8 @@ namespace AzToolsFramework
class ProductAssetBrowserEntry;
class SourceAssetBrowserEntry;
class AssetBrowserEntry;
}
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
class QResizeEvent;
@@ -39,18 +40,17 @@ namespace AZ
{
namespace LyIntegration
{
class CommonPreviewer final
: public AzToolsFramework::AssetBrowser::Previewer
class SharedPreviewer final : public AzToolsFramework::AssetBrowser::Previewer
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(CommonPreviewer, AZ::SystemAllocator, 0);
AZ_CLASS_ALLOCATOR(SharedPreviewer, AZ::SystemAllocator, 0);
explicit CommonPreviewer(QWidget* parent = nullptr);
~CommonPreviewer();
explicit SharedPreviewer(QWidget* parent = nullptr);
~SharedPreviewer();
// AzToolsFramework::AssetBrowser::Previewer overrides...
void Clear() const override {}
void Clear() const override;
void Display(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) override;
const QString& GetName() const override;
@@ -60,9 +60,9 @@ namespace AZ
private:
void UpdateFileInfo() const;
QScopedPointer<Ui::CommonPreviewerClass> m_ui;
QScopedPointer<Ui::SharedPreviewerClass> m_ui;
QString m_fileInfo;
QString m_name = "CommonPreviewer";
QString m_name = "SharedPreviewer";
};
} // namespace LyIntegration
} // namespace AZ
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CommonPreviewerClass</class>
<widget class="QWidget" name="CommonPreviewerClass">
<class>SharedPreviewerClass</class>
<widget class="QWidget" name="SharedPreviewerClass">
<property name="geometry">
<rect>
<x>0</x>
@@ -0,0 +1,33 @@
/*
* 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 <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <SharedPreview/SharedPreviewUtils.h>
#include <SharedPreview/SharedPreviewer.h>
#include <SharedPreview/SharedPreviewerFactory.h>
namespace AZ
{
namespace LyIntegration
{
AzToolsFramework::AssetBrowser::Previewer* SharedPreviewerFactory::CreatePreviewer(QWidget* parent) const
{
return new SharedPreviewer(parent);
}
bool SharedPreviewerFactory::IsEntrySupported(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const
{
return SharedPreviewUtils::IsSupportedAssetType(entry->GetThumbnailKey());
}
const QString& SharedPreviewerFactory::GetName() const
{
return m_name;
}
} // namespace LyIntegration
} // namespace AZ
@@ -5,6 +5,7 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Memory/SystemAllocator.h>
@@ -18,14 +19,13 @@ namespace AZ
{
namespace LyIntegration
{
class CommonPreviewerFactory final
: public AzToolsFramework::AssetBrowser::PreviewerFactory
class SharedPreviewerFactory final : public AzToolsFramework::AssetBrowser::PreviewerFactory
{
public:
AZ_CLASS_ALLOCATOR(CommonPreviewerFactory, AZ::SystemAllocator, 0);
AZ_CLASS_ALLOCATOR(SharedPreviewerFactory, AZ::SystemAllocator, 0);
CommonPreviewerFactory() = default;
~CommonPreviewerFactory() = default;
SharedPreviewerFactory() = default;
~SharedPreviewerFactory() = default;
// AzToolsFramework::AssetBrowser::PreviewerFactory overrides...
AzToolsFramework::AssetBrowser::Previewer* CreatePreviewer(QWidget* parent = nullptr) const override;
@@ -33,7 +33,7 @@ namespace AZ
const QString& GetName() const override;
private:
QString m_name = "CommonPreviewer";
QString m_name = "SharedPreviewer";
};
} // namespace LyIntegration
} // namespace AZ
@@ -0,0 +1,106 @@
/*
* 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 <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <QtConcurrent/QtConcurrent>
#include <SharedPreview/SharedPreviewUtils.h>
#include <SharedPreview/SharedThumbnail.h>
namespace AZ
{
namespace LyIntegration
{
static constexpr const int SharedThumbnailSize = 256;
//////////////////////////////////////////////////////////////////////////
// SharedThumbnail
//////////////////////////////////////////////////////////////////////////
SharedThumbnail::SharedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key)
: Thumbnail(key)
{
for (const AZ::Uuid& typeId : SharedPreviewUtils::GetSupportedAssetTypes())
{
const AZ::Data::AssetId& assetId = SharedPreviewUtils::GetAssetId(key, typeId);
if (assetId.IsValid())
{
m_assetId = assetId;
m_typeId = typeId;
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusConnect(key);
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
return;
}
}
AZ_Error("SharedThumbnail", false, "Failed to find matching assetId for the thumbnailKey.");
m_state = State::Failed;
}
void SharedThumbnail::LoadThread()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::QueueEvent(
m_typeId, &AzToolsFramework::Thumbnailer::ThumbnailerRendererRequests::RenderThumbnail, m_key, SharedThumbnailSize);
// wait for response from thumbnail renderer
m_renderWait.acquire();
}
SharedThumbnail::~SharedThumbnail()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusDisconnect();
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
}
void SharedThumbnail::ThumbnailRendered(const QPixmap& thumbnailImage)
{
m_pixmap = thumbnailImage;
m_renderWait.release();
}
void SharedThumbnail::ThumbnailFailedToRender()
{
m_state = State::Failed;
m_renderWait.release();
}
void SharedThumbnail::OnCatalogAssetChanged([[maybe_unused]] const AZ::Data::AssetId& assetId)
{
if (m_assetId == assetId && m_state == State::Ready)
{
m_state = State::Unloaded;
Load();
}
}
//////////////////////////////////////////////////////////////////////////
// SharedThumbnailCache
//////////////////////////////////////////////////////////////////////////
SharedThumbnailCache::SharedThumbnailCache()
: ThumbnailCache<SharedThumbnail>()
{
}
SharedThumbnailCache::~SharedThumbnailCache() = default;
int SharedThumbnailCache::GetPriority() const
{
// Thumbnails override default source thumbnails, so carry higher priority
return 1;
}
const char* SharedThumbnailCache::GetProviderName() const
{
return ProviderName;
}
bool SharedThumbnailCache::IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const
{
return SharedPreviewUtils::IsSupportedAssetType(key);
}
} // namespace LyIntegration
} // namespace AZ
#include <SharedPreview/moc_SharedThumbnail.cpp>
@@ -0,0 +1,65 @@
/*
* 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
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/std/parallel/binary_semaphore.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#endif
namespace AZ
{
namespace LyIntegration
{
//! Custom thumbnail that detects when an asset changes and updates the thumbnail
class SharedThumbnail final
: public AzToolsFramework::Thumbnailer::Thumbnail
, public AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler
, private AzFramework::AssetCatalogEventBus::Handler
{
Q_OBJECT
public:
SharedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key);
~SharedThumbnail() override;
//! AzToolsFramework::ThumbnailerRendererNotificationBus::Handler overrides...
void ThumbnailRendered(const QPixmap& thumbnailImage) override;
void ThumbnailFailedToRender() override;
protected:
void LoadThread() override;
private:
// AzFramework::AssetCatalogEventBus::Handler interface overrides...
void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
AZStd::binary_semaphore m_renderWait;
Data::AssetId m_assetId;
AZ::Uuid m_typeId;
};
//! Cache configuration for large thumbnails
class SharedThumbnailCache final : public AzToolsFramework::Thumbnailer::ThumbnailCache<SharedThumbnail>
{
public:
SharedThumbnailCache();
~SharedThumbnailCache() override;
int GetPriority() const override;
const char* GetProviderName() const override;
static constexpr const char* ProviderName = "Common Feature Shared Thumbnail= Provider";
protected:
bool IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const override;
};
} // namespace LyIntegration
} // namespace AZ
@@ -0,0 +1,73 @@
/*
* 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 <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <SharedPreview/SharedPreviewContent.h>
#include <SharedPreview/SharedPreviewUtils.h>
#include <SharedPreview/SharedThumbnailRenderer.h>
namespace AZ
{
namespace LyIntegration
{
SharedThumbnailRenderer::SharedThumbnailRenderer()
{
m_previewRenderer.reset(aznew AtomToolsFramework::PreviewRenderer(
"SharedThumbnailRenderer Preview Scene", "SharedThumbnailRenderer Preview Pipeline"));
m_defaultModelAsset.Create(DefaultModelAssetId, true);
m_defaultMaterialAsset.Create(DefaultMaterialAssetId, true);
m_defaultLightingPresetAsset.Create(DefaultLightingPresetAssetId, true);
for (const AZ::Uuid& typeId : SharedPreviewUtils::GetSupportedAssetTypes())
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(typeId);
}
SystemTickBus::Handler::BusConnect();
}
SharedThumbnailRenderer::~SharedThumbnailRenderer()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusDisconnect();
SystemTickBus::Handler::BusDisconnect();
}
void SharedThumbnailRenderer::RenderThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize)
{
m_previewRenderer->AddCaptureRequest(
{ thumbnailSize,
AZStd::make_shared<SharedPreviewContent>(
m_previewRenderer->GetScene(), m_previewRenderer->GetView(), m_previewRenderer->GetEntityContextId(),
SharedPreviewUtils::GetAssetId(thumbnailKey, RPI::ModelAsset::RTTI_Type(), DefaultModelAssetId),
SharedPreviewUtils::GetAssetId(thumbnailKey, RPI::MaterialAsset::RTTI_Type(), DefaultMaterialAssetId),
SharedPreviewUtils::GetAssetId(thumbnailKey, RPI::AnyAsset::RTTI_Type(), DefaultLightingPresetAssetId),
Render::MaterialPropertyOverrideMap()),
[thumbnailKey]()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event(
thumbnailKey, &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender);
},
[thumbnailKey](const QPixmap& pixmap)
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event(
thumbnailKey, &AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailRendered, pixmap);
} });
}
bool SharedThumbnailRenderer::Installed() const
{
return true;
}
void SharedThumbnailRenderer::OnSystemTick()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::ExecuteQueuedEvents();
}
} // namespace LyIntegration
} // namespace AZ
@@ -0,0 +1,60 @@
/*
* 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
*
*/
#pragma once
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Atom/RPI.Reflect/System/AnyAsset.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewRenderer.h>
#include <AzCore/Component/TickBus.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <Thumbnails/Thumbnail.h>
namespace AZ
{
namespace LyIntegration
{
//! Provides custom rendering thumbnails of supported asset types
class SharedThumbnailRenderer final
: public AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler
, public SystemTickBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(SharedThumbnailRenderer, AZ::SystemAllocator, 0);
SharedThumbnailRenderer();
~SharedThumbnailRenderer();
private:
//! ThumbnailerRendererRequestsBus::Handler interface overrides...
void RenderThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize) override;
bool Installed() const override;
//! SystemTickBus::Handler interface overrides...
void OnSystemTick() override;
// Default assets to be kept loaded and used for rendering if not overridden
static constexpr const char* DefaultLightingPresetPath = "lightingpresets/thumbnail.lightingpreset.azasset";
const Data::AssetId DefaultLightingPresetAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultLightingPresetPath);
Data::Asset<RPI::AnyAsset> m_defaultLightingPresetAsset;
static constexpr const char* DefaultModelPath = "models/sphere.azmodel";
const Data::AssetId DefaultModelAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultModelPath);
Data::Asset<RPI::ModelAsset> m_defaultModelAsset;
static constexpr const char* DefaultMaterialPath = "";
const Data::AssetId DefaultMaterialAssetId;
Data::Asset<RPI::MaterialAsset> m_defaultMaterialAsset;
AZStd::unique_ptr<AtomToolsFramework::PreviewRenderer> m_previewRenderer;
};
} // namespace LyIntegration
} // namespace AZ
@@ -1,37 +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 <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Source/Thumbnails/Preview/CommonPreviewer.h>
#include <Source/Thumbnails/Preview/CommonPreviewerFactory.h>
#include <Source/Thumbnails/ThumbnailUtils.h>
namespace AZ
{
namespace LyIntegration
{
AzToolsFramework::AssetBrowser::Previewer* CommonPreviewerFactory::CreatePreviewer(QWidget* parent) const
{
return new CommonPreviewer(parent);
}
bool CommonPreviewerFactory::IsEntrySupported(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const
{
return
Thumbnails::GetAssetId(entry->GetThumbnailKey(), RPI::MaterialAsset::RTTI_Type()).IsValid() ||
Thumbnails::GetAssetId(entry->GetThumbnailKey(), RPI::ModelAsset::RTTI_Type()).IsValid();
}
const QString& CommonPreviewerFactory::GetName() const
{
return m_name;
}
} // namespace LyIntegration
} // namespace AZ
@@ -1,120 +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 <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <Thumbnails/Rendering/CommonThumbnailRenderer.h>
#include <Thumbnails/Rendering/ThumbnailRendererData.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/FindThumbnailToRenderStep.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/WaitForAssetsToLoadStep.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
CommonThumbnailRenderer::CommonThumbnailRenderer()
: m_data(new ThumbnailRendererData)
{
// CommonThumbnailRenderer supports both models and materials, but we connect on materialAssetType
// since MaterialOrModelThumbnail dispatches event on materialAssetType address too
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::MaterialAsset::RTTI_Type());
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::ModelAsset::RTTI_Type());
SystemTickBus::Handler::BusConnect();
ThumbnailFeatureProcessorProviderBus::Handler::BusConnect();
m_steps[Step::Initialize] = AZStd::make_shared<InitializeStep>(this);
m_steps[Step::FindThumbnailToRender] = AZStd::make_shared<FindThumbnailToRenderStep>(this);
m_steps[Step::WaitForAssetsToLoad] = AZStd::make_shared<WaitForAssetsToLoadStep>(this);
m_steps[Step::Capture] = AZStd::make_shared<CaptureStep>(this);
m_steps[Step::ReleaseResources] = AZStd::make_shared<ReleaseResourcesStep>(this);
m_minimalFeatureProcessors =
{
"AZ::Render::TransformServiceFeatureProcessor",
"AZ::Render::MeshFeatureProcessor",
"AZ::Render::SimplePointLightFeatureProcessor",
"AZ::Render::SimpleSpotLightFeatureProcessor",
"AZ::Render::PointLightFeatureProcessor",
// There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow
// flickering [ATOM-13568]
// as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now.
// Possibly re-enable with [GFX TODO][ATOM-13639]
// "AZ::Render::DirectionalLightFeatureProcessor",
"AZ::Render::DiskLightFeatureProcessor",
"AZ::Render::CapsuleLightFeatureProcessor",
"AZ::Render::QuadLightFeatureProcessor",
"AZ::Render::DecalTextureArrayFeatureProcessor",
"AZ::Render::ImageBasedLightFeatureProcessor",
"AZ::Render::PostProcessFeatureProcessor",
"AZ::Render::SkyBoxFeatureProcessor"
};
}
CommonThumbnailRenderer::~CommonThumbnailRenderer()
{
if (m_currentStep != Step::None)
{
CommonThumbnailRenderer::SetStep(Step::ReleaseResources);
}
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusDisconnect();
SystemTickBus::Handler::BusDisconnect();
ThumbnailFeatureProcessorProviderBus::Handler::BusDisconnect();
}
void CommonThumbnailRenderer::SetStep(Step step)
{
if (m_currentStep != Step::None)
{
m_steps[m_currentStep]->Stop();
}
m_currentStep = step;
m_steps[m_currentStep]->Start();
}
Step CommonThumbnailRenderer::GetStep() const
{
return m_currentStep;
}
bool CommonThumbnailRenderer::Installed() const
{
return true;
}
void CommonThumbnailRenderer::OnSystemTick()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::ExecuteQueuedEvents();
}
const AZStd::vector<AZStd::string>& CommonThumbnailRenderer::GetCustomFeatureProcessors() const
{
return m_minimalFeatureProcessors;
}
AZStd::shared_ptr<ThumbnailRendererData> CommonThumbnailRenderer::GetData() const
{
return m_data;
}
void CommonThumbnailRenderer::RenderThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize)
{
m_data->m_thumbnailSize = thumbnailSize;
m_data->m_thumbnailQueue.push(thumbnailKey);
if (m_currentStep == Step::None)
{
SetStep(Step::Initialize);
}
}
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -1,69 +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
*
*/
#pragma once
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <Thumbnails/Rendering/ThumbnailRendererContext.h>
#include <Thumbnails/Rendering/ThumbnailRendererData.h>
#include <AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h>
// Disables warning messages triggered by the Qt library
// 4251: class needs to have dll-interface to be used by clients of class
// 4800: forcing value to bool 'true' or 'false' (performance warning)
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include <QPixmap>
AZ_POP_DISABLE_WARNING
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
class ThumbnailRendererStep;
//! Provides custom rendering of material and model thumbnails
class CommonThumbnailRenderer
: public ThumbnailRendererContext
, private AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler
, private SystemTickBus::Handler
, private ThumbnailFeatureProcessorProviderBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(CommonThumbnailRenderer, AZ::SystemAllocator, 0)
CommonThumbnailRenderer();
~CommonThumbnailRenderer();
//! ThumbnailRendererContext overrides...
void SetStep(Step step) override;
Step GetStep() const override;
AZStd::shared_ptr<ThumbnailRendererData> GetData() const override;
private:
//! ThumbnailerRendererRequestsBus::Handler interface overrides...
void RenderThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey, int thumbnailSize) override;
bool Installed() const override;
//! SystemTickBus::Handler interface overrides...
void OnSystemTick() override;
//! Render::ThumbnailFeatureProcessorProviderBus::Handler interface overrides...
const AZStd::vector<AZStd::string>& GetCustomFeatureProcessors() const override;
AZStd::unordered_map<Step, AZStd::shared_ptr<ThumbnailRendererStep>> m_steps;
Step m_currentStep = Step::None;
AZStd::shared_ptr<ThumbnailRendererData> m_data;
AZStd::vector<AZStd::string> m_minimalFeatureProcessors;
};
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -1,42 +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
*
*/
#pragma once
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/ThumbnailRendererStep.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
struct ThumbnailRendererData;
enum class Step
{
None,
Initialize,
FindThumbnailToRender,
WaitForAssetsToLoad,
Capture,
ReleaseResources
};
//! An interface for ThumbnailRendererSteps to communicate with thumbnail renderer
class ThumbnailRendererContext
{
public:
virtual void SetStep(Step step) = 0;
virtual Step GetStep() const = 0;
virtual AZStd::shared_ptr<ThumbnailRendererData> GetData() const = 0;
};
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -1,70 +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
*
*/
#pragma once
#include "Atom/RPI.Reflect/Model/ModelAsset.h"
#include <Atom/RPI.Public/Base.h>
#include <Atom/RPI.Reflect/System/AnyAsset.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Math/Transform.h>
#include <AzFramework/Entity/GameEntityContextComponent.h>
#include <Thumbnails/Thumbnail.h>
namespace AzFramework
{
class Scene;
}
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
//! ThumbnailRendererData encapsulates all data used by thumbnail renderer and caches assets
struct ThumbnailRendererData final
{
static constexpr const char* LightingPresetPath = "lightingpresets/thumbnail.lightingpreset.azasset";
static constexpr const char* DefaultModelPath = "models/sphere.azmodel";
static constexpr const char* DefaultMaterialPath = "materials/basic_grey.azmaterial";
RPI::ScenePtr m_scene;
AZStd::string m_sceneName = "Material Thumbnail Scene";
AZStd::string m_pipelineName = "Material Thumbnail Pipeline";
AZStd::shared_ptr<AzFramework::Scene> m_frameworkScene;
RPI::RenderPipelinePtr m_renderPipeline;
AZStd::unique_ptr<AzFramework::EntityContext> m_entityContext;
AZStd::vector<AZStd::string> m_passHierarchy;
RPI::ViewPtr m_view = nullptr;
Entity* m_modelEntity = nullptr;
int m_thumbnailSize = 512;
//! Incoming thumbnail requests are appended to this queue and processed one at a time in OnTick function.
AZStd::queue<AzToolsFramework::Thumbnailer::SharedThumbnailKey> m_thumbnailQueue;
//! Current thumbnail key being rendered.
AzToolsFramework::Thumbnailer::SharedThumbnailKey m_thumbnailKeyRendered;
Data::Asset<RPI::AnyAsset> m_lightingPresetAsset;
Data::Asset<RPI::ModelAsset> m_defaultModelAsset;
//! Model asset about to be rendered
Data::Asset<RPI::ModelAsset> m_modelAsset;
Data::Asset<RPI::MaterialAsset> m_defaultMaterialAsset;
//! Material asset about to be rendered
Data::Asset<RPI::MaterialAsset> m_materialAsset;
AZStd::unordered_set<Data::AssetId> m_assetsToLoad;
};
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -1,130 +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 <Atom/Feature/Utils/FrameCaptureBus.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/View.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/TransformBus.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <Thumbnails/Rendering/ThumbnailRendererContext.h>
#include <Thumbnails/Rendering/ThumbnailRendererData.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.h>
#include <AzCore/Math/MatrixUtils.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
CaptureStep::CaptureStep(ThumbnailRendererContext* context)
: ThumbnailRendererStep(context)
{
}
void CaptureStep::Start()
{
if (!m_context->GetData()->m_materialAsset ||
!m_context->GetData()->m_modelAsset)
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event(
m_context->GetData()->m_thumbnailKeyRendered,
&AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender);
m_context->SetStep(Step::FindThumbnailToRender);
return;
}
Render::MaterialComponentRequestBus::Event(
m_context->GetData()->m_modelEntity->GetId(),
&Render::MaterialComponentRequestBus::Events::SetDefaultMaterialOverride,
m_context->GetData()->m_materialAsset.GetId());
Render::MeshComponentRequestBus::Event(
m_context->GetData()->m_modelEntity->GetId(),
&Render::MeshComponentRequestBus::Events::SetModelAsset,
m_context->GetData()->m_modelAsset);
RepositionCamera();
m_readyToCapture = true;
m_ticksToCapture = 1;
TickBus::Handler::BusConnect();
}
void CaptureStep::Stop()
{
m_context->GetData()->m_renderPipeline->RemoveFromRenderTick();
TickBus::Handler::BusDisconnect();
Render::FrameCaptureNotificationBus::Handler::BusDisconnect();
}
void CaptureStep::RepositionCamera() const
{
// Get bounding sphere of the model asset and estimate how far the camera needs to be see all of it
const Aabb& aabb = m_context->GetData()->m_modelAsset->GetAabb();
Vector3 modelCenter;
float radius;
aabb.GetAsSphere(modelCenter, radius);
float distance = StartingDistanceMultiplier *
GetMax(GetMax(aabb.GetExtents().GetX(), aabb.GetExtents().GetY()), aabb.GetExtents().GetZ()) +
DepthNear;
const Quaternion cameraRotation = Quaternion::CreateFromAxisAngle(Vector3::CreateAxisZ(), StartingRotationAngle);
Vector3 cameraPosition(modelCenter.GetX(), modelCenter.GetY() - distance, modelCenter.GetZ());
cameraPosition = cameraRotation.TransformVector(cameraPosition);
auto cameraTransform = Transform::CreateFromQuaternionAndTranslation(cameraRotation, cameraPosition);
m_context->GetData()->m_view->SetCameraTransform(Matrix3x4::CreateFromTransform(cameraTransform));
}
void CaptureStep::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time)
{
if (m_readyToCapture && m_ticksToCapture-- <= 0)
{
m_context->GetData()->m_renderPipeline->AddToRenderTickOnce();
RPI::AttachmentReadback::CallbackFunction readbackCallback = [&](const RPI::AttachmentReadback::ReadbackResult& result)
{
if (!result.m_dataBuffer)
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event(
m_context->GetData()->m_thumbnailKeyRendered,
&AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender);
return;
}
uchar* data = result.m_dataBuffer.get()->data();
QImage image(
data, result.m_imageDescriptor.m_size.m_width, result.m_imageDescriptor.m_size.m_height, QImage::Format_RGBA8888);
QPixmap pixmap;
pixmap.convertFromImage(image);
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event(
m_context->GetData()->m_thumbnailKeyRendered,
&AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailRendered,
pixmap);
};
Render::FrameCaptureNotificationBus::Handler::BusConnect();
bool startedCapture = false;
Render::FrameCaptureRequestBus::BroadcastResult(
startedCapture,
&Render::FrameCaptureRequestBus::Events::CapturePassAttachmentWithCallback,
m_context->GetData()->m_passHierarchy, AZStd::string("Output"), readbackCallback, RPI::PassAttachmentReadbackOption::Output);
// Reset the capture flag if the capture request was successful. Otherwise try capture it again next tick.
if (startedCapture)
{
m_readyToCapture = false;
}
}
}
void CaptureStep::OnCaptureFinished([[maybe_unused]] Render::FrameCaptureResult result, [[maybe_unused]] const AZStd::string& info)
{
m_context->SetStep(Step::FindThumbnailToRender);
}
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -1,55 +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
*
*/
#pragma once
#include <Atom/Feature/Utils/FrameCaptureBus.h>
#include <AzCore/Component/TickBus.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/ThumbnailRendererStep.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
//! CaptureStep renders a thumbnail to a pixmap and notifies MaterialOrModelThumbnail once finished
class CaptureStep
: public ThumbnailRendererStep
, private TickBus::Handler
, private Render::FrameCaptureNotificationBus::Handler
{
public:
CaptureStep(ThumbnailRendererContext* context);
void Start() override;
void Stop() override;
private:
//! Places the camera so that the entire model is visible
void RepositionCamera() const;
//! AZ::TickBus::Handler interface overrides...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
//! Render::FrameCaptureNotificationBus::Handler overrides...
void OnCaptureFinished(Render::FrameCaptureResult result, const AZStd::string& info) override;
static constexpr float DepthNear = 0.01f;
static constexpr float StartingDistanceMultiplier = 1.75f;
static constexpr float StartingRotationAngle = Constants::QuarterPi / 2.0f;
//! This flag is needed to wait one frame after each frame capture to reset FrameCaptureSystemComponent
bool m_readyToCapture = true;
//! This is necessary to suspend capture to allow a frame for Material and Mesh components to assign materials
int m_ticksToCapture = 0;
};
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -1,79 +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 <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Thumbnails/ThumbnailUtils.h>
#include <Thumbnails/Rendering/ThumbnailRendererContext.h>
#include <Thumbnails/Rendering/ThumbnailRendererData.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/FindThumbnailToRenderStep.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
FindThumbnailToRenderStep::FindThumbnailToRenderStep(ThumbnailRendererContext* context)
: ThumbnailRendererStep(context)
{
}
void FindThumbnailToRenderStep::Start()
{
TickBus::Handler::BusConnect();
}
void FindThumbnailToRenderStep::Stop()
{
TickBus::Handler::BusDisconnect();
}
void FindThumbnailToRenderStep::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time)
{
PickNextThumbnail();
}
void FindThumbnailToRenderStep::PickNextThumbnail()
{
if (!m_context->GetData()->m_thumbnailQueue.empty())
{
// pop the next thumbnailkey to be rendered from the queue
m_context->GetData()->m_thumbnailKeyRendered = m_context->GetData()->m_thumbnailQueue.front();
m_context->GetData()->m_thumbnailQueue.pop();
// Find whether thumbnailkey contains a material asset or set a default material
m_context->GetData()->m_materialAsset = m_context->GetData()->m_defaultMaterialAsset;
Data::AssetId materialAssetId = GetAssetId(m_context->GetData()->m_thumbnailKeyRendered, RPI::MaterialAsset::RTTI_Type());
if (materialAssetId.IsValid())
{
if (m_context->GetData()->m_assetsToLoad.emplace(materialAssetId).second)
{
m_context->GetData()->m_materialAsset.Create(materialAssetId);
m_context->GetData()->m_materialAsset.QueueLoad();
}
}
// Find whether thumbnailkey contains a model asset or set a default model
m_context->GetData()->m_modelAsset = m_context->GetData()->m_defaultModelAsset;
Data::AssetId modelAssetId = GetAssetId(m_context->GetData()->m_thumbnailKeyRendered, RPI::ModelAsset::RTTI_Type());
if (modelAssetId.IsValid())
{
if (m_context->GetData()->m_assetsToLoad.emplace(modelAssetId).second)
{
m_context->GetData()->m_modelAsset.Create(modelAssetId);
m_context->GetData()->m_modelAsset.QueueLoad();
}
}
m_context->SetStep(Step::WaitForAssetsToLoad);
}
}
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -1,40 +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
*
*/
#pragma once
#include <Thumbnails/Rendering/ThumbnailRendererSteps/ThumbnailRendererStep.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
//! FindThumbnailToRenderStep checks whether there are any new thumbnails that need to be rendered every tick
class FindThumbnailToRenderStep
: public ThumbnailRendererStep
, private TickBus::Handler
{
public:
FindThumbnailToRenderStep(ThumbnailRendererContext* context);
void Start() override;
void Stop() override;
private:
//! AZ::TickBus::Handler interface overrides...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
void PickNextThumbnail();
};
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -1,191 +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 <AzCore/Math/MatrixUtils.h>
#include <AzCore/EBus/Results.h>
#include <AzFramework/Components/TransformComponent.h>
#include <Atom/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h>
#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
#include <Atom/Feature/Utils/LightingPreset.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/View.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Atom/RPI.Reflect/System/RenderPipelineDescriptor.h>
#include <Atom/RPI.Reflect/System/SceneDescriptor.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h>
#include <Thumbnails/Rendering/ThumbnailRendererData.h>
#include <Thumbnails/Rendering/ThumbnailRendererContext.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
InitializeStep::InitializeStep(ThumbnailRendererContext* context)
: ThumbnailRendererStep(context)
{
}
void InitializeStep::Start()
{
auto data = m_context->GetData();
data->m_entityContext = AZStd::make_unique<AzFramework::EntityContext>();
data->m_entityContext->InitContext();
// Create and register a scene with all required feature processors
RPI::SceneDescriptor sceneDesc;
AZ::EBusAggregateResults<AZStd::vector<AZStd::string>> results;
ThumbnailFeatureProcessorProviderBus::BroadcastResult(results, &ThumbnailFeatureProcessorProviderBus::Handler::GetCustomFeatureProcessors);
AZStd::set<AZStd::string> featureProcessorNames;
for (auto& resultCollection : results.values)
{
for (auto& featureProcessorName : resultCollection)
{
if (featureProcessorNames.emplace(featureProcessorName).second)
{
sceneDesc.m_featureProcessorNames.push_back(featureProcessorName);
}
}
}
data->m_scene = RPI::Scene::CreateScene(sceneDesc);
// Bind m_defaultScene to the GameEntityContext's AzFramework::Scene
auto* sceneSystem = AzFramework::SceneSystemInterface::Get();
AZ_Assert(sceneSystem, "Thumbnail system failed to get scene system implementation.");
Outcome<AZStd::shared_ptr<AzFramework::Scene>, AZStd::string> createSceneOutcome =
sceneSystem->CreateScene(data->m_sceneName);
AZ_Assert(createSceneOutcome, createSceneOutcome.GetError().c_str()); // This should never happen unless scene creation has changed.
data->m_frameworkScene = createSceneOutcome.TakeValue();
data->m_frameworkScene->SetSubsystem(data->m_scene);
data->m_frameworkScene->SetSubsystem(data->m_entityContext.get());
// Create a render pipeline from the specified asset for the window context and add the pipeline to the scene
RPI::RenderPipelineDescriptor pipelineDesc;
pipelineDesc.m_mainViewTagName = "MainCamera";
pipelineDesc.m_name = data->m_pipelineName;
pipelineDesc.m_rootPassTemplate = "ThumbnailPipelineRenderToTexture";
// We have to set the samples to 4 to match the pipeline passes' setting, otherwise it may lead to device lost issue
// [GFX TODO] [ATOM-13551] Default value sand validation required to prevent pipeline crash and device lost
pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 4;
data->m_renderPipeline = RPI::RenderPipeline::CreateRenderPipeline(pipelineDesc);
data->m_scene->AddRenderPipeline(data->m_renderPipeline);
data->m_scene->Activate();
RPI::RPISystemInterface::Get()->RegisterScene(data->m_scene);
data->m_passHierarchy.push_back(data->m_pipelineName);
data->m_passHierarchy.push_back("CopyToSwapChain");
// Connect camera to pipeline's default view after camera entity activated
Name viewName = Name("MainCamera");
data->m_view = RPI::View::CreateView(viewName, RPI::View::UsageCamera);
Matrix4x4 viewToClipMatrix;
MakePerspectiveFovMatrixRH(viewToClipMatrix,
Constants::QuarterPi,
AspectRatio,
NearDist,
FarDist, true);
data->m_view->SetViewToClipMatrix(viewToClipMatrix);
data->m_renderPipeline->SetDefaultView(data->m_view);
// Create lighting preset
data->m_lightingPresetAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::AnyAsset>(ThumbnailRendererData::LightingPresetPath);
if (data->m_lightingPresetAsset.IsReady())
{
auto preset = data->m_lightingPresetAsset->GetDataAs<Render::LightingPreset>();
if (preset)
{
auto iblFeatureProcessor = data->m_scene->GetFeatureProcessor<Render::ImageBasedLightFeatureProcessorInterface>();
auto postProcessFeatureProcessor = data->m_scene->GetFeatureProcessor<Render::PostProcessFeatureProcessorInterface>();
auto exposureControlSettingInterface = postProcessFeatureProcessor->GetOrCreateSettingsInterface(EntityId())->GetOrCreateExposureControlSettingsInterface();
auto directionalLightFeatureProcessor = data->m_scene->GetFeatureProcessor<Render::DirectionalLightFeatureProcessorInterface>();
auto skyboxFeatureProcessor = data->m_scene->GetFeatureProcessor<Render::SkyBoxFeatureProcessorInterface>();
skyboxFeatureProcessor->Enable(true);
skyboxFeatureProcessor->SetSkyboxMode(Render::SkyBoxMode::Cubemap);
Camera::Configuration cameraConfig;
cameraConfig.m_fovRadians = Constants::HalfPi;
cameraConfig.m_nearClipDistance = NearDist;
cameraConfig.m_farClipDistance = FarDist;
cameraConfig.m_frustumWidth = 100.0f;
cameraConfig.m_frustumHeight = 100.0f;
AZStd::vector<Render::DirectionalLightFeatureProcessorInterface::LightHandle> lightHandles;
preset->ApplyLightingPreset(
iblFeatureProcessor,
skyboxFeatureProcessor,
exposureControlSettingInterface,
directionalLightFeatureProcessor,
cameraConfig,
lightHandles);
}
}
// Create preview model
AzFramework::EntityContextRequestBus::EventResult(data->m_modelEntity, data->m_entityContext->GetContextId(),
&AzFramework::EntityContextRequestBus::Events::CreateEntity, "ThumbnailPreviewModel");
data->m_modelEntity->CreateComponent(Render::MeshComponentTypeId);
data->m_modelEntity->CreateComponent(Render::MaterialComponentTypeId);
data->m_modelEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
data->m_modelEntity->Init();
data->m_modelEntity->Activate();
// preload default model
Data::AssetId defaultModelAssetId;
Data::AssetCatalogRequestBus::BroadcastResult(
defaultModelAssetId,
&Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
m_context->GetData()->DefaultModelPath,
RPI::ModelAsset::RTTI_Type(),
false);
AZ_Error("ThumbnailRenderer", defaultModelAssetId.IsValid(), "Default model asset is invalid. Verify the asset %s exists.", m_context->GetData()->DefaultModelPath);
if (m_context->GetData()->m_assetsToLoad.emplace(defaultModelAssetId).second)
{
data->m_defaultModelAsset.Create(defaultModelAssetId);
data->m_defaultModelAsset.QueueLoad();
}
// preload default material
Data::AssetId defaultMaterialAssetId;
Data::AssetCatalogRequestBus::BroadcastResult(
defaultMaterialAssetId,
&Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
m_context->GetData()->DefaultMaterialPath,
RPI::MaterialAsset::RTTI_Type(),
false);
AZ_Error("ThumbnailRenderer", defaultMaterialAssetId.IsValid(), "Default material asset is invalid. Verify the asset %s exists.", m_context->GetData()->DefaultMaterialPath);
if (m_context->GetData()->m_assetsToLoad.emplace(defaultMaterialAssetId).second)
{
data->m_defaultMaterialAsset.Create(defaultMaterialAssetId);
data->m_defaultMaterialAsset.QueueLoad();
}
m_context->SetStep(Step::FindThumbnailToRender);
}
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -1,37 +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
*
*/
#pragma once
#include <Thumbnails/Rendering/ThumbnailRendererSteps/ThumbnailRendererStep.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
//! InitializeStep sets up RPI system and scene and prepares it for rendering thumbnail entities
//! This step is only called once when CommonThumbnailRenderer begins rendering its first thumbnail
class InitializeStep
: public ThumbnailRendererStep
{
public:
InitializeStep(ThumbnailRendererContext* context);
void Start() override;
private:
static constexpr float AspectRatio = 1.0f;
static constexpr float NearDist = 0.1f;
static constexpr float FarDist = 100.0f;
};
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -1,57 +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 <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/Scene.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <Thumbnails/Rendering/ThumbnailRendererContext.h>
#include <Thumbnails/Rendering/ThumbnailRendererData.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
ReleaseResourcesStep::ReleaseResourcesStep(ThumbnailRendererContext* context)
: ThumbnailRendererStep(context)
{
}
void ReleaseResourcesStep::Start()
{
auto data = m_context->GetData();
data->m_defaultMaterialAsset.Release();
data->m_defaultModelAsset.Release();
data->m_materialAsset.Release();
data->m_modelAsset.Release();
data->m_lightingPresetAsset.Release();
if (data->m_modelEntity)
{
AzFramework::EntityContextRequestBus::Event(data->m_entityContext->GetContextId(),
&AzFramework::EntityContextRequestBus::Events::DestroyEntity, data->m_modelEntity);
data->m_modelEntity = nullptr;
}
data->m_scene->Deactivate();
data->m_scene->RemoveRenderPipeline(data->m_renderPipeline->GetId());
RPI::RPISystemInterface::Get()->UnregisterScene(data->m_scene);
data->m_frameworkScene->UnsetSubsystem(data->m_scene);
data->m_frameworkScene->UnsetSubsystem(data->m_entityContext.get());
data->m_scene = nullptr;
data->m_frameworkScene = nullptr;
data->m_renderPipeline = nullptr;
}
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -1,30 +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
*
*/
#pragma once
#include <Thumbnails/Rendering/ThumbnailRendererSteps/ThumbnailRendererStep.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
class ReleaseResourcesStep
: public ThumbnailRendererStep
{
public:
ReleaseResourcesStep(ThumbnailRendererContext* context);
void Start() override;
};
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -1,37 +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
*
*/
#pragma once
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
class ThumbnailRendererContext;
//! ThumbnailRendererStep decouples CommonThumbnailRenderer logic into easy-to-understand and debug pieces
class ThumbnailRendererStep
{
public:
explicit ThumbnailRendererStep(ThumbnailRendererContext* context) : m_context(context) {}
virtual ~ThumbnailRendererStep() = default;
//! Start is called when step begins execution
virtual void Start() {}
//! Stop is called when step ends execution
virtual void Stop() {}
protected:
ThumbnailRendererContext* m_context;
};
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -1,101 +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 "Thumbnails/ThumbnailerBus.h"
#include <Thumbnails/Rendering/ThumbnailRendererData.h>
#include <Thumbnails/Rendering/ThumbnailRendererContext.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/WaitForAssetsToLoadStep.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
WaitForAssetsToLoadStep::WaitForAssetsToLoadStep(ThumbnailRendererContext* context)
: ThumbnailRendererStep(context)
{
}
void WaitForAssetsToLoadStep::Start()
{
LoadNextAsset();
}
void WaitForAssetsToLoadStep::Stop()
{
Data::AssetBus::Handler::BusDisconnect();
TickBus::Handler::BusDisconnect();
m_context->GetData()->m_assetsToLoad.clear();
}
void WaitForAssetsToLoadStep::LoadNextAsset()
{
if (m_context->GetData()->m_assetsToLoad.empty())
{
// When all assets are loaded, render the thumbnail itself
m_context->SetStep(Step::Capture);
}
else
{
// Pick the the next asset and wait until its ready
const auto assetIdIt = m_context->GetData()->m_assetsToLoad.begin();
m_context->GetData()->m_assetsToLoad.erase(assetIdIt);
m_assetId = *assetIdIt;
Data::AssetBus::Handler::BusConnect(m_assetId);
// If asset is already loaded, then AssetEvents will call OnAssetReady instantly and we don't need to wait this time
if (Data::AssetBus::Handler::BusIsConnected())
{
TickBus::Handler::BusConnect();
m_timeRemainingS = TimeOutS;
}
}
}
void WaitForAssetsToLoadStep::OnAssetReady([[maybe_unused]] Data::Asset<Data::AssetData> asset)
{
Data::AssetBus::Handler::BusDisconnect();
LoadNextAsset();
}
void WaitForAssetsToLoadStep::OnAssetError([[maybe_unused]] Data::Asset<Data::AssetData> asset)
{
Data::AssetBus::Handler::BusDisconnect();
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event(
m_context->GetData()->m_thumbnailKeyRendered,
&AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender);
m_context->SetStep(Step::FindThumbnailToRender);
}
void WaitForAssetsToLoadStep::OnAssetCanceled([[maybe_unused]] Data::AssetId assetId)
{
Data::AssetBus::Handler::BusDisconnect();
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event(
m_context->GetData()->m_thumbnailKeyRendered,
&AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender);
m_context->SetStep(Step::FindThumbnailToRender);
}
void WaitForAssetsToLoadStep::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
m_timeRemainingS -= deltaTime;
if (m_timeRemainingS < 0)
{
auto assetIdStr = m_assetId.ToString<AZStd::string>();
AZ_Warning("CommonThumbnailRenderer", false, "Timed out waiting for asset %s to load.", assetIdStr.c_str());
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Event(
m_context->GetData()->m_thumbnailKeyRendered,
&AzToolsFramework::Thumbnailer::ThumbnailerRendererNotifications::ThumbnailFailedToRender);
m_context->SetStep(Step::FindThumbnailToRender);
}
}
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -1,50 +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
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <Thumbnails/Rendering/ThumbnailRendererSteps/ThumbnailRendererStep.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
//! WaitForAssetsToLoadStep pauses further rendering until all assets used for rendering a thumbnail have been loaded
class WaitForAssetsToLoadStep
: public ThumbnailRendererStep
, private Data::AssetBus::Handler
, private TickBus::Handler
{
public:
WaitForAssetsToLoadStep(ThumbnailRendererContext* context);
void Start() override;
void Stop() override;
private:
void LoadNextAsset();
// AZ::Data::AssetBus::Handler
void OnAssetReady(Data::Asset<Data::AssetData> asset) override;
void OnAssetError(Data::Asset<Data::AssetData> asset) override;
void OnAssetCanceled(Data::AssetId assetId) override;
//! AZ::TickBus::Handler interface overrides...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
static constexpr float TimeOutS = 3.0f;
Data::AssetId m_assetId;
float m_timeRemainingS = 0;
};
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -7,9 +7,9 @@
#
set(FILES
Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h
Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h
Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h
Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h
Source/Module.cpp
Source/Animation/EditorAttachmentComponent.h
Source/Animation/EditorAttachmentComponent.cpp
@@ -45,8 +45,6 @@ set(FILES
Source/Material/EditorMaterialSystemComponent.h
Source/Material/MaterialBrowserInteractions.h
Source/Material/MaterialBrowserInteractions.cpp
Source/Material/MaterialThumbnail.cpp
Source/Material/MaterialThumbnail.h
Source/Mesh/EditorMeshComponent.h
Source/Mesh/EditorMeshComponent.cpp
Source/Mesh/EditorMeshStats.h
@@ -55,8 +53,6 @@ set(FILES
Source/Mesh/EditorMeshSystemComponent.h
Source/Mesh/EditorMeshStatsSerializer.cpp
Source/Mesh/EditorMeshStatsSerializer.h
Source/Mesh/MeshThumbnail.h
Source/Mesh/MeshThumbnail.cpp
Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h
Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp
Source/PostProcess/EditorPostFxLayerComponent.cpp
@@ -95,28 +91,19 @@ set(FILES
Source/SkyBox/EditorHDRiSkyboxComponent.h
Source/SkyBox/EditorPhysicalSkyComponent.cpp
Source/SkyBox/EditorPhysicalSkyComponent.h
Source/Thumbnails/ThumbnailUtils.h
Source/Thumbnails/ThumbnailUtils.cpp
Source/Thumbnails/Preview/CommonPreviewer.cpp
Source/Thumbnails/Preview/CommonPreviewer.h
Source/Thumbnails/Preview/CommonPreviewer.ui
Source/Thumbnails/Preview/CommonPreviewerFactory.cpp
Source/Thumbnails/Preview/CommonPreviewerFactory.h
Source/Thumbnails/Rendering/CommonThumbnailRenderer.cpp
Source/Thumbnails/Rendering/CommonThumbnailRenderer.h
Source/Thumbnails/Rendering/ThumbnailRendererData.h
Source/Thumbnails/Rendering/ThumbnailRendererContext.h
Source/Thumbnails/Rendering/ThumbnailRendererSteps/ThumbnailRendererStep.h
Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp
Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.h
Source/Thumbnails/Rendering/ThumbnailRendererSteps/FindThumbnailToRenderStep.cpp
Source/Thumbnails/Rendering/ThumbnailRendererSteps/FindThumbnailToRenderStep.h
Source/Thumbnails/Rendering/ThumbnailRendererSteps/WaitForAssetsToLoadStep.cpp
Source/Thumbnails/Rendering/ThumbnailRendererSteps/WaitForAssetsToLoadStep.h
Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.cpp
Source/Thumbnails/Rendering/ThumbnailRendererSteps/CaptureStep.h
Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.cpp
Source/Thumbnails/Rendering/ThumbnailRendererSteps/ReleaseResourcesStep.h
Source/SharedPreview/SharedPreviewer.cpp
Source/SharedPreview/SharedPreviewer.h
Source/SharedPreview/SharedPreviewer.ui
Source/SharedPreview/SharedPreviewerFactory.cpp
Source/SharedPreview/SharedPreviewerFactory.h
Source/SharedPreview/SharedPreviewContent.cpp
Source/SharedPreview/SharedPreviewContent.h
Source/SharedPreview/SharedPreviewUtils.cpp
Source/SharedPreview/SharedPreviewUtils.h
Source/SharedPreview/SharedThumbnail.cpp
Source/SharedPreview/SharedThumbnail.h
Source/SharedPreview/SharedThumbnailRenderer.cpp
Source/SharedPreview/SharedThumbnailRenderer.h
Source/Scripting/EditorEntityReferenceComponent.cpp
Source/Scripting/EditorEntityReferenceComponent.h
Source/SurfaceData/EditorSurfaceDataMeshComponent.cpp
+3 -3
View File
@@ -344,9 +344,9 @@ namespace Blast
void UpdateMassProperties(
[[maybe_unused]] AzPhysics::MassComputeFlags flags,
[[maybe_unused]] const AZ::Vector3* centerOfMassOffsetOverride,
[[maybe_unused]] const AZ::Matrix3x3* inertiaTensorOverride,
[[maybe_unused]] const float* massOverride) override
[[maybe_unused]] const AZ::Vector3& centerOfMassOffsetOverride,
[[maybe_unused]] const AZ::Matrix3x3& inertiaTensorOverride,
[[maybe_unused]] const float massOverride) override
{
}
@@ -95,7 +95,9 @@ def generate_assetinfo_product(request):
outputFilename = os.path.join(request.tempDirPath, assetinfoFilename)
# the only rule in it is to run this file again as a scene processor
currentScript = pathlib.Path(__file__).resolve()
currentScript = str(pathlib.Path(__file__).resolve())
currentScript = currentScript.replace('\\', '/').lower()
currentScript = currentScript.replace('blast_asset_builder.py', 'blast_chunk_processor.py')
aDict = {"values": [{"$type": "ScriptProcessorRule", "scriptFilename": f"{currentScript}"}]}
jsonString = json.dumps(aDict)
jsonFile = open(outputFilename, "w")
@@ -167,124 +169,3 @@ try:
pythonAssetBuilderHandler = register_asset_builder()
except:
pythonAssetBuilderHandler = None
#
# SceneAPI Processor
#
blastChunksAssetType = azlmbr.math.Uuid_CreateString('{993F0B0F-37D9-48C6-9CC2-E27D3F3E343E}', 0)
def export_chunk_asset(scene, outputDirectory, platformIdentifier, productList):
import azlmbr.scene
import azlmbr.object
import azlmbr.paths
import json, os
jsonFilename = os.path.basename(scene.sourceFilename)
jsonFilename = os.path.join(outputDirectory, jsonFilename + '.blast_chunks')
# prepare output folder
basePath, _ = os.path.split(jsonFilename)
outputPath = os.path.join(outputDirectory, basePath)
if not os.path.exists(outputPath):
os.makedirs(outputPath, False)
# write out a JSON file with the chunk file info
with open(jsonFilename, "w") as jsonFile:
jsonFile.write(scene.manifest.ExportToJson())
exportProduct = azlmbr.scene.ExportProduct()
exportProduct.filename = jsonFilename
exportProduct.sourceId = scene.sourceGuid
exportProduct.assetType = blastChunksAssetType
exportProduct.subId = 101
exportProductList = azlmbr.scene.ExportProductList()
exportProductList.AddProduct(exportProduct)
return exportProductList
def on_prepare_for_export(args):
try:
scene = args[0] # azlmbr.scene.Scene
outputDirectory = args[1] # string
platformIdentifier = args[2] # string
productList = args[3] # azlmbr.scene.ExportProductList
return export_chunk_asset(scene, outputDirectory, platformIdentifier, productList)
except:
log_exception_traceback()
def get_mesh_node_names(sceneGraph):
import azlmbr.scene as sceneApi
import azlmbr.scene.graph
from scene_api import scene_data as sceneData
meshDataList = []
node = sceneGraph.get_root()
children = []
while node.IsValid():
# store children to process after siblings
if sceneGraph.has_node_child(node):
children.append(sceneGraph.get_node_child(node))
# store any node that has mesh data content
nodeContent = sceneGraph.get_node_content(node)
if nodeContent is not None and nodeContent.CastWithTypeName('MeshData'):
if sceneGraph.is_node_end_point(node) is False:
nodeName = sceneData.SceneGraphName(sceneGraph.get_node_name(node))
nodePath = nodeName.get_path()
if (len(nodeName.get_path())):
meshDataList.append(sceneData.SceneGraphName(sceneGraph.get_node_name(node)))
# advance to next node
if sceneGraph.has_node_sibling(node):
node = sceneGraph.get_node_sibling(node)
elif children:
node = children.pop()
else:
node = azlmbr.scene.graph.NodeIndex()
return meshDataList
def update_manifest(scene):
import uuid, os
import azlmbr.scene as sceneApi
import azlmbr.scene.graph
from scene_api import scene_data as sceneData
graph = sceneData.SceneGraph(scene.graph)
meshNameList = get_mesh_node_names(graph)
sceneManifest = sceneData.SceneManifest()
sourceFilenameOnly = os.path.basename(scene.sourceFilename)
sourceFilenameOnly = sourceFilenameOnly.replace('.','_')
for activeMeshIndex in range(len(meshNameList)):
chunkName = meshNameList[activeMeshIndex]
chunkPath = chunkName.get_path()
meshGroupName = '{}_{}'.format(sourceFilenameOnly, chunkName.get_name())
meshGroup = sceneManifest.add_mesh_group(meshGroupName)
meshGroup['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, sourceFilenameOnly + chunkPath)) + '}'
sceneManifest.mesh_group_select_node(meshGroup, chunkPath)
return sceneManifest.export()
sceneJobHandler = None
def on_update_manifest(args):
try:
scene = args[0]
return update_manifest(scene)
except:
global sceneJobHandler
sceneJobHandler = None
log_exception_traceback()
# try to create SceneAPI handler for processing
try:
import azlmbr.scene as sceneApi
if (sceneJobHandler == None):
sceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler()
sceneJobHandler.connect()
sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest)
sceneJobHandler.add_callback('OnPrepareForExport', on_prepare_for_export)
except:
sceneJobHandler = None
@@ -0,0 +1,141 @@
"""
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
"""
"""
This a Python Asset Builder script examines each .blast file to see if an
associated .fbx file needs to be processed by exporting all of its chunks
into a scene manifest
This is also a SceneAPI script that executes from a foo.fbx.assetinfo scene
manifest that writes out asset chunk data for .blast files
"""
import os, traceback, binascii, sys, json, pathlib
import azlmbr.math
import azlmbr.asset
import azlmbr.asset.entity
import azlmbr.asset.builder
import azlmbr.bus
#
# SceneAPI Processor
#
blastChunksAssetType = azlmbr.math.Uuid_CreateString('{993F0B0F-37D9-48C6-9CC2-E27D3F3E343E}', 0)
def export_chunk_asset(scene, outputDirectory, platformIdentifier, productList):
import azlmbr.scene
import azlmbr.object
import azlmbr.paths
import json, os
jsonFilename = os.path.basename(scene.sourceFilename)
jsonFilename = os.path.join(outputDirectory, jsonFilename + '.blast_chunks')
# prepare output folder
basePath, _ = os.path.split(jsonFilename)
outputPath = os.path.join(outputDirectory, basePath)
if not os.path.exists(outputPath):
os.makedirs(outputPath, False)
# write out a JSON file with the chunk file info
with open(jsonFilename, "w") as jsonFile:
jsonFile.write(scene.manifest.ExportToJson())
exportProduct = azlmbr.scene.ExportProduct()
exportProduct.filename = jsonFilename
exportProduct.sourceId = scene.sourceGuid
exportProduct.assetType = blastChunksAssetType
exportProduct.subId = 101
exportProductList = azlmbr.scene.ExportProductList()
exportProductList.AddProduct(exportProduct)
return exportProductList
def on_prepare_for_export(args):
try:
scene = args[0] # azlmbr.scene.Scene
outputDirectory = args[1] # string
platformIdentifier = args[2] # string
productList = args[3] # azlmbr.scene.ExportProductList
return export_chunk_asset(scene, outputDirectory, platformIdentifier, productList)
except:
log_exception_traceback()
def get_mesh_node_names(sceneGraph):
import azlmbr.scene as sceneApi
import azlmbr.scene.graph
from scene_api import scene_data as sceneData
meshDataList = []
node = sceneGraph.get_root()
children = []
while node.IsValid():
# store children to process after siblings
if sceneGraph.has_node_child(node):
children.append(sceneGraph.get_node_child(node))
# store any node that has mesh data content
nodeContent = sceneGraph.get_node_content(node)
if nodeContent is not None and nodeContent.CastWithTypeName('MeshData'):
if sceneGraph.is_node_end_point(node) is False:
nodeName = sceneData.SceneGraphName(sceneGraph.get_node_name(node))
nodePath = nodeName.get_path()
if (len(nodeName.get_path())):
meshDataList.append(sceneData.SceneGraphName(sceneGraph.get_node_name(node)))
# advance to next node
if sceneGraph.has_node_sibling(node):
node = sceneGraph.get_node_sibling(node)
elif children:
node = children.pop()
else:
node = azlmbr.scene.graph.NodeIndex()
return meshDataList
def update_manifest(scene):
import uuid, os
import azlmbr.scene as sceneApi
import azlmbr.scene.graph
from scene_api import scene_data as sceneData
graph = sceneData.SceneGraph(scene.graph)
meshNameList = get_mesh_node_names(graph)
sceneManifest = sceneData.SceneManifest()
sourceFilenameOnly = os.path.basename(scene.sourceFilename)
sourceFilenameOnly = sourceFilenameOnly.replace('.','_')
for activeMeshIndex in range(len(meshNameList)):
chunkName = meshNameList[activeMeshIndex]
chunkPath = chunkName.get_path()
meshGroupName = '{}_{}'.format(sourceFilenameOnly, chunkName.get_name())
meshGroup = sceneManifest.add_mesh_group(meshGroupName)
meshGroup['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, sourceFilenameOnly + chunkPath)) + '}'
sceneManifest.mesh_group_select_node(meshGroup, chunkPath)
return sceneManifest.export()
sceneJobHandler = None
def on_update_manifest(args):
try:
scene = args[0]
return update_manifest(scene)
except:
global sceneJobHandler
sceneJobHandler = None
log_exception_traceback()
# try to create SceneAPI handler for processing
try:
import azlmbr.scene as sceneApi
if (sceneJobHandler == None):
sceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler()
sceneJobHandler.connect()
sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest)
sceneJobHandler.add_callback('OnPrepareForExport', on_prepare_for_export)
except:
sceneJobHandler = None
@@ -27,7 +27,7 @@ namespace EMotionFX
{}
};
class INTEG_PoseComparisonFixture
class PoseComparisonFixture
: public SystemComponentFixture
, public ::testing::WithParamInterface<PoseComparisonFixtureParams>
{
@@ -47,8 +47,8 @@ namespace EMotionFX
// This fixture exists to separate the tests that test the pose comparsion
// functionality from the tests that use the pose comparison functionality
// (even though it doesn't use the recording)
class INTEG_TestPoseComparisonFixture
: public INTEG_PoseComparisonFixture
class TestPoseComparisonFixture
: public PoseComparisonFixture
{
};
}; // namespace EMotionFX
@@ -154,14 +154,14 @@ namespace EMotionFX
return MakeMatcher(new KeyTrackMatcher<T>(expected, nodeName));
}
void INTEG_PoseComparisonFixture::SetUp()
void PoseComparisonFixture::SetUp()
{
SystemComponentFixture::SetUp();
LoadAssets();
}
void INTEG_PoseComparisonFixture::TearDown()
void PoseComparisonFixture::TearDown()
{
m_actorInstance->Destroy();
@@ -176,7 +176,7 @@ namespace EMotionFX
SystemComponentFixture::TearDown();
}
void INTEG_PoseComparisonFixture::LoadAssets()
void PoseComparisonFixture::LoadAssets()
{
const AZStd::string actorPath = ResolvePath(GetParam().m_actorFile);
m_actor = EMotionFX::GetImporter().LoadActor(actorPath);
@@ -195,7 +195,7 @@ namespace EMotionFX
m_actorInstance->SetAnimGraphInstance(AnimGraphInstance::Create(m_animGraph, m_actorInstance, m_motionSet));
}
TEST_P(INTEG_PoseComparisonFixture, Integ_TestPoses)
TEST_P(PoseComparisonFixture, TestPoses)
{
const AZStd::string recordingPath = ResolvePath(GetParam().m_recordingFile);
Recorder* recording = EMotionFX::Recorder::LoadFromFile(recordingPath.c_str());
@@ -231,7 +231,7 @@ namespace EMotionFX
recording->Destroy();
}
TEST_P(INTEG_TestPoseComparisonFixture, Integ_TestRecording)
TEST_P(TestPoseComparisonFixture, TestRecording)
{
// Make one recording, 10 seconds at 60 fps
Recorder::RecordSettings settings;
@@ -294,30 +294,30 @@ namespace EMotionFX
recording->Destroy();
}
INSTANTIATE_TEST_CASE_P(Integ_TestPoses, INTEG_PoseComparisonFixture,
INSTANTIATE_TEST_CASE_P(DISABLED_TestPoses, PoseComparisonFixture,
::testing::Values(
PoseComparisonFixtureParams (
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.motionset",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.emfxrecording"
"@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor",
"@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph",
"@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.motionset",
"@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.emfxrecording"
),
PoseComparisonFixtureParams (
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.actor",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.animgraph",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.motionset",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.emfxrecording"
"@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.actor",
"@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.animgraph",
"@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.motionset",
"@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Pendulum/pendulum.emfxrecording"
)
)
);
INSTANTIATE_TEST_CASE_P(Integ_TestPoseComparison, INTEG_TestPoseComparisonFixture,
INSTANTIATE_TEST_CASE_P(DISABLED_TestPoseComparison, TestPoseComparisonFixture,
::testing::Values(
PoseComparisonFixtureParams (
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.motionset",
"@products@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.emfxrecording"
"@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.actor",
"@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.animgraph",
"@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.motionset",
"@exefolder@/Test.Assets/Gems/EMotionFX/Code/Tests/TestAssets/Rin/rin.emfxrecording"
)
)
);
@@ -17,10 +17,16 @@
#include <Source/PythonSymbolsBus.h>
#include <pybind11/embed.h>
#include <pybind11/pybind11.h>
#include <pybind11/eval.h>
#include <AzCore/PlatformDef.h>
#include <AzCore/JSON/rapidjson.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/RTTI/AttributeReader.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/JsonSerializationSettings.h>
#include <AzCore/Serialization/Json/JsonUtils.h>
namespace EditorPythonBindings
{
@@ -571,6 +577,37 @@ namespace EditorPythonBindings
return false;
}
pybind11::object PythonProxyObject::ToJson()
{
rapidjson::Document document;
AZ::JsonSerializerSettings settings;
settings.m_keepDefaults = true;
auto resultCode =
AZ::JsonSerialization::Store(document, document.GetAllocator(), m_wrappedObject.m_address, nullptr, m_wrappedObject.m_typeId, settings);
if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
{
AZ_Error("PythonProxyObject", false, "Failed to serialize to json");
return pybind11::cast<pybind11::none>(Py_None);
}
AZStd::string jsonString;
AZ::Outcome<void, AZStd::string> outcome = AZ::JsonSerializationUtils::WriteJsonString(document, jsonString);
if (!outcome.IsSuccess())
{
AZ_Error("PythonProxyObject", false, "Failed to write json string: %s", outcome.GetError().c_str());
return pybind11::cast<pybind11::none>(Py_None);
}
jsonString.erase(AZStd::remove(jsonString.begin(), jsonString.end(), '\n'), jsonString.end());
auto pythonCode = AZStd::string::format(
R"PYTHON(exec("import json") or json.loads("""%s"""))PYTHON", jsonString.c_str());
return pybind11::eval(pythonCode.c_str());
}
bool PythonProxyObject::DoComparisonEvaluation(pybind11::object pythonOther, Comparison comparison)
{
bool invertLogic = false;
@@ -912,6 +949,7 @@ namespace EditorPythonBindings
.def("set_property", &PythonProxyObject::SetPropertyValue)
.def("get_property", &PythonProxyObject::GetPropertyValue)
.def("invoke", &PythonProxyObject::Invoke)
.def("to_json", &PythonProxyObject::ToJson)
.def(Operator::s_isEqual, [](PythonProxyObject& self, pybind11::object rhs)
{
return self.DoEqualityEvaluation(rhs);
@@ -58,6 +58,8 @@ namespace EditorPythonBindings
//! Performs an equality operation to compare this object with another object
bool DoEqualityEvaluation(pybind11::object pythonOther);
pybind11::object ToJson();
//! Perform a comparison of a Python operator
enum class Comparison
{
@@ -7,52 +7,50 @@
*/
#include <AzTest/AzTest.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/parallel/atomic.h>
#include <AzCore/std/parallel/condition_variable.h>
#include "HttpRequestManager.h"
class Integ_HttpTest
: public ::testing::Test
class HttpTest
: public UnitTest::ScopedAllocatorSetupFixture
{
public:
HttpRequestor::ManagerPtr m_httpRequestManager;
// to wait for test to complete
AZStd::mutex m_requestMutex;
AZStd::condition_variable m_requestConditionVar;
AZStd::string resultData;
AZStd::atomic<Aws::Http::HttpResponseCode> resultCode;
Integ_HttpTest()
{
m_httpRequestManager = AZStd::make_shared<HttpRequestor::Manager>();
resultCode = Aws::Http::HttpResponseCode::REQUEST_NOT_MADE;
resultData = "{}";
AZStd::unique_lock<AZStd::mutex> lock(m_requestMutex);
m_requestConditionVar.wait_for(lock, AZStd::chrono::milliseconds(10));
}
virtual ~Integ_HttpTest()
{
m_httpRequestManager.reset();
}
};
TEST_F(Integ_HttpTest, HttpRequesterTest)
TEST_F(HttpTest, DISABLED_HttpRequesterTest)
{
m_httpRequestManager->AddTextRequest(HttpRequestor::TextParameters("https://httpbin.org/ip", Aws::Http::HttpMethod::HTTP_GET, [this](const AZStd::string & data, Aws::Http::HttpResponseCode code)
{
resultData = data;
resultCode = code;
m_requestConditionVar.notify_all();
}));
HttpRequestor::Manager httpRequestManager;
AZStd::unique_lock<AZStd::mutex> lock(m_requestMutex);
m_requestConditionVar.wait_for(lock, AZStd::chrono::milliseconds(5000));
// to wait for test to complete
AZStd::mutex requestMutex;
AZStd::condition_variable requestConditionVar;
AZStd::string resultData = {};
AZStd::atomic<Aws::Http::HttpResponseCode> resultCode = Aws::Http::HttpResponseCode::REQUEST_NOT_MADE;
{
AZStd::unique_lock<AZStd::mutex> lock(requestMutex);
requestConditionVar.wait_for(lock, AZStd::chrono::milliseconds(10));
}
httpRequestManager.AddTextRequest(
HttpRequestor::TextParameters("https://httpbin.org/ip",
Aws::Http::HttpMethod::HTTP_GET,
[&resultData, &resultCode, &requestConditionVar](const AZStd::string& data, Aws::Http::HttpResponseCode code)
{
resultData = data;
resultCode = code;
requestConditionVar.notify_all();
}
)
);
{
AZStd::unique_lock<AZStd::mutex> lock(requestMutex);
requestConditionVar.wait_for(lock, AZStd::chrono::milliseconds(5000));
}
EXPECT_NE(Aws::Http::HttpResponseCode::REQUEST_NOT_MADE, resultCode);
}
@@ -26,12 +26,12 @@
namespace UnitTest
{
class Integ_BundlingSystemComponentFixture :
class BundlingSystemComponentFixture :
public ::testing::Test
{
public:
Integ_BundlingSystemComponentFixture() = default;
BundlingSystemComponentFixture() = default;
bool TestAsset(const char* assetPath)
{
@@ -59,7 +59,7 @@ namespace UnitTest
}
};
TEST_F(Integ_BundlingSystemComponentFixture, HasBundle_LoadBundles_Success)
TEST_F(BundlingSystemComponentFixture, DISABLED_HasBundle_LoadBundles_Success)
{
// This asset lives only within LmbrCentral/Assets/Test/Bundle/staticdata.pak which is copied to our
// cache as test/bundle/staticdata.pak and should be loaded below
@@ -72,7 +72,7 @@ namespace UnitTest
EXPECT_FALSE(TestAsset(testAssetPath));
}
TEST_F(Integ_BundlingSystemComponentFixture, HasBundle_LoadBundlesCatalogChecks_Success)
TEST_F(BundlingSystemComponentFixture, DISABLED_HasBundle_LoadBundlesCatalogChecks_Success)
{
// This asset lives only within LmbrCentral/Assets/Test/Bundle/staticdata.pak which is copied to our
// cache as test/bundle/staticdata.pak and should be loaded below
@@ -92,7 +92,7 @@ namespace UnitTest
EXPECT_FALSE(TestAsset(noCatalogAsset));
}
TEST_F(Integ_BundlingSystemComponentFixture, BundleSystemComponent_SingleUnloadCheckCatalog_Success)
TEST_F(BundlingSystemComponentFixture, DISABLED_BundleSystemComponent_SingleUnloadCheckCatalog_Success)
{
// This asset lives only within LmbrCentral/Assets/Test/Bundle/staticdata.pak which is copied to our
// cache as test/bundle/staticdata.pak and should be loaded below
@@ -132,7 +132,7 @@ namespace UnitTest
EXPECT_FALSE(TestAssetId(testDDSAsset));
}
TEST_F(Integ_BundlingSystemComponentFixture, BundleSystemComponent_SingleLoadAndBundleMode_Success)
TEST_F(BundlingSystemComponentFixture, DISABLED_BundleSystemComponent_SingleLoadAndBundleMode_Success)
{
// This asset lives only within LmbrCentral/Assets/Test/Bundle/staticdata.pak which is copied to our
// cache as test/bundle/staticdata.pak and should be loaded below
@@ -157,7 +157,7 @@ namespace UnitTest
EXPECT_FALSE(TestAssetId(testMTLAsset));
}
TEST_F(Integ_BundlingSystemComponentFixture, BundleSystemComponent_OpenClosePackCount_Match)
TEST_F(BundlingSystemComponentFixture, DISABLED_BundleSystemComponent_OpenClosePackCount_Match)
{
// This asset lives only within LmbrCentral/Assets/Test/Bundle/staticdata.pak which is copied to our
// cache as test/bundle/staticdata.pak and should be loaded below
@@ -198,7 +198,7 @@ namespace UnitTest
EXPECT_EQ(bundleCount, 0);
}
TEST_F(Integ_BundlingSystemComponentFixture, BundleSystemComponent_SplitPakTestWithAsset_Success)
TEST_F(BundlingSystemComponentFixture, DISABLED_BundleSystemComponent_SplitPakTestWithAsset_Success)
{
// This asset lives only within LmbrCentral/Assets/Test/SplitBundleTest/splitbundle__1.pak which is a dependent bundle of splitbundle.pak
const char testDDSAsset_split[] = "textures/milestone2/am_floor_tile_ddna_test.dds.7";
@@ -228,7 +228,7 @@ namespace UnitTest
}
// Verify that our bundles using catalogs of the same name work properly
TEST_F(Integ_BundlingSystemComponentFixture, BundleSystemComponent_SharedCatalogName_Success)
TEST_F(BundlingSystemComponentFixture, DISABLED_BundleSystemComponent_SharedCatalogName_Success)
{
// This bundle was built for PC but is generic and the test should work fine on other platforms
// gamepropertioessmall_pc.pak has a smaller version of the gameproperties csv
+3 -3
View File
@@ -148,16 +148,16 @@ namespace PhysX
using VisibilityFunc = bool(*)();
editContext->Class<Collider>(
"PhysX Collider Debug Draw", "Manages global and per-collider debug draw settings and logic")
"PhysX Collider Debug Draw", "Global and per-collider debug draw preferences.")
->DataElement(AZ::Edit::UIHandlers::CheckBox, &Collider::m_locallyEnabled, "Draw collider",
"Shows the geometry for the collider in the viewport")
"Display collider geometry in the viewport.")
->Attribute(AZ::Edit::Attributes::CheckboxTooltip,
"If set, the geometry of this collider is visible in the viewport. 'Draw Helpers' needs to be enabled to use.")
->Attribute(AZ::Edit::Attributes::Visibility,
VisibilityFunc{ []() { return IsGlobalColliderDebugCheck(GlobalCollisionDebugState::Manual); } })
->Attribute(AZ::Edit::Attributes::ReadOnly, &IsDrawColliderReadOnly)
->DataElement(AZ::Edit::UIHandlers::Button, &Collider::m_globalButtonState, "Draw collider",
"Shows the geometry for the collider in the viewport")
"Display collider geometry in the viewport.")
->Attribute(AZ::Edit::Attributes::ButtonText, "Global override")
->Attribute(AZ::Edit::Attributes::ButtonTooltip,
"A global setting is overriding this property (to disable the override, "
@@ -51,23 +51,27 @@ namespace PhysX
if (auto* editContext = serializeContext->GetEditContext())
{
editContext->Class<PhysX::EditorJointLimitConfig>(
"Editor Joint Limit Config Base", "Base joint limit parameters")
"Editor Joint Limit Config Base", "Base joint limit parameters.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(0, &PhysX::EditorJointLimitConfig::m_isLimited, "Limit", "True if the motion about the unconstrained axes of this joint are limited")
->DataElement(0, &PhysX::EditorJointLimitConfig::m_isLimited, "Limit",
"When active, the joint's degrees of freedom are limited.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorJointLimitConfig::IsInComponentMode)
->DataElement(0, &PhysX::EditorJointLimitConfig::m_isSoftLimit, "Soft limit", "True if the joint is allowed to rotate beyond limits and spring back")
->DataElement(0, &PhysX::EditorJointLimitConfig::m_isSoftLimit, "Soft limit",
"When active, motion beyond the joint limit with a spring-like return is allowed.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitConfig::m_isLimited)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorJointLimitConfig::IsInComponentMode)
->DataElement(0, &PhysX::EditorJointLimitConfig::m_damping, "Damping", "The damping strength of the drive, the force proportional to the velocity error")
->DataElement(0, &PhysX::EditorJointLimitConfig::m_damping, "Damping",
"Dissipation of energy and reduction in spring oscillations when outside the joint limit.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitConfig::IsSoftLimited)
->Attribute(AZ::Edit::Attributes::Max, s_springMax)
->Attribute(AZ::Edit::Attributes::Min, s_springMin)
->DataElement(0, &PhysX::EditorJointLimitConfig::m_stiffness, "Stiffness", "The spring strength of the drive, the force proportional to the position error")
->DataElement(0, &PhysX::EditorJointLimitConfig::m_stiffness, "Stiffness",
"The spring's drive relative to the position of the follower when outside the joint limit.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitConfig::IsSoftLimited)
->Attribute(AZ::Edit::Attributes::Max, s_springMax)
->Attribute(AZ::Edit::Attributes::Min, s_springMin)
@@ -115,18 +119,20 @@ namespace PhysX
if (auto* editContext = serializeContext->GetEditContext())
{
editContext->Class<PhysX::EditorJointLimitPairConfig>(
"Angular Limit", "Rotation limitation")
"Angular Limit", "Rotation limitation.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(0, &PhysX::EditorJointLimitPairConfig::m_standardLimitConfig
, "Standard limit configuration"
, "Common limit parameters to all joint types")
->DataElement(0, &PhysX::EditorJointLimitPairConfig::m_limitPositive, "Positive angular limit", "Positive rotation angle")
, "Common limit parameters to all joint types.")
->DataElement(0, &PhysX::EditorJointLimitPairConfig::m_limitPositive, "Positive angular limit",
"Positive rotation angle.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitPairConfig::IsLimited)
->Attribute(AZ::Edit::Attributes::Max, s_angleMax)
->Attribute(AZ::Edit::Attributes::Min, s_angleMin)
->DataElement(0, &PhysX::EditorJointLimitPairConfig::m_limitNegative, "Negative angular limit", "Negative rotation angle")
->DataElement(0, &PhysX::EditorJointLimitPairConfig::m_limitNegative, "Negative angular limit",
"Negative rotation angle.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitPairConfig::IsLimited)
->Attribute(AZ::Edit::Attributes::Max, s_angleMin)
->Attribute(AZ::Edit::Attributes::Min, -s_angleMax)
@@ -164,18 +170,20 @@ namespace PhysX
if (auto* editContext = serializeContext->GetEditContext())
{
editContext->Class<PhysX::EditorJointLimitConeConfig>(
"Angular Limit", "Rotation limitation")
"Angular Limit", "Rotation limitation.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(0, &PhysX::EditorJointLimitConeConfig::m_standardLimitConfig
, "Standard limit configuration"
, "Common limit parameters to all joint types")
->DataElement(0, &PhysX::EditorJointLimitConeConfig::m_limitY, "Y axis angular limit", "Limit for swing angle about Y axis")
, "Common limit parameters to all joint types.")
->DataElement(0, &PhysX::EditorJointLimitConeConfig::m_limitY, "Y axis angular limit",
"Limit for swing angle about Y axis.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitConeConfig::IsLimited)
->Attribute(AZ::Edit::Attributes::Max, s_angleMax)
->Attribute(AZ::Edit::Attributes::Min, s_angleMin)
->DataElement(0, &PhysX::EditorJointLimitConeConfig::m_limitZ, "Z axis angular limit", "Limit for swing angle about Z axis")
->DataElement(0, &PhysX::EditorJointLimitConeConfig::m_limitZ, "Z axis angular limit",
"Limit for swing angle about Z axis.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointLimitConeConfig::IsLimited)
->Attribute(AZ::Edit::Attributes::Max, s_angleMax)
->Attribute(AZ::Edit::Attributes::Min, s_angleMin)
@@ -226,33 +234,33 @@ namespace PhysX
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(0, &PhysX::EditorJointConfig::m_localPosition, "Local Position"
, "Local Position of joint, relative to its entity")
, "Local Position of joint, relative to its entity.")
->DataElement(0, &PhysX::EditorJointConfig::m_localRotation, "Local Rotation"
, "Local Rotation of joint, relative to its entity")
, "Local Rotation of joint, relative to its entity.")
->Attribute(AZ::Edit::Attributes::Min, LocalRotationMin)
->Attribute(AZ::Edit::Attributes::Max, LocalRotationMax)
->DataElement(0, &PhysX::EditorJointConfig::m_leadEntity, "Lead Entity"
, "Parent entity associated with joint")
, "Parent entity associated with joint.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorJointConfig::ValidateLeadEntityId)
->DataElement(0, &PhysX::EditorJointConfig::m_selfCollide, "Lead-Follower Collide"
, "Lead and follower pair will collide with each other")
, "When active, the lead and follower pair will collide with each other.")
->DataElement(0, &PhysX::EditorJointConfig::m_displayJointSetup, "Display Setup in Viewport"
, "Display joint setup in the viewport")
, "Display joint setup in the viewport.")
->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorJointConfig::IsInComponentMode)
->DataElement(0, &PhysX::EditorJointConfig::m_selectLeadOnSnap, "Select Lead on Snap"
, "Select lead entity on snap to position in component mode")
, "Select lead entity on snap to position in component mode.")
->DataElement(0, &PhysX::EditorJointConfig::m_breakable
, "Breakable"
, "Joint is breakable when force or torque exceeds limit")
, "Joint is breakable when force or torque exceeds limit.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorJointConfig::IsInComponentMode)
->DataElement(0, &PhysX::EditorJointConfig::m_forceMax,
"Maximum Force", "Amount of force joint can withstand before breakage")
"Maximum Force", "Amount of force joint can withstand before breakage.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointConfig::m_breakable)
->Attribute(AZ::Edit::Attributes::Max, s_breakageMax)
->Attribute(AZ::Edit::Attributes::Min, s_breakageMin)
->DataElement(0, &PhysX::EditorJointConfig::m_torqueMax,
"Maximum Torque", "Amount of torque joint can withstand before breakage")
"Maximum Torque", "Amount of torque joint can withstand before breakage.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorJointConfig::m_breakable)
->Attribute(AZ::Edit::Attributes::Max, s_breakageMax)
->Attribute(AZ::Edit::Attributes::Min, s_breakageMin)
@@ -54,17 +54,17 @@ namespace PhysX
if (AZ::EditContext* editContext = serialize->GetEditContext())
{
editContext->Class<PhysX::WindConfiguration>("Wind Configuration", "Wind settings for PhysX")
editContext->Class<PhysX::WindConfiguration>("Wind Configuration", "Wind force entity tags.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &WindConfiguration::m_globalWindTag,
"Global wind tag",
"Tag value that will be used to mark entities that provide global wind value.\n"
"Global wind has no bounds and affects objects across entire level.")
"Global wind provider tags.\n"
"Global winds apply to entire world.")
->DataElement(AZ::Edit::UIHandlers::Default, &WindConfiguration::m_localWindTag,
"Local wind tag",
"Tag value that will be used to mark entities that provide local wind value.\n"
"Local wind is only applied within bounds defined by PhysX collider.")
"Local wind provider tags.\n"
"Local winds are constrained to a PhysX collider's boundaries.")
;
}
}
@@ -31,38 +31,39 @@ namespace PhysX
if (AZ::EditContext* editContext = serialize->GetEditContext())
{
editContext->Class<PvdConfiguration>("PhysX PVD Settings", "PhysX PVD Settings")
editContext->Class<PvdConfiguration>("PhysX PVD Settings",
"Connection configuration settings for the PhysX Visual Debugger (PVD). Requires PhysX Debug Gem.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &PvdConfiguration::m_transportType,
"PVD Transport Type", "PVD supports writing to a TCP/IP network socket or to a file.")
"PVD Transport Type", "Output PhysX Visual Debugger data to a TCP/IP network socket or to a file.")
->EnumAttribute(Debug::PvdTransportType::Network, "Network")
->EnumAttribute(Debug::PvdTransportType::File, "File")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->DataElement(AZ::Edit::UIHandlers::Default, &PvdConfiguration::m_host,
"PVD Host", "Host IP address of the PhysX Visual Debugger application")
"PVD Host", "Host IP address of the PhysX Visual Debugger server.")
->Attribute(AZ::Edit::Attributes::Visibility, &PvdConfiguration::IsNetworkDebug)
->DataElement(AZ::Edit::UIHandlers::Default, &PvdConfiguration::m_port,
"PVD Port", "Port of the PhysX Visual Debugger application")
"PVD Port", "Port of the PhysX Visual Debugger server.")
->Attribute(AZ::Edit::Attributes::Visibility, &PvdConfiguration::IsNetworkDebug)
->Attribute(AZ::Edit::Attributes::Min, AZStd::numeric_limits<uint16_t>::min())
->Attribute(AZ::Edit::Attributes::Max, AZStd::numeric_limits<uint16_t>::max())
->DataElement(AZ::Edit::UIHandlers::Default, &PvdConfiguration::m_timeoutInMilliseconds,
"PVD Timeout", "Timeout (in milliseconds) used when connecting to the PhysX Visual Debugger application")
"PVD Timeout", "Timeout (in milliseconds) when connecting to the PhysX Visual Debugger server.")
->Attribute(AZ::Edit::Attributes::Visibility, &PvdConfiguration::IsNetworkDebug)
->DataElement(AZ::Edit::UIHandlers::Default, &PvdConfiguration::m_fileName,
"PVD FileName", "Filename to output PhysX Visual Debugger data.")
"PVD FileName", "Output filename for PhysX Visual Debugger data.")
->Attribute(AZ::Edit::Attributes::Visibility, &PvdConfiguration::IsFileDebug)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &PvdConfiguration::m_autoConnectMode,
"PVD Auto Connect", "Automatically connect to the PhysX Visual Debugger "
"(Requires PhysX Debug gem for Editor and Game modes).")
"PVD Auto Connect", "Automatically connect to the PhysX Visual Debugger.")
->EnumAttribute(Debug::PvdAutoConnectMode::Disabled, "Disabled")
->EnumAttribute(Debug::PvdAutoConnectMode::Editor, "Editor")
->EnumAttribute(Debug::PvdAutoConnectMode::Game, "Game")
->DataElement(AZ::Edit::UIHandlers::CheckBox, &PvdConfiguration::m_reconnect,
"PVD Reconnect", "Reconnect (Disconnect and Connect) when switching between game and edit mode "
"(Requires PhysX Debug gem).")
->DataElement(AZ::Edit::UIHandlers::CheckBox, &PvdConfiguration::m_reconnect, "PVD Reconnect",
"Reconnect (disconnect and connect) to the PhysX Visual Debugger server when switching between game and edit mode.")
;
}
}
@@ -131,7 +132,7 @@ namespace PhysX
if (AZ::EditContext* editContext = serialize->GetEditContext())
{
editContext->Class<DebugDisplayData>("Editor Configuration", "Editor settings for PhysX")
editContext->Class<DebugDisplayData>("Editor Configuration", "Editor settings for PhysX.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Slider, &DebugDisplayData::m_centerOfMassDebugSize,
@@ -33,14 +33,14 @@ namespace PhysX
if (auto* editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorBallJointComponent>(
"PhysX Ball Joint", "The ball joint supports a cone limiting the maximum rotation around the y and z axes.")
"PhysX Ball Joint", "A dynamic joint constraint with swing rotation limits around the Y and Z axes of the joint.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/ball-joint/")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(0, &EditorBallJointComponent::m_swingLimit, "Swing Limit", "Limitations for the swing (Y and Z axis) about joint")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorBallJointComponent::m_componentModeDelegate, "Component Mode", "Ball Joint Component Mode")
->DataElement(0, &EditorBallJointComponent::m_swingLimit, "Swing Limit", "The rotation angle limit around the joint's Y and Z axes.")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorBallJointComponent::m_componentModeDelegate, "Component Mode", "Ball Joint Component Mode.")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
;
}
@@ -53,13 +53,15 @@ namespace PhysX
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorProxyAssetShapeConfig>("EditorProxyShapeConfig", "PhysX Base shape collider")
editContext->Class<EditorProxyAssetShapeConfig>("EditorProxyShapeConfig", "PhysX Base collider.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyAssetShapeConfig::m_pxAsset, "PhysX Mesh", "PhysX mesh collider asset")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyAssetShapeConfig::m_pxAsset, "PhysX Mesh",
"Specifies the PhysX mesh collider asset for this PhysX collider component.")
->Attribute(AZ_CRC_CE("EditButton"), "")
->Attribute(AZ_CRC_CE("EditDescription"), "Open in Scene Settings")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyAssetShapeConfig::m_configuration, "Configuration", "Configuration of asset shape")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyAssetShapeConfig::m_configuration, "Configuration",
"PhysX mesh asset collider configuration.")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly);
}
}
@@ -86,7 +88,7 @@ namespace PhysX
{
editContext->Class<EditorProxyShapeConfig>(
"EditorProxyShapeConfig", "PhysX Base shape collider")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorProxyShapeConfig::m_shapeType, "Shape", "The shape of the collider")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorProxyShapeConfig::m_shapeType, "Shape", "The shape of the collider.")
->EnumAttribute(Physics::ShapeType::Sphere, "Sphere")
->EnumAttribute(Physics::ShapeType::Box, "Box")
->EnumAttribute(Physics::ShapeType::Capsule, "Capsule")
@@ -96,20 +98,20 @@ namespace PhysX
// potentially be different ComponentModes for different shape types)
->Attribute(AZ::Edit::Attributes::ReadOnly, &AzToolsFramework::ComponentModeFramework::InComponentMode)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_sphere, "Sphere", "Configuration of sphere shape")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_sphere, "Sphere", "Configuration of sphere shape.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyShapeConfig::IsSphereConfig)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorProxyShapeConfig::OnConfigurationChanged)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_box, "Box", "Configuration of box shape")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_box, "Box", "Configuration of box shape.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyShapeConfig::IsBoxConfig)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorProxyShapeConfig::OnConfigurationChanged)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_capsule, "Capsule", "Configuration of capsule shape")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_capsule, "Capsule", "Configuration of capsule shape.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyShapeConfig::IsCapsuleConfig)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorProxyShapeConfig::OnConfigurationChanged)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_physicsAsset, "Asset", "Configuration of asset shape")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_physicsAsset, "Asset", "Configuration of asset shape.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyShapeConfig::IsAssetConfig)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorProxyShapeConfig::OnConfigurationChanged)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorProxyShapeConfig::m_subdivisionLevel, "Subdivision level",
"The level of subdivision if a primitive shape is replaced with a convex mesh due to scaling")
"The level of subdivision if a primitive shape is replaced with a convex mesh due to scaling.")
->Attribute(AZ::Edit::Attributes::Min, Utils::MinCapsuleSubdivisionLevel)
->Attribute(AZ::Edit::Attributes::Max, Utils::MaxCapsuleSubdivisionLevel)
->Attribute(AZ::Edit::Attributes::Visibility, &EditorProxyShapeConfig::ShowingSubdivisionLevel)
@@ -200,7 +202,7 @@ namespace PhysX
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorColliderComponent>(
"PhysX Collider", "PhysX shape collider")
"PhysX Collider", "Creates geometry in the PhysX simulation, using either a primitive shape or geometry from an asset.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCollider.svg")
@@ -208,17 +210,17 @@ namespace PhysX
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/collider/")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_configuration, "Collider Configuration", "Configuration of the collider")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_configuration, "Collider Configuration", "Configuration of the collider.")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorColliderComponent::OnConfigurationChanged)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_shapeConfiguration, "Shape Configuration", "Configuration of the shape")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_shapeConfiguration, "Shape Configuration", "Configuration of the shape.")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorColliderComponent::OnConfigurationChanged)
->Attribute(AZ::Edit::Attributes::RemoveNotify, &EditorColliderComponent::ValidateRigidBodyMeshGeometryType)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_componentModeDelegate, "Component Mode", "Collider Component Mode")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_componentModeDelegate, "Component Mode", "Collider Component Mode.")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_colliderDebugDraw,
"Debug draw settings", "Debug draw settings")
"Debug draw settings", "Debug draw settings.")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
;
}
@@ -30,13 +30,14 @@ namespace PhysX
if (auto* editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorFixedJointComponent>(
"PhysX Fixed Joint", "The fixed joint constraints the position and orientation of a body to another.")
"PhysX Fixed Joint",
"A dynamic joint constraint that constrains a rigid body to the joint with no free translation or rotation on any axis.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/fixed-joint/")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorFixedJointComponent::m_componentModeDelegate, "Component Mode", "Fixed Joint Component Mode")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorFixedJointComponent::m_componentModeDelegate, "Component Mode", "Fixed Joint Component Mode.")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
;
}
@@ -164,7 +164,7 @@ namespace PhysX
{
// EditorForceRegionComponent
editContext->Class<EditorForceRegionComponent>(
"PhysX Force Region", "The force region component is used to apply a physical force on objects within the region")
"PhysX Force Region", "The force region component is used to apply a physical force on objects within the region.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/ForceVolume.svg")
@@ -173,9 +173,10 @@ namespace PhysX
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/force-region/")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::RequiredService, AZ_CRC("PhysXTriggerService", 0x3a117d7b))
->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_visibleInEditor, "Visible", "Always show the component in viewport")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_debugForces, "Debug Forces", "Draws debug arrows when an entity enters a force region. This occurs in gameplay mode to show the force direction on an entity.")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_forces, "Forces", "Forces in force region")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_visibleInEditor, "Visible", "Always show the component in viewport.")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_debugForces, "Debug Forces",
"Draws debug arrows when an entity enters a force region. This occurs in gameplay mode to show the force direction on an entity.")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorForceRegionComponent::m_forces, "Forces", "Forces in force region.")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorForceRegionComponent::OnForcesChanged)
;
@@ -33,14 +33,14 @@ namespace PhysX
if (auto* editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorHingeJointComponent>(
"PhysX Hinge Joint", "The entity constrains two actors in PhysX, keeping the origins and x-axes together, and allows free rotation around this common axis")
"PhysX Hinge Joint", "A dynamic joint that constrains a rigid body with rotation limits around a single axis.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/hinge-joint/")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(0, &EditorHingeJointComponent::m_angularLimit, "Angular Limit", "Limitations for the rotation about hinge axis")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorHingeJointComponent::m_componentModeDelegate, "Component Mode", "Hinge Joint Component Mode")
->DataElement(0, &EditorHingeJointComponent::m_angularLimit, "Angular Limit", "The rotation angle limit around the joint's axis.")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorHingeJointComponent::m_componentModeDelegate, "Component Mode", "Hinge Joint Component Mode.")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
;
}
@@ -37,11 +37,11 @@ namespace PhysX
if (auto* editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorJointComponent>(
"PhysX Joint", "The joint constrains the position and orientation of a body to another.")
"PhysX Joint", "A dynamic joint that constrains the position and orientation of one rigid body to another.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(0, &EditorJointComponent::m_config, "Standard Joint Parameters", "Joint parameters shared by all joint types")
->DataElement(0, &EditorJointComponent::m_config, "Standard Joint Parameters", "Joint parameters shared by all joint types.")
;
}
}
@@ -122,38 +122,38 @@ namespace PhysX
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_initialLinearVelocity,
"Initial linear velocity", "Initial linear velocity")
"Initial linear velocity", "Linear velocity applied when the rigid body is activated.")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInitialVelocitiesVisibility)
->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetSpeedUnit())
->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_initialAngularVelocity,
"Initial angular velocity", "Initial angular velocity (limited by maximum angular velocity)")
"Initial angular velocity", "Angular velocity applied when the rigid body is activated (limited by maximum angular velocity).")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInitialVelocitiesVisibility)
->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetAngularVelocityUnit())
->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_linearDamping,
"Linear damping", "Linear damping (must be non-negative)")
"Linear damping", "The rate of decay over time for linear velocity even if no forces are acting on the rigid body.")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetDampingVisibility)
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_angularDamping,
"Angular damping", "Angular damping (must be non-negative)")
"Angular damping", "The rate of decay over time for angular velocity even if no forces are acting on the rigid body.")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetDampingVisibility)
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_sleepMinEnergy,
"Sleep threshold", "Kinetic energy per unit mass below which body can go to sleep (must be non-negative)")
"Sleep threshold", "The rigid body can go to sleep (settle) when kinetic energy per unit mass is persistently below this value.")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetSleepOptionsVisibility)
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetSleepThresholdUnit())
->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_startAsleep,
"Start asleep", "The rigid body will be asleep when spawned")
"Start asleep", "When active, the rigid body will be asleep when spawned, and wake when the body is disturbed.")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetSleepOptionsVisibility)
->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_interpolateMotion,
"Interpolate motion", "Makes object motion look smoother")
"Interpolate motion", "When active, simulation results are interpolated resulting in smoother motion.")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInterpolationVisibility)
->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_gravityEnabled,
"Gravity enabled", "Rigid body will be affected by gravity")
"Gravity enabled", "When active, global gravity affects this rigid body.")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetGravityVisibility)
->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_kinematic,
"Kinematic", "Rigid body is kinematic")
"Kinematic", "When active, the rigid body is not affected by gravity or other forces and is moved by script.")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetKinematicVisibility)
// Linear axis locking properties
@@ -161,85 +161,90 @@ namespace PhysX
->Attribute(AZ::Edit::Attributes::AutoExpand, false)
->DataElement(
AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockLinearX, "Lock X",
"Lock motion along X direction")
"When active, forces won't create translation on the X axis of the rigid body.")
->DataElement(
AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockLinearY, "Lock Y",
"Lock motion along Y direction")
"When active, forces won't create translation on the Y axis of the rigid body.")
->DataElement(
AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockLinearZ, "Lock Z",
"Lock motion along Z direction")
"When active, forces won't create translation on the Z axis of the rigid body.")
// Angular axis locking properties
->ClassElement(AZ::Edit::ClassElements::Group, "Angular Axis Locking")
->Attribute(AZ::Edit::Attributes::AutoExpand, false)
->DataElement(
AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockAngularX, "Lock X",
"Lock rotation around X direction")
"When active, forces won't create rotation on the X axis of the rigid body.")
->DataElement(
AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockAngularY, "Lock Y",
"Lock rotation around Y direction")
"When active, forces won't create rotation on the Y axis of the rigid body.")
->DataElement(
AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockAngularZ, "Lock Z",
"Lock rotation around Z direction")
"When active, forces won't create rotation on the Z axis of the rigid body.")
->ClassElement(AZ::Edit::ClassElements::Group, "Continuous Collision Detection")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetCCDVisibility)
->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_ccdEnabled,
"CCD enabled", "Whether continuous collision detection is enabled for this body")
"CCD enabled", "When active, the rigid body has continuous collision detection (CCD). Use this to ensure accurate "
"collision detection, particularly for fast moving rigid bodies. CCD must be activated in the global PhysX preferences.")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetCCDVisibility)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_ccdMinAdvanceCoefficient,
"Min advance coefficient", "Lower values reduce clipping but can affect simulation smoothness")
"Min advance coefficient", "Lower values reduce clipping but can affect simulation smoothness.")
->Attribute(AZ::Edit::Attributes::Min, 0.01f)
->Attribute(AZ::Edit::Attributes::Step, 0.01f)
->Attribute(AZ::Edit::Attributes::Max, 0.99f)
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::IsCCDEnabled)
->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_ccdFrictionEnabled,
"CCD friction", "Whether friction is applied when CCD collisions are resolved")
"CCD friction", "When active, friction is applied when continuous collision detection (CCD) collisions are resolved.")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::IsCCDEnabled)
->ClassElement(AZ::Edit::ClassElements::Group, "") // end previous group by starting new unnamed expanded group
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_maxAngularVelocity,
"Maximum angular velocity", "The PhysX solver will clamp angular velocities with magnitude exceeding this value")
"Maximum angular velocity", "Clamp angular velocities to this maximum value. "
"This prevents rigid bodies from rotating at unrealistic velocities after collisions.")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetMaxVelocitiesVisibility)
->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetAngularVelocityUnit())
// Mass properties
->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_computeCenterOfMass,
"Compute COM", "Whether to automatically compute the center of mass")
"Compute COM", "Compute the center of mass (COM) for this rigid body.")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_centerOfMassOffset,
"COM offset", "Center of mass offset in local frame")
"COM offset", "Local space offset for the center of mass (COM).")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetCoMVisibility)
->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetLengthUnit())
->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_computeMass,
"Compute Mass", "Whether to automatically compute the mass")
"Compute Mass", "When active, the mass of the rigid body is computed based on the volume and density values of its colliders.")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_mass,
"Mass", "The mass of the object (must be non-negative, with a value of zero treated as infinite)")
"Mass", "The mass of the rigid body in kilograms. A value of 0 is treated as infinite. "
"The trajectory of infinite mass bodies cannot be affected by any collisions or forces other than gravity.")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetMassUnit())
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetMassVisibility)
->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_computeInertiaTensor,
"Compute inertia", "Whether to automatically compute the inertia values based on the mass and shape of the rigid body")
"Compute inertia", "When active, inertia is computed based on the mass and shape of the rigid body.")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->DataElement(Editor::InertiaHandler, &AzPhysics::RigidBodyConfiguration::m_inertiaTensor,
"Inertia diagonal", "Diagonal elements of the inertia tensor")
"Inertia diagonal", "Inertia diagonal elements that specify an inertia tensor; determines the "
"torque required to rotate the rigid body on each axis.")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaVisibility)
->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetInertiaUnit())
->DataElement(AZ::Edit::UIHandlers::Default, &RigidBodyConfiguration::m_includeAllShapesInMassCalculation,
"Include non-simulated shapes in Mass", "If set, non-simulated shapes will also be included in the center of mass, inertia and mass calculations.")
"Include non-simulated shapes in Mass",
"When active, non-simulated shapes are included in the center of mass, inertia, and mass calculations.")
->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetInertiaSettingsVisibility)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
;
@@ -250,7 +255,7 @@ namespace PhysX
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorRigidBodyConfiguration::m_centerOfMassDebugDraw,
"Debug draw COM", "Whether to debug draw the center of mass for this body")
"Debug draw COM", "Display the rigid body's center of mass (COM) in the viewport.")
;
}
}
@@ -79,7 +79,7 @@ namespace PhysX
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorShapeColliderComponent>(
"PhysX Shape Collider", "Creates geometry in the PhysX simulation based on an attached shape component")
"PhysX Shape Collider", "Create a PhysX collider using a shape provided by a Shape component.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCollider.svg")
@@ -88,13 +88,14 @@ namespace PhysX
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/shape-collider/")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_colliderConfig,
"Collider configuration", "Configuration of the collider")
"Collider configuration", "Configuration of the collider.")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorShapeColliderComponent::OnConfigurationChanged)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_colliderDebugDraw,
"Debug draw settings", "Debug draw settings")
"Debug draw settings", "Debug draw settings.")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_subdivisionCount, "Subdivision count", "Number of angular subdivisions in the PhysX cylinder")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_subdivisionCount, "Subdivision count",
"Number of angular subdivisions in the PhysX cylinder.")
->Attribute(AZ::Edit::Attributes::Min, Utils::MinFrustumSubdivisions)
->Attribute(AZ::Edit::Attributes::Max, Utils::MaxFrustumSubdivisions)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorShapeColliderComponent::OnSubdivisionCountChange)
+2 -2
View File
@@ -62,10 +62,10 @@ namespace PhysX
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<ForceRegion>(
"Force Region", "Applies forces on entities within a region")
"Force Region", "Applies forces on entities within a region.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &ForceRegion::m_forces, "Forces", "Forces acting in the region")
->DataElement(AZ::Edit::UIHandlers::Default, &ForceRegion::m_forces, "Forces", "Forces acting in the region.")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
+22 -17
View File
@@ -37,13 +37,13 @@ namespace PhysX
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<ForceWorldSpace>(
"World Space Force", "Applies a force in world space")
"World Space Force", "Applies a force in world space.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Vector3, &ForceWorldSpace::m_direction, "Direction", "Direction of the force in world space")
->DataElement(AZ::Edit::UIHandlers::Vector3, &ForceWorldSpace::m_direction, "Direction", "Direction of the force in world space.")
->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue)
->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue)
->DataElement(AZ::Edit::UIHandlers::Default, &ForceWorldSpace::m_magnitude, "Magnitude", "Magnitude of the force in world space")
->DataElement(AZ::Edit::UIHandlers::Default, &ForceWorldSpace::m_magnitude, "Magnitude", "Magnitude of the force in world space.")
->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue)
->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue)
;
@@ -109,13 +109,13 @@ namespace PhysX
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<ForceLocalSpace>(
"Local Space Force", "Applies a force in the volume's local space")
"Local Space Force", "Applies a force in the volume's local space.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Vector3, &ForceLocalSpace::m_direction, "Direction", "Direction of the force in local space")
->DataElement(AZ::Edit::UIHandlers::Vector3, &ForceLocalSpace::m_direction, "Direction", "Direction of the force in local space.")
->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue)
->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue)
->DataElement(AZ::Edit::UIHandlers::Default, &ForceLocalSpace::m_magnitude, "Magnitude", "Magnitude of the force in local space")
->DataElement(AZ::Edit::UIHandlers::Default, &ForceLocalSpace::m_magnitude, "Magnitude", "Magnitude of the force in local space.")
->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue)
->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue)
;
@@ -179,10 +179,10 @@ namespace PhysX
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<ForcePoint>(
"Point Force", "Applies a force relative to the center of the volume")
"Point Force", "Applies a force directed towards or away from the center of the volume.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &ForcePoint::m_magnitude, "Magnitude", "Magnitude of the point force")
->DataElement(AZ::Edit::UIHandlers::Default, &ForcePoint::m_magnitude, "Magnitude", "Magnitude of the point force.")
->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue)
->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue)
;
@@ -242,19 +242,24 @@ namespace PhysX
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<ForceSplineFollow>(
"Spline Follow Force", "Applies a force to make objects follow a spline at a given speed")
"Spline Follow Force", "Applies a force to make objects follow a spline at a given speed.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_dampingRatio, "Damping Ratio", "Amount of damping applied to an entity that is moving towards a spline")
->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_dampingRatio, "Damping Ratio",
"Values below 1 cause the entity to approach the spline faster but lead to overshooting and oscillation, "
"while higher values will cause it to approach more slowly but more smoothly.")
->Attribute(AZ::Edit::Attributes::Min, s_forceRegionZeroValue)
->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxDampingRatio)
->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_frequency, "Frequency", "Frequency at which an entity moves towards a spline")
->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_frequency, "Frequency",
"Affects how quickly the entity approaches the spline.")
->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinFrequency)
->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxFrequency)
->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_targetSpeed, "Target Speed", "Speed at which entities in the force region move along a spline")
->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_targetSpeed, "Target Speed",
"Speed at which entities in the force region move along a spline.")
->Attribute(AZ::Edit::Attributes::Min, s_forceRegionMinValue)
->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue)
->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_lookAhead, "Lookahead", "Distance at which entities look ahead in their path to reach a point on a spline")
->DataElement(AZ::Edit::UIHandlers::Default, &ForceSplineFollow::m_lookAhead, "Lookahead",
"Distance at which entities look ahead in their path to reach a point on a spline.")
->Attribute(AZ::Edit::Attributes::Min, s_forceRegionZeroValue)
->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxValue)
;
@@ -393,10 +398,10 @@ namespace PhysX
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<ForceSimpleDrag>(
"Simple Drag Force", "Simulates a drag force on entities")
"Simple Drag Force", "Simulates a drag force on entities.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &ForceSimpleDrag::m_volumeDensity, "Region Density", "Density of the region")
->DataElement(AZ::Edit::UIHandlers::Default, &ForceSimpleDrag::m_volumeDensity, "Region Density", "Density of the region.")
->Attribute(AZ::Edit::Attributes::Min, s_forceRegionZeroValue)
->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxDensity)
;
@@ -463,10 +468,10 @@ namespace PhysX
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<ForceLinearDamping>(
"Linear Damping Force", "Applies an opposite force to the entity's velocity")
"Linear Damping Force", "Applies an opposite force to the entity's velocity.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &ForceLinearDamping::m_damping, "Damping", "Amount of damping applied to an opposite force")
->DataElement(AZ::Edit::UIHandlers::Default, &ForceLinearDamping::m_damping, "Damping", "Amount of damping applied to an opposite force.")
->Attribute(AZ::Edit::Attributes::Min, s_forceRegionZeroValue)
->Attribute(AZ::Edit::Attributes::Max, s_forceRegionMaxDamping)
;
@@ -59,22 +59,22 @@ namespace PhysX
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_swingLimitY, "Swing limit Y",
"Maximum angle from the Y axis of the joint frame")
"The rotation angle limit around the joint's Y axis.")
->Attribute(AZ::Edit::Attributes::Suffix, " degrees")
->Attribute(AZ::Edit::Attributes::Min, JointConstants::MinSwingLimitDegrees)
->Attribute(AZ::Edit::Attributes::Max, 180.0f)
->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_swingLimitZ, "Swing limit Z",
"Maximum angle from the Z axis of the joint frame")
"The rotation angle limit around the joint's Z axis.")
->Attribute(AZ::Edit::Attributes::Suffix, " degrees")
->Attribute(AZ::Edit::Attributes::Min, JointConstants::MinSwingLimitDegrees)
->Attribute(AZ::Edit::Attributes::Max, 180.0f)
->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_twistLimitLower, "Twist lower limit",
"Lower limit for rotation about the X axis of the joint frame")
"The lower rotation angle limit around the joint's X axis.")
->Attribute(AZ::Edit::Attributes::Suffix, " degrees")
->Attribute(AZ::Edit::Attributes::Min, -180.0f)
->Attribute(AZ::Edit::Attributes::Max, 180.0f)
->DataElement(AZ::Edit::UIHandlers::Default, &D6JointLimitConfiguration::m_twistLimitUpper, "Twist upper limit",
"Upper limit for rotation about the X axis of the joint frame")
"The upper rotation angle limit around the joint's X axis.")
->Attribute(AZ::Edit::Attributes::Suffix, " degrees")
->Attribute(AZ::Edit::Attributes::Min, -180.0f)
->Attribute(AZ::Edit::Attributes::Max, 180.0f)
@@ -42,16 +42,16 @@ namespace PhysX
"PhysX Character Controller Configuration", "PhysX Character Controller Configuration")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &CharacterControllerConfiguration::m_slopeBehaviour,
"Slope Behaviour", "Behaviour of the controller on surfaces above the maximum slope")
"Slope Behavior", "Behavior of the controller on surfaces that exceed the Maximum Slope Angle.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->EnumAttribute(SlopeBehaviour::PreventClimbing, "Prevent Climbing")
->EnumAttribute(SlopeBehaviour::ForceSliding, "Force Sliding")
->DataElement(AZ::Edit::UIHandlers::Default, &CharacterControllerConfiguration::m_contactOffset,
"Contact Offset", "Extra distance outside the controller used for smoother contact resolution")
"Contact Offset", "Distance from the controller boundary where contact with surfaces can be resolved.")
->Attribute(AZ::Edit::Attributes::Min, 0.01f)
->Attribute(AZ::Edit::Attributes::Step, 0.01f)
->DataElement(AZ::Edit::UIHandlers::Default, &CharacterControllerConfiguration::m_scaleCoefficient,
"Scale", "Scalar coefficient used to scale the controller, usually slightly smaller than 1")
"Scale", "Scales the controller. Usually less than 1.0 to ensure visual contact between the character and surface.")
->Attribute(AZ::Edit::Attributes::Min, 0.01f)
->Attribute(AZ::Edit::Attributes::Step, 0.01f)
;
@@ -33,7 +33,7 @@ namespace PhysX
"PhysX Character Gameplay Configuration", "PhysX Character Gameplay Configuration")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->DataElement(AZ::Edit::UIHandlers::Default, &CharacterGameplayConfiguration::m_gravityMultiplier,
"Gravity Multiplier", "Multiplier to be combined with the world gravity value for applying character gravity")
"Gravity Multiplier", "Multiplier for global gravity value that applies only to this character entity.")
->Attribute(AZ::Edit::Attributes::Step, 0.1f)
;
}
@@ -36,18 +36,18 @@ namespace PhysX
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorCharacterControllerProxyShapeConfig>(
"EditorCharacterControllerProxyShapeConfig", "PhysX character controller shape")
"EditorCharacterControllerProxyShapeConfig", "PhysX character controller shape.")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorCharacterControllerProxyShapeConfig::m_shapeType, "Shape",
"The shape associated with the character controller")
"The shape of the character controller.")
->EnumAttribute(Physics::ShapeType::Capsule, "Capsule")
->EnumAttribute(Physics::ShapeType::Box, "Box")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterControllerProxyShapeConfig::m_box, "Box",
"Configuration of box shape")
"Configuration of box shape.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorCharacterControllerProxyShapeConfig::IsBoxConfig)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterControllerProxyShapeConfig::m_capsule, "Capsule",
"Configuration of capsule shape")
"Configuration of capsule shape.")
->Attribute(AZ::Edit::Attributes::Visibility, &EditorCharacterControllerProxyShapeConfig::IsCapsuleConfig)
;
}
@@ -93,7 +93,8 @@ namespace PhysX
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorCharacterControllerComponent>(
"PhysX Character Controller", "PhysX Character Controller")
"PhysX Character Controller",
"Provides basic character interactions with the physical world, such as preventing movement through other PhysX bodies.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCharacter.svg")
@@ -101,12 +102,12 @@ namespace PhysX
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/character-controller/")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterControllerComponent::m_configuration,
"Configuration", "Configuration for the character controller")
"Configuration", "Configuration for the character controller.")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorCharacterControllerComponent::OnControllerConfigChanged)
->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterControllerComponent::m_proxyShapeConfiguration,
"Shape Configuration", "The configuration for the shape associated with the character controller")
"Shape Configuration", "The configuration for the shape associated with the character controller.")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorCharacterControllerComponent::OnShapeConfigChanged)
@@ -43,7 +43,7 @@ namespace PhysX
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorCharacterGameplayComponent>(
"PhysX Character Gameplay", "PhysX Character Gameplay")
"PhysX Character Gameplay", "An example implementation of character physics behavior such as gravity.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXCharacter.svg")
@@ -51,7 +51,7 @@ namespace PhysX
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/character-gameplay/")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorCharacterGameplayComponent::m_gameplayConfig,
"Gameplay Configuration", "Gameplay Configuration")
"Gameplay Configuration", "Gameplay Configuration.")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
@@ -82,7 +82,7 @@ namespace PhysX
if (editContext)
{
editContext->Class<RagdollComponent>(
"PhysX Ragdoll", "Provides simulation of characters in PhysX.")
"PhysX Ragdoll", "Creates a PhysX ragdoll simulation for an animation actor.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/PhysXRagdoll.svg")
@@ -91,26 +91,28 @@ namespace PhysX
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/physx/ragdoll/")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_positionIterations, "Position Iteration Count",
"A higher iteration count generally improves fidelity at the cost of performance, but note that very high "
"values may lead to severe instability if ragdoll colliders interfere with satisfying joint constraints")
"The frequency at which ragdoll collider positions are resolved. Higher values can increase fidelity but decrease "
"performance. Very high values might introduce instability.")
->Attribute(AZ::Edit::Attributes::Min, 1)
->Attribute(AZ::Edit::Attributes::Max, 255)
->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_velocityIterations, "Velocity Iteration Count",
"A higher iteration count generally improves fidelity at the cost of performance, but note that very high "
"values may lead to severe instability if ragdoll colliders interfere with satisfying joint constraints")
"The frequency at which ragdoll collider velocities are resolved. Higher values can increase fidelity but decrease "
"performance. Very high values might introduce instability.")
->Attribute(AZ::Edit::Attributes::Min, 1)
->Attribute(AZ::Edit::Attributes::Max, 255)
->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_enableJointProjection,
"Enable Joint Projection", "Whether to use joint projection to preserve joint constraints "
"in demanding situations at the expense of potentially reducing physical correctness")
"Enable Joint Projection", "When active, preserves joint constraints in volatile simulations. "
"Might not be physically correct in all simulations.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_jointProjectionLinearTolerance,
"Joint Projection Linear Tolerance", "Linear joint error above which projection will be applied")
"Joint Projection Linear Tolerance",
"Maximum linear joint error. Projection is applied to linear joint errors above this value.")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Step, 1e-3f)
->Attribute(AZ::Edit::Attributes::Visibility, &RagdollComponent::IsJointProjectionVisible)
->DataElement(AZ::Edit::UIHandlers::Default, &RagdollComponent::m_jointProjectionAngularToleranceDegrees,
"Joint Projection Angular Tolerance", "Angular joint error (in degrees) above which projection will be applied")
"Joint Projection Angular Tolerance",
"Maximum angular joint error. Projection is applied to angular joint errors above this value.")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Step, 0.1f)
->Attribute(AZ::Edit::Attributes::Suffix, " degrees")
+152 -135
View File
@@ -8,6 +8,7 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/Math/ToString.h>
#include <AzFramework/Physics/Utils.h>
#include <AzFramework/Physics/Configuration/RigidBodyConfiguration.h>
#include <PhysX/NativeTypeIdentifiers.h>
@@ -23,6 +24,28 @@
namespace PhysX
{
namespace
{
const AZ::Vector3 DefaultCenterOfMass = AZ::Vector3::CreateZero();
const float DefaultMass = 1.0f;
const AZ::Matrix3x3 DefaultInertiaTensor = AZ::Matrix3x3::CreateIdentity();
bool IsSimulationShape(const physx::PxShape& pxShape)
{
return (pxShape.getFlags() & physx::PxShapeFlag::eSIMULATION_SHAPE);
}
bool CanShapeComputeMassProperties(const physx::PxShape& pxShape)
{
// Note: List based on computeMassAndInertia function in ExtRigidBodyExt.cpp file in PhysX.
const physx::PxGeometryType::Enum geometryType = pxShape.getGeometryType();
return geometryType == physx::PxGeometryType::eSPHERE
|| geometryType == physx::PxGeometryType::eBOX
|| geometryType == physx::PxGeometryType::eCAPSULE
|| geometryType == physx::PxGeometryType::eCONVEXMESH;
}
}
void RigidBody::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
@@ -152,104 +175,120 @@ namespace PhysX
m_shapes.erase(found);
}
void RigidBody::UpdateMassProperties(AzPhysics::MassComputeFlags flags, const AZ::Vector3* centerOfMassOffsetOverride, const AZ::Matrix3x3* inertiaTensorOverride, const float* massOverride)
void RigidBody::UpdateMassProperties(AzPhysics::MassComputeFlags flags, const AZ::Vector3& centerOfMassOffsetOverride, const AZ::Matrix3x3& inertiaTensorOverride, const float massOverride)
{
// Input validation
bool computeCenterOfMass = AzPhysics::MassComputeFlags::COMPUTE_COM == (flags & AzPhysics::MassComputeFlags::COMPUTE_COM);
AZ_Assert(computeCenterOfMass || centerOfMassOffsetOverride,
"UpdateMassProperties: MassComputeFlags::COMPUTE_COM is not set but COM offset is not specified");
computeCenterOfMass = computeCenterOfMass || !centerOfMassOffsetOverride;
const bool computeCenterOfMass = AzPhysics::MassComputeFlags::COMPUTE_COM == (flags & AzPhysics::MassComputeFlags::COMPUTE_COM);
const bool computeInertiaTensor = AzPhysics::MassComputeFlags::COMPUTE_INERTIA == (flags & AzPhysics::MassComputeFlags::COMPUTE_INERTIA);
const bool computeMass = AzPhysics::MassComputeFlags::COMPUTE_MASS == (flags & AzPhysics::MassComputeFlags::COMPUTE_MASS);
const bool needsCompute = computeCenterOfMass || computeInertiaTensor || computeMass;
const bool includeAllShapesInMassCalculation = AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES == (flags & AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES);
bool computeInertiaTensor = AzPhysics::MassComputeFlags::COMPUTE_INERTIA == (flags & AzPhysics::MassComputeFlags::COMPUTE_INERTIA);
AZ_Assert(computeInertiaTensor || inertiaTensorOverride,
"UpdateMassProperties: MassComputeFlags::COMPUTE_INERTIA is not set but inertia tensor is not specified");
computeInertiaTensor = computeInertiaTensor || !inertiaTensorOverride;
bool computeMass = AzPhysics::MassComputeFlags::COMPUTE_MASS == (flags & AzPhysics::MassComputeFlags::COMPUTE_MASS);
AZ_Assert(computeMass || massOverride,
"UpdateMassProperties: MassComputeFlags::COMPUTE_MASS is not set but mass is not specified");
computeMass = computeMass || !massOverride;
AZ::u32 shapesCount = GetShapeCount();
// Basic cases when we don't need to compute anything
if (shapesCount == 0 || flags == AzPhysics::MassComputeFlags::NONE)
// Basic case where all properties are set directly.
if (!needsCompute)
{
if (massOverride)
{
SetMass(*massOverride);
}
if (inertiaTensorOverride)
{
SetInertia(*inertiaTensorOverride);
}
if (centerOfMassOffsetOverride)
{
SetCenterOfMassOffset(*centerOfMassOffsetOverride);
}
SetCenterOfMassOffset(centerOfMassOffsetOverride);
SetMass(massOverride);
SetInertia(inertiaTensorOverride);
return;
}
// Setup center of mass offset pointer for PxRigidBodyExt::updateMassAndInertia function
AZStd::optional<physx::PxVec3> optionalComOverride;
if (!computeCenterOfMass && centerOfMassOffsetOverride)
// If there are no shapes then set the properties directly without computing anything.
if (m_shapes.empty())
{
optionalComOverride = PxMathConvert(*centerOfMassOffsetOverride);
}
const physx::PxVec3* massLocalPose = optionalComOverride.has_value() ? &optionalComOverride.value() : nullptr;
bool includeAllShapesInMassCalculation =
AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES == (flags & AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES);
// Handle the case when we don't compute mass
if (!computeMass)
{
{
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, *massOverride, massLocalPose,
includeAllShapesInMassCalculation);
}
if (!computeInertiaTensor)
{
SetInertia(*inertiaTensorOverride);
}
SetCenterOfMassOffset(computeCenterOfMass ? DefaultCenterOfMass : centerOfMassOffsetOverride);
SetMass(computeMass ? DefaultMass : massOverride);
SetInertia(computeInertiaTensor ? DefaultInertiaTensor : inertiaTensorOverride);
return;
}
// Handle the cases when mass should be computed from density
if (shapesCount == 1)
auto cannotComputeMassProperties = [this, includeAllShapesInMassCalculation]
{
AZStd::shared_ptr<Physics::Shape> shape = GetShape(0);
float density = shape->GetMaterial()->GetDensity();
PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene());
return AZStd::any_of(m_shapes.cbegin(), m_shapes.cend(),
[includeAllShapesInMassCalculation](const AZStd::shared_ptr<PhysX::Shape>& shape)
{
const physx::PxShape& pxShape = *shape->GetPxShape();
const bool includeShape = includeAllShapesInMassCalculation || IsSimulationShape(pxShape);
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor, density, massLocalPose,
includeAllShapesInMassCalculation);
return includeShape && !CanShapeComputeMassProperties(pxShape);
});
};
// If contains shapes that cannot compute mass properties (triangle mesh,
// plane or heightfield) then default values will be used.
if (cannotComputeMassProperties())
{
AZ_Warning("RigidBody", !computeCenterOfMass,
"Rigid body '%s' cannot compute COM because it contains triangle mesh, plane or heightfield shapes, it will default to %s.",
GetName().c_str(), AZ::ToString(DefaultCenterOfMass).c_str());
AZ_Warning("RigidBody", !computeMass,
"Rigid body '%s' cannot compute Mass because it contains triangle mesh, plane or heightfield shapes, it will default to %0.1f.",
GetName().c_str(), DefaultMass);
AZ_Warning("RigidBody", !computeInertiaTensor,
"Rigid body '%s' cannot compute Inertia because it contains triangle mesh, plane or heightfield shapes, it will default to %s.",
GetName().c_str(), AZ::ToString(DefaultInertiaTensor.RetrieveScale()).c_str());
SetCenterOfMassOffset(computeCenterOfMass ? DefaultCenterOfMass : centerOfMassOffsetOverride);
SetMass(computeMass ? DefaultMass : massOverride);
SetInertia(computeInertiaTensor ? DefaultInertiaTensor : inertiaTensorOverride);
return;
}
// Center of mass needs to be considered first since
// it's needed when computing mass and inertia.
if (computeCenterOfMass)
{
// Compute Center of Mass
UpdateCenterOfMass(includeAllShapesInMassCalculation);
}
else
{
AZStd::vector<float> densities(shapesCount);
for (AZ::u32 i = 0; i < shapesCount; ++i)
SetCenterOfMassOffset(centerOfMassOffsetOverride);
}
const physx::PxVec3 pxCenterOfMass = PxMathConvert(GetCenterOfMassLocal());
if (computeMass)
{
// Gather material densities from all shapes,
// mass computation is based on them.
AZStd::vector<float> densities;
densities.reserve(m_shapes.size());
for (const auto& shape : m_shapes)
{
densities[i] = GetShape(i)->GetMaterial()->GetDensity();
densities.emplace_back(shape->GetMaterial()->GetDensity());
}
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor, densities.data(),
shapesCount, massLocalPose, includeAllShapesInMassCalculation);
}
// Compute Mass + Inertia
{
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor,
densities.data(), static_cast<AZ::u32>(densities.size()),
&pxCenterOfMass, includeAllShapesInMassCalculation);
}
// Set the overrides if provided.
// Note: We don't set the center of mass here because it was already provided
// to PxRigidBodyExt::updateMassAndInertia above
if (!computeInertiaTensor)
// There is no physx function to only compute the mass without
// computing the inertia. So now that both have been computed
// we can override the inertia if it's suppose to use a
// specific value set by the user.
if (!computeInertiaTensor)
{
SetInertia(inertiaTensorOverride);
}
}
else
{
SetInertia(*inertiaTensorOverride);
if (computeInertiaTensor)
{
// Set Mass + Compute Inertia
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, massOverride,
&pxCenterOfMass, includeAllShapesInMassCalculation);
}
else
{
SetMass(massOverride);
SetInertia(inertiaTensorOverride);
}
}
}
@@ -344,52 +383,49 @@ namespace PhysX
}
}
void RigidBody::UpdateComputedCenterOfMass()
void RigidBody::UpdateCenterOfMass(bool includeAllShapesInMassCalculation)
{
if (m_pxRigidActor)
if (m_shapes.empty())
{
physx::PxU32 shapeCount = 0;
SetCenterOfMassOffset(DefaultCenterOfMass);
return;
}
AZStd::vector<const physx::PxShape*> pxShapes;
pxShapes.reserve(m_shapes.size());
{
// Filter shapes in the same way that updateMassAndInertia function does.
PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene());
for (const auto& shape : m_shapes)
{
PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene());
shapeCount = m_pxRigidActor->getNbShapes();
}
if (shapeCount > 0)
{
AZStd::vector<physx::PxShape*> shapes;
shapes.resize(shapeCount);
const physx::PxShape& pxShape = *shape->GetPxShape();
const bool includeShape = includeAllShapesInMassCalculation || IsSimulationShape(pxShape);
if (includeShape && CanShapeComputeMassProperties(pxShape))
{
PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene());
m_pxRigidActor->getShapes(&shapes[0], shapeCount);
pxShapes.emplace_back(&pxShape);
}
shapes.erase(AZStd::remove_if(shapes.begin()
, shapes.end()
, [](const physx::PxShape* shape)
{
return shape->getFlags() & physx::PxShapeFlag::eTRIGGER_SHAPE;
})
, shapes.end());
shapeCount = static_cast<physx::PxU32>(shapes.size());
if (shapeCount == 0)
{
SetZeroCenterOfMass();
return;
}
const auto properties = physx::PxRigidBodyExt::computeMassPropertiesFromShapes(&shapes[0], shapeCount);
const physx::PxTransform computedCenterOfMass(properties.centerOfMass);
{
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
m_pxRigidActor->setCMassLocalPose(computedCenterOfMass);
}
}
else
{
SetZeroCenterOfMass();
}
}
if (pxShapes.empty())
{
SetCenterOfMassOffset(DefaultCenterOfMass);
return;
}
const physx::PxMassProperties pxMassProperties = [this, &pxShapes]
{
// Note: PhysX computeMassPropertiesFromShapes function does not use densities
// to compute the shape's masses, which are needed to calculate the center of mass.
// This differs from updateMassAndInertia function, which uses material density values.
// So the masses used during center of mass calculation do not match the masses
// used during mass/inertia calculation. This is an inconsistency in PhysX.
PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene());
return physx::PxRigidBodyExt::computeMassPropertiesFromShapes(pxShapes.data(), static_cast<physx::PxU32>(pxShapes.size()));
}();
SetCenterOfMassOffset(PxMathConvert(pxMassProperties.centerOfMass));
}
void RigidBody::SetInertia(const AZ::Matrix3x3& inertia)
@@ -401,16 +437,6 @@ namespace PhysX
}
}
void RigidBody::ComputeInertia()
{
if (m_pxRigidActor)
{
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
auto localPose = m_pxRigidActor->getCMassLocalPose().p;
physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, m_pxRigidActor->getMass(), &localPose);
}
}
AZ::Vector3 RigidBody::GetLinearVelocity() const
{
if (m_pxRigidActor)
@@ -783,13 +809,4 @@ namespace PhysX
{
return m_name;
}
void RigidBody::SetZeroCenterOfMass()
{
if (m_pxRigidActor)
{
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
m_pxRigidActor->setCMassLocalPose(physx::PxTransform(PxMathConvert(AZ::Vector3::CreateZero())));
}
}
}
+4 -6
View File
@@ -109,17 +109,15 @@ namespace PhysX
void RemoveShape(AZStd::shared_ptr<Physics::Shape> shape) override;
void UpdateMassProperties(AzPhysics::MassComputeFlags flags = AzPhysics::MassComputeFlags::DEFAULT,
const AZ::Vector3* centerOfMassOffsetOverride = nullptr,
const AZ::Matrix3x3* inertiaTensorOverride = nullptr,
const float* massOverride = nullptr) override;
const AZ::Vector3& centerOfMassOffsetOverride = AZ::Vector3::CreateZero(),
const AZ::Matrix3x3& inertiaTensorOverride = AZ::Matrix3x3::CreateIdentity(),
const float massOverride = 1.0f) override;
private:
void CreatePhysXActor(const AzPhysics::RigidBodyConfiguration& configuration);
void UpdateComputedCenterOfMass();
void ComputeInertia();
void UpdateCenterOfMass(bool includeAllShapesInMassCalculation);
void SetInertia(const AZ::Matrix3x3& inertia);
void SetZeroCenterOfMass();
AZStd::shared_ptr<physx::PxRigidDynamic> m_pxRigidActor;
AZStd::vector<AZStd::shared_ptr<PhysX::Shape>> m_shapes;
+2 -2
View File
@@ -198,8 +198,8 @@ namespace PhysX
AZ_Warning("PhysXScene", shapeAdded, "No Collider or Shape information found when creating Rigid body [%s]", configuration->m_debugName.c_str());
}
const AzPhysics::MassComputeFlags& flags = configuration->GetMassComputeFlags();
newBody->UpdateMassProperties(flags, &configuration->m_centerOfMassOffset,
&configuration->m_inertiaTensor, &configuration->m_mass);
newBody->UpdateMassProperties(flags, configuration->m_centerOfMassOffset,
configuration->m_inertiaTensor, configuration->m_mass);
crc = AZ::Crc32(newBody, sizeof(*newBody));
return newBody;
+1 -1
View File
@@ -101,7 +101,7 @@ namespace PhysX
if (AZ::EditContext* editContext = serialize->GetEditContext())
{
editContext->Class<SystemComponent>("PhysX", "Global PhysX physics configuration")
editContext->Class<SystemComponent>("PhysX", "Global PhysX physics configuration.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "PhysX")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))

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