You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/RenderToTexture/Code/Source/EditorRenderToTextureCompon...

375 lines
17 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "RenderToTexture_precompiled.h"
#if AZ_RENDER_TO_TEXTURE_GEM_ENABLED
#include "EditorRenderToTextureComponent.h"
#include "RenderToTextureComponent.h"
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Math/Transform.h>
#include <MathConversion.h>
#include <I3DEngine.h>
#include <IViewSystem.h>
#include <AzFramework/Components/CameraBus.h>
#include <RTTBus.h>
#include <CryName.h>
#include <QApplication>
#include <QMessageBox>
namespace AzRTT
{
/**
* AA type
*/
enum class AAType
{
None = 0,
FXAA,
SMAA1TX
};
AZ::Crc32 RenderContextConfig::GetShadowSettingsVisible()
{
return m_shadowsEnabled ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
}
bool RenderContextConfig::ValidateTextureSize(void* newValue, const AZ::Uuid& valueType)
{
if (azrtti_typeid<uint32_t>() != valueType)
{
AZ_Assert(false, "Unexpected value type");
return false;
}
uint32_t* newTextureSize = static_cast<uint32_t*>(newValue);
AZ_Assert(newTextureSize, "Texture size parameter is null in ValidateTextureSize");
if (*newTextureSize > AzRTT::MaxRecommendedRenderTargetSize)
{
if (QMessageBox::Cancel == QMessageBox::warning(QApplication::activeWindow(),
QObject::tr("Large Texture Size"),
QObject::tr("Large texture sizes can lead to excess memory usage, low performance and instability. Do you want to continue?"),
QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel))
{
return false;
}
}
return true;
}
}
namespace RenderToTexture
{
void EditorRenderToTextureConfig::Reflect(AZ::ReflectContext* reflection)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
if (serializeContext)
{
serializeContext->Class<EditorRenderToTextureConfig, RenderToTextureConfig>()
->Version(1)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<AzRTT::RenderContextConfig>("RenderContext", "RenderContext")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::Category, "Rendering")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"))
// Required settings
->DataElement(0, &AzRTT::RenderContextConfig::m_width, "Width", "Texture width")
->Attribute(AZ::Edit::Attributes::ChangeValidate, &AzRTT::RenderContextConfig::ValidateTextureSize)
->Attribute(AZ::Edit::Attributes::Min, AzRTT::MinRenderTargetWidth)
->DataElement(0, &AzRTT::RenderContextConfig::m_height, "Height", "Texture height")
->Attribute(AZ::Edit::Attributes::ChangeValidate, &AzRTT::RenderContextConfig::ValidateTextureSize)
->Attribute(AZ::Edit::Attributes::Min, AzRTT::MinRenderTargetHeight)
->DataElement(0, &AzRTT::RenderContextConfig::m_sRGBWrite, "Apply Gamma", "")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &AzRTT::RenderContextConfig::m_alphaMode, "Alpha Mode", "")
->EnumAttribute(AzRTT::AlphaMode::ALPHA_OPAQUE, "Opaque")
->EnumAttribute(AzRTT::AlphaMode::ALPHA_DEPTH_BASED, "Depth Based")
// Scene Settings
->ClassElement(AZ::Edit::ClassElements::Group, "Scene Settings")
->Attribute(AZ::Edit::Attributes::AutoExpand, false)
->DataElement(0, &AzRTT::RenderContextConfig::m_oceanEnabled, "Enable Ocean", "")
->DataElement(0, &AzRTT::RenderContextConfig::m_terrainEnabled, "Enable Terrain", "")
->DataElement(0, &AzRTT::RenderContextConfig::m_vegetationEnabled, "Enable Vegetation", "")
->DataElement(0, &AzRTT::RenderContextConfig::m_shadowsEnabled, "Enable Shadows", "")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
->DataElement(0, &AzRTT::RenderContextConfig::m_shadowsNumCascades, "GSM LODs", "")
->Attribute(AZ::Edit::Attributes::Visibility, &AzRTT::RenderContextConfig::GetShadowSettingsVisible)
->Attribute(AZ::Edit::Attributes::Min, -1)
->DataElement(0, &AzRTT::RenderContextConfig::m_shadowsGSMRange, "GSM range", "")
->Attribute(AZ::Edit::Attributes::Visibility, &AzRTT::RenderContextConfig::GetShadowSettingsVisible)
->Attribute(AZ::Edit::Attributes::Min, -1)
->DataElement(0, &AzRTT::RenderContextConfig::m_shadowsGSMRangeStep, "GSM range step", "")
->Attribute(AZ::Edit::Attributes::Visibility, &AzRTT::RenderContextConfig::GetShadowSettingsVisible)
->Attribute(AZ::Edit::Attributes::Min, -1)
// Post Effects
->ClassElement(AZ::Edit::ClassElements::Group, "Post Effects")
->Attribute(AZ::Edit::Attributes::AutoExpand, false)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &AzRTT::RenderContextConfig::m_aaMode, "Antialiasing Mode", "")
->EnumAttribute(AzRTT::AAType::None, "None")
->EnumAttribute(AzRTT::AAType::FXAA, "FXAA")
;
editContext->Class<RenderToTextureConfig>("EditorRenderToTextureConfig", "Editor Config for RenderToTextureConfig")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::Category, "Rendering")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"))
// Required settings
->DataElement(0, &RenderToTextureConfig::m_camera, "Camera", "Optional camera to use")
->DataElement(0, &RenderToTextureConfig::m_textureName, "Texture name", "Name of texture to render to")
->DataElement(0, &RenderToTextureConfig::m_maxFPS, "Max FPS", "Maximum FPS limit, or 0 for no limit.")
->DataElement(0, &RenderToTextureConfig::m_renderContextConfig, "Render Context Config", "Render Context Config")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->ClassElement(AZ::Edit::ClassElements::Group, "Debug")
->Attribute(AZ::Edit::Attributes::AutoExpand, false)
->DataElement(0, &RenderToTextureConfig::m_displayDebugImage, "Display Debug Image", "Display an image of the render target in the main viewport.");
}
}
}
void EditorRenderToTextureComponent::Reflect(AZ::ReflectContext* reflection)
{
EditorRenderToTextureConfig::Reflect(reflection);
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
if (serializeContext)
{
serializeContext->Class<EditorRenderToTextureComponent, AzToolsFramework::Components::EditorComponentBase>()
->Version(1)
->Field("Config", &EditorRenderToTextureComponent::m_config)
->Field("Update In Editor", &EditorRenderToTextureComponent::m_updateInEditor)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorRenderToTextureComponent>("Render to Texture", "Render the world to a texture")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Rendering")
->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/RenderToTexture.svg")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/RenderToTexture.png")
->Attribute(AZ::Edit::Attributes::PreferNoViewportIcon, true)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"))
->Attribute(AZ::Edit::Attributes::HelpPageURL, "http://docs.aws.amazon.com/console/lumberyard/userguide/render-to-texture-component")
->DataElement(0, &EditorRenderToTextureComponent::m_config, "Config", "Render To Texture Configuration")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorRenderToTextureComponent::ConfigurationChanged)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(0, &EditorRenderToTextureComponent::m_updateInEditor, "Update in editor", "If enabled, the render texture will update every frame while in editor mode.")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorRenderToTextureComponent::ConfigurationChanged)
;
}
}
}
void EditorRenderToTextureComponent::BuildGameEntity(AZ::Entity* gameEntity)
{
auto component = gameEntity->CreateComponent<RenderToTextureComponent>();
if (component)
{
component->SetConfiguration(m_config);
}
}
void EditorRenderToTextureComponent::Activate()
{
// Default to using the entity this component is assigned to.
// This makes it obvious to the user where the camera data is coming from.
if (!m_config.m_camera.IsValid())
{
m_config.m_camera = GetEntityId();
}
AzRTT::RTTRequestBus::BroadcastResult(m_config.m_renderContextId, &AzRTT::RTTRequestBus::Events::CreateContext, m_config.m_renderContextConfig);
if (m_config.m_renderContextId.IsNull())
{
AZ_Printf("EditorRenderToTextureComponent", "$2Failed to create render context.");
}
else
{
m_nextRefreshTime = 0.0;
ConfigurationChanged();
ValidateCVars();
}
if (!m_bRenderDebugDrawRegistered)
{
gEnv->pRenderer->AddRenderDebugListener(this);
m_bRenderDebugDrawRegistered = true;
}
}
int EditorRenderToTextureComponent::GetTickOrder()
{
return AZ::TICK_LAST;
}
void EditorRenderToTextureComponent::OnTick(float deltaTime, AZ::ScriptTimePoint time)
{
// we can do this in the editor because it is single threaded
Render(m_renderTargetHandle, m_config, GetEntityId());
}
void EditorRenderToTextureComponent::UpdateRenderTarget(const AZStd::string& textureName, int width, int height)
{
if (textureName.empty() || width < AzRTT::MinRenderTargetWidth || height < AzRTT::MinRenderTargetHeight )
{
return;
}
// verify this name isn't already in use
ITexture* texture = gEnv->pRenderer->EF_GetTextureByName(m_config.m_textureName.c_str());
if (texture != nullptr)
{
// make sure we're not already using this target
if (texture->GetTextureID() != m_renderTargetHandle)
{
// we can re-use an existing render target
if (texture->GetFlags() & FT_USAGE_RENDERTARGET)
{
ReleaseCurrentRenderTarget();
m_renderTargetHandle = texture->GetTextureID();
// increment the reference count because we will call DestroyRenderTarget
// which will attempt to release this CTexture
texture->AddRef();
}
else if (!gEnv->pRenderer->IsTextureExist(texture))
{
// it is possible that a CTexture was created because it was part of a material
// but no file exists for it. Calling CreateRenderTarget will attempt to
// convert it to a render target and increment the CTexture reference count
ReleaseCurrentRenderTarget();
m_renderTargetHandle = gEnv->pRenderer->CreateRenderTarget(textureName.c_str(), width, height, Clr_Unknown, eTF_R8G8B8A8);
AZ_Warning("EditorRenderToTextureComponent", m_renderTargetHandle != INVALID_RENDER_TARGET , "$4Failed to create render target %s.", textureName.c_str());
}
else
{
AZ_Warning("EditorRenderToTextureComponent", false, "$2The name %s is already in use by a texture that is not a valid render target.", textureName.c_str());
}
}
else if (texture->GetWidth() != width || texture->GetHeight() != height)
{
gEnv->pRenderer->ResizeRenderTarget(m_renderTargetHandle, width, height);
}
}
else
{
ReleaseCurrentRenderTarget();
m_renderTargetHandle = gEnv->pRenderer->CreateRenderTarget(textureName.c_str(), width, height, Clr_Unknown, eTF_R8G8B8A8);
AZ_Warning("EditorRenderToTextureComponent", m_renderTargetHandle != INVALID_RENDER_TARGET , "$4Failed to create render target %s.", textureName.c_str());
}
}
void EditorRenderToTextureComponent::ReleaseCurrentRenderTarget()
{
if (m_renderTargetHandle != INVALID_RENDER_TARGET)
{
gEnv->pRenderer->DestroyRenderTarget(m_renderTargetHandle);
m_renderTargetHandle = INVALID_RENDER_TARGET;
}
}
void EditorRenderToTextureComponent::ConfigurationChanged()
{
UpdateRenderTarget(m_config.m_textureName, m_config.m_renderContextConfig.m_width, m_config.m_renderContextConfig.m_height);
if (m_renderTargetHandle != INVALID_RENDER_TARGET)
{
if (m_updateInEditor != AZ::TickBus::Handler::BusIsConnected())
{
if (m_updateInEditor)
{
AZ::TickBus::Handler::BusConnect();
}
else
{
AZ::TickBus::Handler::BusDisconnect();
}
}
}
// push the changes from the component's RenderContextConfig to our RenderContext
if (!m_config.m_renderContextId.IsNull())
{
AzRTT::RTTRequestBus::Broadcast(&AzRTT::RTTRequestBus::Events::SetContextConfig, m_config.m_renderContextId, m_config.m_renderContextConfig);
}
}
void EditorRenderToTextureComponent::Deactivate()
{
if (!m_config.m_renderContextId.IsNull())
{
AzRTT::RTTRequestBus::Broadcast(&AzRTT::RTTRequestBus::Events::DestroyContext, m_config.m_renderContextId);
}
ReleaseCurrentRenderTarget();
if (AZ::TickBus::Handler::BusIsConnected())
{
AZ::TickBus::Handler::BusDisconnect();
}
if (m_bRenderDebugDrawRegistered)
{
gEnv->pRenderer->RemoveRenderDebugListener(this);
m_bRenderDebugDrawRegistered = false;
}
}
bool EditorRenderToTextureComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
{
if (auto config = azrtti_cast<const RenderToTextureConfig*>(baseConfig))
{
static_cast<RenderToTextureConfig&>(m_config) = *config;
return true;
}
return false;
}
bool EditorRenderToTextureComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
{
if (auto outConfig = azrtti_cast<RenderToTextureConfig*>(outBaseConfig))
{
*outConfig = m_config;
return true;
}
return false;
}
void EditorRenderToTextureComponent::OnDebugDraw()
{
if (m_config.m_displayDebugImage)
{
DisplayDebugImage(m_config);
}
}
}
#endif // if AZ_RENDER_TO_TEXTURE_GEM_ENABLED